Improve apps contextual actions (#2943)

This commit is contained in:
Dawid 2023-01-12 09:19:13 +01:00 committed by GitHub
parent ddf4adda9d
commit eb01b84412
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 209 deletions

View file

@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" height="20px" width="20px" viewBox="0 0 13.2 14.4"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" height="20px" width="20px" viewBox="0 0 13.2 14.4">
<path fill="#828282" d="M13.2,2.7v0.6c0,0.1,0,0.2-0.1,0.2c-0.1,0.1-0.1,0.1-0.2,0.1H12v8.9c0,0.5-0.1,1-0.4,1.3 <path fill="currentColor" d="M13.2,2.7v0.6c0,0.1,0,0.2-0.1,0.2c-0.1,0.1-0.1,0.1-0.2,0.1H12v8.9c0,0.5-0.1,1-0.4,1.3
c-0.3,0.4-0.6,0.6-1.1,0.6H2.7c-0.4,0-0.8-0.2-1.1-0.5s-0.4-0.8-0.4-1.3V3.6H0.3c-0.1,0-0.2,0-0.2-0.1C0,3.5,0,3.4,0,3.3V2.7 c-0.3,0.4-0.6,0.6-1.1,0.6H2.7c-0.4,0-0.8-0.2-1.1-0.5s-0.4-0.8-0.4-1.3V3.6H0.3c-0.1,0-0.2,0-0.2-0.1C0,3.5,0,3.4,0,3.3V2.7
c0-0.1,0-0.2,0.1-0.2c0.1-0.1,0.1-0.1,0.2-0.1h2.9l0.7-1.6C4,0.6,4.1,0.4,4.4,0.2C4.6,0.1,4.9,0,5.1,0h3c0.3,0,0.5,0.1,0.7,0.2 c0-0.1,0-0.2,0.1-0.2c0.1-0.1,0.1-0.1,0.2-0.1h2.9l0.7-1.6C4,0.6,4.1,0.4,4.4,0.2C4.6,0.1,4.9,0,5.1,0h3c0.3,0,0.5,0.1,0.7,0.2
c0.2,0.2,0.4,0.4,0.5,0.6L10,2.4h2.9c0.1,0,0.2,0,0.2,0.1C13.2,2.5,13.2,2.6,13.2,2.7z M10.8,12.5V3.6H2.4v8.9c0,0.1,0,0.3,0.1,0.4 c0.2,0.2,0.4,0.4,0.5,0.6L10,2.4h2.9c0.1,0,0.2,0,0.2,0.1C13.2,2.5,13.2,2.6,13.2,2.7z M10.8,12.5V3.6H2.4v8.9c0,0.1,0,0.3,0.1,0.4

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -11,6 +11,7 @@ const props: AppDetailsPageProps = {
navigateToApp: () => undefined, navigateToApp: () => undefined,
onAppActivateOpen: () => undefined, onAppActivateOpen: () => undefined,
onAppDeactivateOpen: () => undefined, onAppDeactivateOpen: () => undefined,
onAppDeleteOpen: () => undefined,
}; };
storiesOf("Apps / App details", module) storiesOf("Apps / App details", module)

View file

