Improve apps contextual actions (#2943)
This commit is contained in:
parent
ddf4adda9d
commit
eb01b84412
13 changed files with 128 additions and 209 deletions
|
@ -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 |
|
@ -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)
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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", () => (
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
|
|
@ -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> &
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
9
src/apps/views/AppDetails/messages.ts
Normal file
9
src/apps/views/AppDetails/messages.ts
Normal 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",
|
||||||
|
},
|
||||||
|
});
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
10
src/intl.ts
10
src/intl.ts
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue