saleor-dashboard/src/apps/views/AppsList/AppsList.tsx
AlicjaSzu 211b0b892d
Apps (#599)
* create Apps view

* create more app components, generate types and messages

* apps refactor, update snapshots

* show error message in tooltip  when app installation fail

* update apps components and view, add apps list to storybook

* update defaultMessages

* create app details view

* update AppListPage with Skeleton component

* create app activate/deactivate dialogs, create app details stories

* add AppHeader to AppDetailsPage

* update defaultMessages

* update AppDetails view and components after review

* create custom app details view

* refactor webhooks

* update webhooks fixtures

* update WebhookDetailsPage story

* update strings

* create CustomAppCreate view and components

* update AppListPage story

* create AppInstall view and page

* handle errors in AppInstall view

* update defaultMessages

* add AppInstallPage to storybook

* add status prop to MessageManager

* update defaultMessages

* remove service account section

* remove service account routes

* remove as operator from notify status

* add notifications for app installations

* update styles for deactivated app

* update app installations with local storage

* update defaultMessages

* AppInstall update

* dd delete button to ongoin installations table

* fix active installations condition

* fix error messages in AppsList

* update defaultMessages

* add iframe to AppDetailsPage

* create AppDetailsSettingsPage

* install macaw-ui

* apps styles clean up

* update schema, fixtures

* few apps updates

* WebhookCreate - fix onBack button name

* WebhookCreatePage story update

* rename apps table from external to thirdparty

* update defaultMessages

* fix test, update snapshots

* AppDetailsSettings - add token to headers

* fix first number in local apps query

* app details settings - use shop domain host

* add onSettingsRowClick to InstalledApps

* resolve conflicts

* update changelog and messages

* add noopener noreferrer do app privacy link

* update snapshots

* update snapshots

* updates after review

* update defaultMessages

* CustomAppDetails - add missing notify status
2020-07-22 12:54:15 +02:00

341 lines
9.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import useListSettings from "@saleor/hooks/useListSettings";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import { ListViews } from "@saleor/types";
import getAppErrorMessage from "@saleor/utils/errors/app";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import React, { useEffect, useRef } from "react";
import { useIntl } from "react-intl";
import {
AppSortField,
AppTypeEnum,
JobStatusEnum,
OrderDirection
} from "../../../types/globalTypes";
import AppDeleteDialog from "../../components/AppDeleteDialog";
import AppInProgressDeleteDialog from "../../components/AppInProgressDeleteDialog";
import AppsListPage from "../../components/AppsListPage";
import {
useAppDeleteFailedInstallationMutation,
useAppDeleteMutation,
useAppRetryInstallMutation
} from "../../mutations";
import { useAppsInProgressListQuery, useAppsListQuery } from "../../queries";
import { AppDelete } from "../../types/AppDelete";
import { AppDeleteFailedInstallation } from "../../types/AppDeleteFailedInstallation";
import { AppsInstallations_appsInstallations } from "../../types/AppsInstallations";
import { AppsList_apps_edges } from "../../types/AppsList";
import {
AppListUrlDialog,
AppListUrlQueryParams,
appSettingsUrl,
appsListUrl,
appUrl,
customAppAddUrl,
customAppUrl
} from "../../urls";
const getCurrentAppName = (id: string, collection?: AppsList_apps_edges[]) =>
collection?.find(edge => edge.node.id === id)?.node?.name;
const getAppInProgressName = (
id: string,
collection?: AppsInstallations_appsInstallations[]
) => collection?.find(app => app.id === id)?.appName;
interface AppsListProps {
params: AppListUrlQueryParams;
}
export const AppsList: React.FC<AppsListProps> = ({ params }) => {
const { action } = params;
const [activeInstallations, setActiveInstallations] = useLocalStorage(
"activeInstallations",
[]
);
const notify = useNotifier();
const intl = useIntl();
const navigate = useNavigator();
const { updateListSettings, settings } = useListSettings(ListViews.APPS_LIST);
const paginate = usePaginator();
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = {
sort: {
direction: OrderDirection.DESC,
field: AppSortField.CREATION_DATE
}
};
const intervalId = useRef<null | number>(null);
const removeInstallation = (id: string) =>
setActiveInstallations(installations =>
installations.filter(item => item.id !== id)
);
const {
data: appsInProgressData,
loading: loadingAppsInProgress,
refetch: appsInProgressRefetch
} = useAppsInProgressListQuery({
displayLoader: false
});
const { data, loading, refetch } = useAppsListQuery({
displayLoader: true,
variables: {
...paginationState,
...queryVariables,
filter: {
type: AppTypeEnum.THIRDPARTY
}
}
});
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
data?.apps?.pageInfo,
paginationState,
params
);
const {
data: customAppsData,
loading: customAppsLoading,
refetch: customAppsRefetch
} = useAppsListQuery({
displayLoader: true,
variables: {
first: 100,
...queryVariables,
filter: {
type: AppTypeEnum.LOCAL
}
}
});
const installedAppNotify = (name: string) => {
notify({
status: "success",
text: intl.formatMessage(
{
defaultMessage: "{name} is ready to be used",
description: "app has been installed"
},
{ name }
),
title: intl.formatMessage({
defaultMessage: "App installed",
description: "message title"
})
});
};
const [retryInstallApp] = useAppRetryInstallMutation({
onCompleted: data => {
const errors = data.appRetryInstall.errors;
if (!errors.length) {
const appInstallation = data.appRetryInstall.appInstallation;
setActiveInstallations(installations => [
...installations,
{ id: appInstallation.id, name: appInstallation.appName }
]);
} else {
errors.forEach(error =>
notify({ status: "error", text: getAppErrorMessage(error, intl) })
);
}
}
});
const [openModal, closeModal] = createDialogActionHandlers<
AppListUrlDialog,
AppListUrlQueryParams
>(navigate, appsListUrl, params);
const onAppRemove = (data: AppDelete) => {
const errors = data.appDelete.errors;
if (errors.length === 0) {
if (data.appDelete.app.type === AppTypeEnum.LOCAL) {
customAppsRefetch();
} else {
refetch();
}
closeModal();
removeAppNotify();
} else {
errors.forEach(error =>
notify({
status: "error",
text: getAppErrorMessage(error, intl)
})
);
}
};
const [deleteApp, deleteAppOpts] = useAppDeleteMutation({
onCompleted: data => {
onAppRemove(data);
}
});
const [
deleteInProgressApp,
deleteInProgressAppOpts
] = useAppDeleteFailedInstallationMutation({
onCompleted: data => {
onAppInProgressRemove(data);
}
});
useEffect(() => {
const appsInProgress = appsInProgressData?.appsInstallations || [];
if (activeInstallations.length && !!appsInProgressData) {
if (!intervalId.current) {
intervalId.current = window.setInterval(
() => appsInProgressRefetch(),
2000
);
}
activeInstallations.forEach(installation => {
const item = appsInProgress?.find(app => app.id === installation.id);
if (!item) {
removeInstallation(installation.id);
installedAppNotify(installation.name);
appsInProgressRefetch();
} else if (item.status === JobStatusEnum.SUCCESS) {
removeInstallation(installation.id);
installedAppNotify(item.appName);
refetch();
} else if (item.status === JobStatusEnum.FAILED) {
removeInstallation(installation.id);
notify({
status: "error",
text: item.message,
title: intl.formatMessage(
{
defaultMessage: "Couldnt Install {name}",
description: "message title"
},
{ name: item.appName }
)
});
}
});
}
if (!activeInstallations.length && intervalId.current) {
clearInterval(intervalId.current);
intervalId.current = null;
}
return () => {
if (intervalId.current) {
clearInterval(intervalId.current);
intervalId.current = null;
}
};
}, [activeInstallations.length, appsInProgressData]);
const handleRemoveInProgressConfirm = () =>
deleteInProgressApp({
variables: {
id: params.id
}
});
const handleRemoveConfirm = () =>
deleteApp({
variables: {
id: params.id
}
});
const removeAppNotify = () => {
notify({
status: "success",
text: intl.formatMessage({
defaultMessage: "App successfully removed",
description: "app has been removed"
})
});
};
const onAppInProgressRemove = (data: AppDeleteFailedInstallation) => {
const errors = data.appDeleteFailedInstallation.errors;
if (errors.length === 0) {
removeAppNotify();
appsInProgressRefetch();
closeModal();
} else {
errors.forEach(error =>
notify({
status: "error",
text: getAppErrorMessage(error, intl)
})
);
}
};
const onAppInstallRetry = (id: string) =>
retryInstallApp({ variables: { id } });
const installedApps = data?.apps?.edges;
const customApps = customAppsData?.apps?.edges;
return (
<>
<AppDeleteDialog
confirmButtonState={deleteAppOpts.status}
name={getCurrentAppName(
params.id,
action === "remove-app" ? installedApps : customApps
)}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
type={action === "remove-app" ? "EXTERNAL" : "CUSTOM"}
open={action === "remove-app" || action === "remove-custom-app"}
/>
<AppInProgressDeleteDialog
confirmButtonState={deleteInProgressAppOpts.status}
name={getAppInProgressName(
params.id,
appsInProgressData?.appsInstallations
)}
onClose={closeModal}
onConfirm={handleRemoveInProgressConfirm}
open={action === "remove"}
/>
<AppsListPage
installedAppsList={installedApps}
customAppsList={customApps}
appsInProgressList={appsInProgressData}
loadingAppsInProgress={loadingAppsInProgress}
disabled={loading || customAppsLoading}
settings={settings}
pageInfo={pageInfo}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(appUrl(id))}
onSettingsRowClick={id => () => navigate(appSettingsUrl(id))}
onAppInstallRetry={onAppInstallRetry}
navigateToCustomApp={id => () => navigate(customAppUrl(id))}
navigateToCustomAppCreate={() => navigate(customAppAddUrl)}
onInstalledAppRemove={id =>
openModal("remove-app", {
id
})
}
onCustomAppRemove={id =>
openModal("remove-custom-app", {
id
})
}
onAppInProgressRemove={id =>
openModal("remove", {
id
})
}
/>
</>
);
};
export default AppsList;