@ -9,13 +9,14 @@ import ExternalLink from "@saleor/components/ExternalLink";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { AppQuery } from "@saleor/graphql"; import { AppQuery } from "@saleor/graphql";
import { sectionNames } from "@saleor/intl"; import { buttonMessages, sectionNames } from "@saleor/intl";
import React from "react"; import React from "react";
import SVG from "react-inlinesvg"; import SVG from "react-inlinesvg";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import activateIcon from "../../../../assets/images/activate-icon.svg"; import activateIcon from "../../../../assets/images/activate-icon.svg";
import deleteIcon from "../../../../assets/images/delete.svg";
import supportIcon from "../../../../assets/images/support-icon.svg"; import supportIcon from "../../../../assets/images/support-icon.svg";
import { useStyles } from "../../styles"; import { useStyles } from "../../styles";
import DeactivatedText from "../DeactivatedText"; import DeactivatedText from "../DeactivatedText";
@ -26,6 +27,7 @@ export interface AppDetailsPageProps {
navigateToApp: () => void; navigateToApp: () => void;
onAppActivateOpen: () => void; onAppActivateOpen: () => void;
onAppDeactivateOpen: () => void; onAppDeactivateOpen: () => void;
onAppDeleteOpen: () => void;
} }
export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
@ -34,6 +36,7 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
navigateToApp, navigateToApp,
onAppActivateOpen, onAppActivateOpen,
onAppDeactivateOpen, onAppDeactivateOpen,
onAppDeleteOpen,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
@ -80,19 +83,19 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
> >
<SVG src={activateIcon} /> <SVG src={activateIcon} />
{data?.isActive ? ( {data?.isActive ? (
<FormattedMessage <FormattedMessage {...buttonMessages.deactivate} />
id="whTEcF"
defaultMessage="Deactivate"
description="link"
/>
) : ( ) : (
<FormattedMessage <FormattedMessage {...buttonMessages.activate} />
id="P5twxk"
defaultMessage="Activate"
description="link"
/>
)} )}
</ButtonBase> </ButtonBase>
<ButtonBase
className={classes.headerLinkContainer}
disableRipple
onClick={onAppDeleteOpen}
>
<SVG src={deleteIcon} />
<FormattedMessage {...buttonMessages.delete} />
</ButtonBase>
</div> </div>
) : ( ) : (
<Skeleton /> <Skeleton />

View file

@ -1,4 +1,3 @@
import { AppListContext } from "@saleor/apps/context";
import { import {
listActionsProps, listActionsProps,
pageListProps, pageListProps,
@ -28,18 +27,11 @@ const props: AppsListPageProps = {
installedAppsList: appsList, installedAppsList: appsList,
onAppInProgressRemove: () => undefined, onAppInProgressRemove: () => undefined,
onAppInstallRetry: () => undefined, onAppInstallRetry: () => undefined,
onInstalledAppRemove: () => undefined, onSettingsAppOpen: () => undefined,
}; };
storiesOf("Apps / Apps list", module) storiesOf("Apps / Apps list", module)
.addDecorator(Decorator) .addDecorator(Decorator)
.addDecorator(story => (
<AppListContext.Provider
value={{ activateApp: () => undefined, deactivateApp: () => undefined }}
>
{story()}
</AppListContext.Provider>
))
.addDecorator(PaginatorContextDecorator) .addDecorator(PaginatorContextDecorator)
.add("default", () => <AppsListPage {...props} />) .add("default", () => <AppsListPage {...props} />)
.add("loading", () => ( .add("loading", () => (

View file

@ -19,7 +19,7 @@ import InstalledApps from "../InstalledApps/InstalledApps";
export interface AppsListPageProps extends ListProps { export interface AppsListPageProps extends ListProps {
installedAppsList: AppListItemFragment[]; installedAppsList: AppListItemFragment[];
appsInProgressList?: AppsInstallationsQuery; appsInProgressList?: AppsInstallationsQuery;
onInstalledAppRemove: (id: string) => void; onSettingsAppOpen: (id: string) => void;
onAppInProgressRemove: (id: string) => void; onAppInProgressRemove: (id: string) => void;
onAppInstallRetry: (id: string) => void; onAppInstallRetry: (id: string) => void;
} }
@ -42,7 +42,7 @@ const useStyles = makeStyles(
const AppsListPage: React.FC<AppsListPageProps> = ({ const AppsListPage: React.FC<AppsListPageProps> = ({
appsInProgressList, appsInProgressList,
installedAppsList, installedAppsList,
onInstalledAppRemove, onSettingsAppOpen,
onAppInProgressRemove, onAppInProgressRemove,
onAppInstallRetry, onAppInstallRetry,
...listProps ...listProps
@ -123,7 +123,7 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
description: "section header", description: "section header",
})} })}
appsList={thirdPartyApps} appsList={thirdPartyApps}
onRemove={onInstalledAppRemove} onSettingsClick={onSettingsAppOpen}
displayQuickManifestButton displayQuickManifestButton
{...listProps} {...listProps}
/> />
@ -149,7 +149,7 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
description: "section header", description: "section header",
})} })}
appsList={saleorApps} appsList={saleorApps}
onRemove={onInstalledAppRemove} onSettingsClick={onSettingsAppOpen}
{...listProps} {...listProps}
/> />
<div className={styles.browseMarketplaceContainer}> <div className={styles.browseMarketplaceContainer}>

View file

