diff --git a/gradle/plugin-center.yaml b/gradle/plugin-center.yaml new file mode 100644 index 0000000000..7ec767885c --- /dev/null +++ b/gradle/plugin-center.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Refactor plugin manager diff --git a/scm-ui/ui-webapp/public/locales/de/admin.json b/scm-ui/ui-webapp/public/locales/de/admin.json index fe55c03faa..dea5ab99c2 100644 --- a/scm-ui/ui-webapp/public/locales/de/admin.json +++ b/scm-ui/ui-webapp/public/locales/de/admin.json @@ -38,11 +38,11 @@ }, "markedAsPending": "Als ausstehend markiert", "showPending": "Änderungen anzeigen", - "executePending": "Änderungen ausführen", + "executePending": "Warten auf Neustart", "outdatedPlugins": "{{count}} Plugin aktualisieren", "outdatedPlugins_plural": "{{count}} Plugins aktualisieren", "updateAll": "Alle Plugins aktualisieren", - "cancelPending": "Änderungen abbrechen", + "cancelPending": "Änderungen verwerfen", "noPlugins": "Keine Plugins gefunden.", "pluginCenterStatus": { "ERROR": "Das Plugin Center ist nicht verfügbar. Plugins können weder installiert noch aktualisiert werden.", diff --git a/scm-ui/ui-webapp/public/locales/en/admin.json b/scm-ui/ui-webapp/public/locales/en/admin.json index ceae39de19..fae0368b33 100644 --- a/scm-ui/ui-webapp/public/locales/en/admin.json +++ b/scm-ui/ui-webapp/public/locales/en/admin.json @@ -38,11 +38,11 @@ }, "markedAsPending": "Marked as pending", "showPending": "Show Changes", - "executePending": "Execute Changes", + "executePending": "Waiting for restart", "outdatedPlugins": "Update {{count}} Plugin", "outdatedPlugins_plural": "Update {{count}} Plugins", "updateAll": "Update All Plugins", - "cancelPending": "Cancel Changes", + "cancelPending": "Discard Changes", "noPlugins": "No plugins found.", "pluginCenterStatus": { "ERROR": "The Plugin Center is not available. Plugins can neither be installed nor updated.", diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PluginActionModal.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PluginActionModal.tsx index fc829c90fe..3ab7ae3c5e 100644 --- a/scm-ui/ui-webapp/src/admin/plugins/components/PluginActionModal.tsx +++ b/scm-ui/ui-webapp/src/admin/plugins/components/PluginActionModal.tsx @@ -25,8 +25,9 @@ import * as React from "react"; import { FC, useRef } from "react"; import { useTranslation } from "react-i18next"; import { PendingPlugins, PluginCollection } from "@scm-manager/ui-types"; -import { Button, ButtonGroup, ErrorNotification, Modal } from "@scm-manager/ui-components"; +import { ButtonGroup, ErrorNotification, Modal } from "@scm-manager/ui-components"; import SuccessNotification from "./SuccessNotification"; +import {Button} from "@scm-manager/ui-buttons"; type Props = { onClose: () => void; @@ -143,22 +144,20 @@ const PluginActionModal: FC = ({ {success ? ( ) : ( <> + )} diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PluginBottomActions.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PluginBottomActions.tsx deleted file mode 100644 index 41ec4e8225..0000000000 --- a/scm-ui/ui-webapp/src/admin/plugins/components/PluginBottomActions.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -import * as React from "react"; -import classNames from "classnames"; -import styled from "styled-components"; - -type Props = { - children?: React.Node; -}; - -const ActionWrapper = styled.div` - border: 2px solid var(--scm-border-color); -`; - -export default class PluginBottomActions extends React.Component { - render() { - const { children } = this.props; - return ( - - {children} - - ); - } -} diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PluginEntry.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PluginEntry.tsx index f13f384e3f..24dea1b313 100644 --- a/scm-ui/ui-webapp/src/admin/plugins/components/PluginEntry.tsx +++ b/scm-ui/ui-webapp/src/admin/plugins/components/PluginEntry.tsx @@ -84,13 +84,14 @@ const PluginEntry: FC = ({ plugin, openModal, pluginCenterAuthInfo }) => return undefined; }; - const pendingSpinner = () => ( - ( + <> + + /> ); const actionBar = () => ( @@ -125,7 +126,7 @@ const PluginEntry: FC = ({ plugin, openModal, pluginCenterAuthInfo }) => avatar={} title={plugin.displayName ? {plugin.displayName} : {plugin.name}} description={plugin.description} - contentRight={plugin.pending || plugin.markedForUninstall ? pendingSpinner() : actionBar()} + contentRight={plugin.pending || plugin.markedForUninstall ? pendingInfo() : actionBar()} footerLeft={{plugin.version}} footerRight={null} /> diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PluginTopActions.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PluginTopActions.tsx index 4bc134f95c..399e45a8ee 100644 --- a/scm-ui/ui-webapp/src/admin/plugins/components/PluginTopActions.tsx +++ b/scm-ui/ui-webapp/src/admin/plugins/components/PluginTopActions.tsx @@ -25,7 +25,7 @@ import * as React from "react"; import classNames from "classnames"; type Props = { - children?: React.Node; + children?: React.ReactElement; }; export default class PluginTopActions extends React.Component { @@ -34,9 +34,6 @@ export default class PluginTopActions extends React.Component { return (
= ({ pendingPlugins, onClose }) => { title={t("plugins.showPending")} closeFunction={onClose} body={} - footer={} active={true} /> ); diff --git a/scm-ui/ui-webapp/src/admin/plugins/containers/PluginsOverview.tsx b/scm-ui/ui-webapp/src/admin/plugins/containers/PluginsOverview.tsx index 1c983be93c..432759b728 100644 --- a/scm-ui/ui-webapp/src/admin/plugins/containers/PluginsOverview.tsx +++ b/scm-ui/ui-webapp/src/admin/plugins/containers/PluginsOverview.tsx @@ -25,18 +25,9 @@ import * as React from "react"; import { FC, useState } from "react"; import { useTranslation } from "react-i18next"; import { Plugin } from "@scm-manager/ui-types"; -import { - Button, - ButtonGroup, - ErrorNotification, - Loading, - Notification, - Subtitle, - Title, -} from "@scm-manager/ui-components"; +import { ButtonGroup, ErrorNotification, Loading, Notification, Subtitle, Title } from "@scm-manager/ui-components"; import PluginsList from "../components/PluginList"; import PluginTopActions from "../components/PluginTopActions"; -import PluginBottomActions from "../components/PluginBottomActions"; import ExecutePendingActionModal from "../components/ExecutePendingActionModal"; import CancelPendingActionModal from "../components/CancelPendingActionModal"; import UpdateAllActionModal from "../components/UpdateAllActionModal"; @@ -50,6 +41,8 @@ import { import PluginModal from "../components/PluginModal"; import MyCloudoguBanner from "../components/MyCloudoguBanner"; import PluginCenterAuthInfo from "../components/PluginCenterAuthInfo"; +import styled from "styled-components"; +import { Button } from "@scm-manager/ui-buttons"; export enum PluginAction { INSTALL = "install", @@ -67,6 +60,17 @@ type Props = { installed: boolean; }; +const StickyHeader = styled.div` + position: sticky; + top: 52px; + z-index: 10; + margin-bottom: 1rem; + margin-top: -1rem; + border-bottom: solid 2px var(--scm-border-color); + padding-bottom: 1rem; + padding-top: 1rem; +`; + const PluginsOverview: FC = ({ installed }) => { const [t] = useTranslation("admin"); const { @@ -90,88 +94,60 @@ const PluginsOverview: FC = ({ installed }) => { const error = (installed ? installedPluginsError : availablePluginsError) || pendingPluginsError; const loading = (installed ? isLoadingInstalledPlugins : isLoadingAvailablePlugins) || isLoadingPendingPlugins; - const renderHeader = (actions: React.ReactNode) => { + const renderHeader = (actions: React.ReactElement) => { return ( -
-
- - {t("plugins.title")} <PluginCenterAuthInfo {...pluginCenterAuthInfo} /> - - + +
+
+ + {t("plugins.title")} <PluginCenterAuthInfo {...pluginCenterAuthInfo} /> + + +
+ {actions}
- {actions} -
+ ); }; - const renderFooter = (actions: React.ReactNode) => { - if (actions) { - return {actions}; - } - return null; - }; - const createActions = () => { const buttons = []; if (pendingPlugins && pendingPlugins._links) { if (pendingPlugins._links.execute) { buttons.push( - ); } - if (pendingPlugins._links.cancel) { - if (!pendingPlugins._links.execute) { - buttons.push( - ); } } if (collection && collection._links && collection._links.update) { buttons.push( - ); } - if (buttons.length > 0) { - return {buttons}; + if (pendingPlugins && pendingPlugins._links && pendingPlugins._links.cancel) { + buttons.push( + + ); } - return null; + + return <>{buttons.length > 0 ? {buttons} : null}; }; const computeUpdateAllSize = () => { @@ -238,10 +214,8 @@ const PluginsOverview: FC = ({ installed }) => { return ( <> {renderHeader(actions)} -
{pluginCenterAuthInfo.data?.default ? : null} {renderPluginsList()} - {renderFooter(actions)} {renderModals()} );