@ -1,13 +1,6 @@
import { import { Card, TableBody, TableCell, Typography } from "@material-ui/core";
Card,
Switch,
TableBody,
TableCell,
Typography,
} from "@material-ui/core";
import { AppManifestTableDisplay } from "@saleor/apps/components/AppManifestTableDisplay/AppManifestTableDisplay"; import { AppManifestTableDisplay } from "@saleor/apps/components/AppManifestTableDisplay/AppManifestTableDisplay";
import { InstallWithManifestFormButton } from "@saleor/apps/components/InstallWithManifestFormButton"; import { InstallWithManifestFormButton } from "@saleor/apps/components/InstallWithManifestFormButton";
import { useAppListContext } from "@saleor/apps/context";
import { appUrl, createAppInstallUrl } from "@saleor/apps/urls"; import { appUrl, createAppInstallUrl } from "@saleor/apps/urls";
import { isAppInTunnel } from "@saleor/apps/utils"; import { isAppInTunnel } from "@saleor/apps/utils";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
@ -16,7 +9,7 @@ import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableB
import TableRowLink from "@saleor/components/TableRowLink"; import TableRowLink from "@saleor/components/TableRowLink";
import { AppListItemFragment } from "@saleor/graphql"; import { AppListItemFragment } from "@saleor/graphql";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import { DeleteIcon, ResponsiveTable } from "@saleor/macaw-ui"; import { ResponsiveTable, SettingsIcon } from "@saleor/macaw-ui";
import { renderCollection } from "@saleor/misc"; import { renderCollection } from "@saleor/misc";
import { ListProps } from "@saleor/types"; import { ListProps } from "@saleor/types";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
@ -28,20 +21,19 @@ import AppsSkeleton from "../AppsSkeleton";
export interface InstalledAppsProps extends ListProps { export interface InstalledAppsProps extends ListProps {
appsList: AppListItemFragment[]; appsList: AppListItemFragment[];
onRemove: (id: string) => void; onSettingsClick: (id: string) => void;
displayQuickManifestButton?: boolean; displayQuickManifestButton?: boolean;
title: string; title: string;
} }
const InstalledApps: React.FC<InstalledAppsProps> = ({ const InstalledApps: React.FC<InstalledAppsProps> = ({
appsList, appsList,
onRemove, onSettingsClick,
title, title,
displayQuickManifestButton = false, displayQuickManifestButton = false,
...props ...props
}) => { }) => {
const classes = useStyles(props); const classes = useStyles(props);
const { activateApp, deactivateApp } = useAppListContext();
const navigate = useNavigator(); const navigate = useNavigator();
const navigateToAppInstallPage = useCallback( const navigateToAppInstallPage = useCallback(
@ -51,14 +43,6 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
[navigate], [navigate],
); );
const getHandleToggle = (app: AppListItemFragment) => () => {
if (app.isActive) {
deactivateApp(app.id);
} else {
activateApp(app.id);
}
};
return ( return (
<Card className={classes.apps}> <Card className={classes.apps}>
<CardTitle <CardTitle
@ -102,20 +86,14 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
{app.manifestUrl && ( {app.manifestUrl && (
<AppManifestTableDisplay manifestUrl={app.manifestUrl} /> <AppManifestTableDisplay manifestUrl={app.manifestUrl} />
)} )}
<TableButtonWrapper>
<Switch
checked={!!app.isActive}
onChange={getHandleToggle(app)}
/>
</TableButtonWrapper>
<AppPermissions permissions={app.permissions || []} /> <AppPermissions permissions={app.permissions || []} />
<TableButtonWrapper> <TableButtonWrapper>
<IconButton <IconButton
variant="secondary" variant="secondary"
color="primary" color="primary"
onClick={() => onRemove(app.id)} onClick={() => onSettingsClick(app.id)}
> >
<DeleteIcon /> <SettingsIcon />
</IconButton> </IconButton>
</TableButtonWrapper> </TableButtonWrapper>
</TableCell> </TableCell>

View file

@ -1,21 +0,0 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import React from "react";
export interface AppListContextValues {
activateApp: (appId: string) => void;
deactivateApp: (appId: string) => void;
}
export const AppListContext = React.createContext<
AppListContextValues | undefined
>(undefined);
export const useAppListContext = () => {
const context = React.useContext(AppListContext);
if (!context) {
throw new Error("useAppListContext must be used within a AppListContext");
}
return context;
};

View file

@ -6,13 +6,12 @@ import { ActiveTab, Dialog, Pagination, SingleAction } from "../types";
export const MANIFEST_ATTR = "manifestUrl"; export const MANIFEST_ATTR = "manifestUrl";
export type AppListUrlDialog = export type AppListUrlDialog = "app-installation-remove";
| "remove"
| "remove-app"
| "app-activate"
| "app-deactivate";
export type AppDetailsUrlDialog = "app-activate" | "app-deactivate"; export type AppDetailsUrlDialog =
| "app-activate"
| "app-deactivate"
| "app-delete";
export type AppListUrlQueryParams = ActiveTab & export type AppListUrlQueryParams = ActiveTab &
Dialog<AppListUrlDialog> & Dialog<AppListUrlDialog> &

View file

@ -1,4 +1,11 @@
import { AppsInstallationsQuery } from "@saleor/graphql";
const tunnelKeywords = [".ngrok.io", ".saleor.live"]; const tunnelKeywords = [".ngrok.io", ".saleor.live"];
export const isAppInTunnel = (manifestUrl: string) => export const isAppInTunnel = (manifestUrl: string) =>
Boolean(tunnelKeywords.find(keyword => manifestUrl.includes(keyword))); Boolean(tunnelKeywords.find(keyword => manifestUrl.includes(keyword)));
export const getAppInProgressName = (
id: string,
collection?: AppsInstallationsQuery["appsInstallations"],
) => collection?.find(app => app.id === id)?.appName || id;

View file

@ -1,8 +1,12 @@
import { useApolloClient } from "@apollo/client";
import AppDeleteDialog from "@saleor/apps/components/AppDeleteDialog";
import { appMessages } from "@saleor/apps/messages"; import { appMessages } from "@saleor/apps/messages";
import { EXTENSION_LIST_QUERY } from "@saleor/apps/queries";
import NotFoundPage from "@saleor/components/NotFoundPage"; import NotFoundPage from "@saleor/components/NotFoundPage";
import { import {
useAppActivateMutation, useAppActivateMutation,
useAppDeactivateMutation, useAppDeactivateMutation,
useAppDeleteMutation,
useAppQuery, useAppQuery,
} from "@saleor/graphql"; } from "@saleor/graphql";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
@ -22,6 +26,7 @@ import {
appsListPath, appsListPath,
appUrl, appUrl,
} from "../../urls"; } from "../../urls";
import { messages } from "./messages";
interface AppDetailsProps { interface AppDetailsProps {
id: string; id: string;
@ -29,6 +34,7 @@ interface AppDetailsProps {
} }
export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => { export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => {
const client = useApolloClient();
const { data, loading, refetch } = useAppQuery({ const { data, loading, refetch } = useAppQuery({
displayLoader: true, displayLoader: true,
variables: { id }, variables: { id },
@ -85,17 +91,38 @@ export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => {
}, },
}); });
const refetchExtensionList = () => {
client.refetchQueries({
include: [EXTENSION_LIST_QUERY],
});
};
const removeAppNotify = () => {
notify({
status: "success",
text: intl.formatMessage(messages.appRemoved),
});
};
const [deleteApp, deleteAppOpts] = useAppDeleteMutation({
onCompleted: data => {
if (!data?.appDelete?.errors?.length) {
refetch();
closeModal();
refetchExtensionList();
removeAppNotify();
}
},
});
const [openModal, closeModal] = createDialogActionHandlers< const [openModal, closeModal] = createDialogActionHandlers<
AppDetailsUrlDialog, AppDetailsUrlDialog,
AppDetailsUrlQueryParams AppDetailsUrlQueryParams
>(navigate, params => appDetailsUrl(id, params), params); >(navigate, params => appDetailsUrl(id, params), params);
const handleActivateConfirm = () => { const handleActivateConfirm = () => activateApp(mutationOpts);
activateApp(mutationOpts); const handleDeactivateConfirm = () => deactivateApp(mutationOpts);
}; const handleRemoveConfirm = () => deleteApp(mutationOpts);
const handleDeactivateConfirm = () => {
deactivateApp(mutationOpts);
};
if (!appExists) { if (!appExists) {
return <NotFoundPage backHref={appsListPath} />; return <NotFoundPage backHref={appsListPath} />;
@ -117,12 +144,21 @@ export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => {
onConfirm={handleDeactivateConfirm} onConfirm={handleDeactivateConfirm}
open={params.action === "app-deactivate"} open={params.action === "app-deactivate"}
/> />
<AppDeleteDialog
confirmButtonState={deleteAppOpts.status}
name={data?.app?.name || ""}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
type="EXTERNAL"
open={params.action === "app-delete"}
/>
<AppDetailsPage <AppDetailsPage
data={data?.app || null} data={data?.app || null}
loading={loading} loading={loading}
navigateToApp={() => navigate(appUrl(id))} navigateToApp={() => navigate(appUrl(id))}
onAppActivateOpen={() => openModal("app-activate")} onAppActivateOpen={() => openModal("app-activate")}
onAppDeactivateOpen={() => openModal("app-deactivate")} onAppDeactivateOpen={() => openModal("app-deactivate")}
onAppDeleteOpen={() => openModal("app-delete")}
/> />
</> </>
); );

View file

@ -0,0 +1,9 @@
import { defineMessages } from "react-intl";
export const messages = defineMessages({
appRemoved: {
id: "uIPD1i",
defaultMessage: "App successfully removed",
description: "app has been removed",
},
});

View file

@ -1,17 +1,11 @@
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import AppActivateDialog from "@saleor/apps/components/AppActivateDialog"; import { getAppInProgressName } from "@saleor/apps/utils";
import AppDeactivateDialog from "@saleor/apps/components/AppDeactivateDialog";
import { AppListContext, AppListContextValues } from "@saleor/apps/context";
import { import {
AppsInstallationsQuery,
AppSortField, AppSortField,
AppTypeEnum, AppTypeEnum,
JobStatusEnum, JobStatusEnum,
OrderDirection, OrderDirection,
useAppActivateMutation,
useAppDeactivateMutation,
useAppDeleteFailedInstallationMutation, useAppDeleteFailedInstallationMutation,
useAppDeleteMutation,
useAppRetryInstallMutation, useAppRetryInstallMutation,
useAppsInstallationsQuery, useAppsInstallationsQuery,
useAppsListQuery, useAppsListQuery,
@ -25,28 +19,23 @@ import usePaginator, {
PageInfo, PageInfo,
PaginatorContext, PaginatorContext,
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import { findById } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { mapEdgesToItems } from "@saleor/utils/maps"; import { mapEdgesToItems } from "@saleor/utils/maps";
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import AppDeleteDialog from "../../components/AppDeleteDialog";
import AppInProgressDeleteDialog from "../../components/AppInProgressDeleteDialog"; import AppInProgressDeleteDialog from "../../components/AppInProgressDeleteDialog";
import AppsListPage from "../../components/AppsListPage"; import AppsListPage from "../../components/AppsListPage";
import { EXTENSION_LIST_QUERY } from "../../queries"; import { EXTENSION_LIST_QUERY } from "../../queries";
import { import {
appDetailsUrl,
AppListUrlDialog, AppListUrlDialog,
AppListUrlQueryParams, AppListUrlQueryParams,
appsListUrl, appsListUrl,
} from "../../urls"; } from "../../urls";
import { messages } from "./messages"; import { messages } from "./messages";
const getAppInProgressName = (
id: string,
collection?: AppsInstallationsQuery["appsInstallations"],
) => collection?.find(app => app.id === id)?.appName || id;
interface AppsListProps { interface AppsListProps {
params: AppListUrlQueryParams; params: AppListUrlQueryParams;
} }
@ -127,45 +116,11 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
} }
}, },
}); });
const [activateApp, activateAppResult] = useAppActivateMutation({
onCompleted: data => {
if (!data?.appActivate?.errors?.length) {
notify({
status: "success",
text: intl.formatMessage(messages.appActivated),
});
refetch();
closeModal();
}
},
});
const [deactivateApp, deactivateAppResult] = useAppDeactivateMutation({
onCompleted: data => {
if (!data?.appDeactivate?.errors?.length) {
notify({
status: "success",
text: intl.formatMessage(messages.appDeactivated),
});
refetch();
closeModal();
}
},
});
const [openModal, closeModal] = createDialogActionHandlers< const [openModal, closeModal] = createDialogActionHandlers<
AppListUrlDialog, AppListUrlDialog,
AppListUrlQueryParams AppListUrlQueryParams
>(navigate, appsListUrl, params); >(navigate, appsListUrl, params);
const [deleteApp, deleteAppOpts] = useAppDeleteMutation({
onCompleted: data => {
if (!data?.appDelete?.errors?.length) {
refetch();
closeModal();
refetchExtensionList();
removeAppNotify();
}
},
});
const [ const [
deleteInProgressApp, deleteInProgressApp,
deleteInProgressAppOpts, deleteInProgressAppOpts,
@ -236,13 +191,6 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
}, },
}); });
const handleRemoveConfirm = () =>
deleteApp({
variables: {
id: params?.id || "",
},
});
const removeAppNotify = () => { const removeAppNotify = () => {
notify({ notify({
status: "success", status: "success",
@ -250,81 +198,38 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
}); });
}; };
const handleActivateAppConfirm = () =>
activateApp({ variables: { id: params?.id || "" } });
const handleDeactivateAppConfirm = () =>
deactivateApp({ variables: { id: params?.id || "" } });
const onAppInstallRetry = (id: string) => const onAppInstallRetry = (id: string) =>
retryInstallApp({ variables: { id } }); retryInstallApp({ variables: { id } });
const context: AppListContextValues = React.useMemo(
() => ({
activateApp: id => openModal("app-activate", { id }),
deactivateApp: id => openModal("app-deactivate", { id }),
}),
[activateApp, deactivateApp],
);
const installedApps = mapEdgesToItems(data?.apps || { edges: [] }) || []; const installedApps = mapEdgesToItems(data?.apps || { edges: [] }) || [];
const currentAppName = findById(params?.id || "", installedApps)?.name || "";
return ( return (
<AppListContext.Provider value={context}> <PaginatorContext.Provider value={paginationValues}>
<PaginatorContext.Provider value={paginationValues}> <AppInProgressDeleteDialog
<AppDeleteDialog confirmButtonState={deleteInProgressAppOpts.status}
confirmButtonState={deleteAppOpts.status} name={getAppInProgressName(
name={currentAppName} params.id || "",
onClose={closeModal} appsInProgressData?.appsInstallations,
onConfirm={handleRemoveConfirm} )}
type="EXTERNAL" onClose={closeModal}
open={action === "remove-app"} onConfirm={handleRemoveInProgressConfirm}
/> open={action === "app-installation-remove"}
<AppActivateDialog />
confirmButtonState={activateAppResult.status} <AppsListPage
name={currentAppName} installedAppsList={installedApps}
onClose={closeModal} appsInProgressList={appsInProgressData}
onConfirm={handleActivateAppConfirm} disabled={loading}
open={params.action === "app-activate"} settings={settings}
/> onUpdateListSettings={updateListSettings}
<AppDeactivateDialog onAppInstallRetry={onAppInstallRetry}
confirmButtonState={deactivateAppResult.status} onSettingsAppOpen={id => navigate(appDetailsUrl(id))}
name={currentAppName} onAppInProgressRemove={id =>
onClose={closeModal} openModal("app-installation-remove", {
onConfirm={handleDeactivateAppConfirm} id,
open={params.action === "app-deactivate"} })
/> }
<AppInProgressDeleteDialog />
confirmButtonState={deleteInProgressAppOpts.status} </PaginatorContext.Provider>
name={getAppInProgressName(
params.id || "",
appsInProgressData?.appsInstallations,
)}
onClose={closeModal}
onConfirm={handleRemoveInProgressConfirm}
open={action === "remove"}
/>
<AppsListPage
installedAppsList={installedApps}
appsInProgressList={appsInProgressData}
disabled={loading}
settings={settings}
onUpdateListSettings={updateListSettings}
onAppInstallRetry={onAppInstallRetry}
onInstalledAppRemove={id =>
openModal("remove-app", {
id,
})
}
onAppInProgressRemove={id =>
openModal("remove", {
id,
})
}
/>
</PaginatorContext.Provider>
</AppListContext.Provider>
); );
}; };

View file

@ -237,6 +237,16 @@ export const buttonMessages = defineMessages({
defaultMessage: "Assign", defaultMessage: "Assign",
description: "button", description: "button",
}, },
activate: {
id: "+b3KCV",
defaultMessage: "Activate",
description: "button",
},
deactivate: {
id: "gygOA1",
defaultMessage: "Deactivate",
description: "button",
},
back: { back: {
id: "0OfZJA", id: "0OfZJA",
defaultMessage: "Back", defaultMessage: "Back",