diff --git a/cypress.config.js b/cypress.config.js index 37080d858..17fc73980 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -11,8 +11,8 @@ module.exports = defineConfig({ viewportWidth: 1400, viewportHeight: 660, retries: { - runMode: 1, - openMode: 0, + runMode: 2, + openMode: 1, }, e2e: { env: { @@ -23,7 +23,7 @@ module.exports = defineConfig({ config = require("./cypress/plugins/index.js")(on, config); on("after:spec", (spec, results) => { if (results && results.video) { - return fs.unlink(results.video, function(err) { + return fs.unlink(results.video, function (err) { if (err) { console.warn(`Could not remove video - ${err}`); } else { diff --git a/cypress/e2e/apps.js b/cypress/e2e/apps.js index 6f81efae2..e7577e291 100644 --- a/cypress/e2e/apps.js +++ b/cypress/e2e/apps.js @@ -98,8 +98,6 @@ describe("As a staff user I want to manage apps", () => { const randomAppName = `${startsWith}${faker.datatype.number()}`; cy.visit(urlList.apps) - .get(APPS_LIST.webhookAndEventsTab) - .click() .get(APPS_LIST.createLocalAppButton) .click() .get(APP_DETAILS.nameInput) diff --git a/cypress/elements/apps/appsList.js b/cypress/elements/apps/appsList.js index d594d2b7f..e46683d48 100644 --- a/cypress/elements/apps/appsList.js +++ b/cypress/elements/apps/appsList.js @@ -1,4 +1,3 @@ export const APPS_LIST = { createLocalAppButton: '[data-test-id="create-app"]', - webhookAndEventsTab: '[id="WEBHOOKS_AND_EVENTS"]', }; diff --git a/cypress/fixtures/urlList.js b/cypress/fixtures/urlList.js index f9ad97da0..420f1ff5b 100644 --- a/cypress/fixtures/urlList.js +++ b/cypress/fixtures/urlList.js @@ -1,7 +1,7 @@ export const urlList = { apiUri: Cypress.env("API_URI"), addProduct: "products/add", - apps: "apps/", + apps: "custom-apps/", attributes: "attributes/", channels: "channels/", categories: "categories/", @@ -35,7 +35,7 @@ export const urlList = { export const addVariantUrl = productId => `${urlList.products}${productId}/${urlList.variants}add`; -export const appDetailsUrl = appId => `${urlList.apps}custom/${appId}`; +export const appDetailsUrl = appId => `${urlList.apps}${appId}`; export const attributeDetailsUrl = attributeId => `${urlList.attributes}${attributeId}`; diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 58645c770..ee2982193 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1105,10 +1105,6 @@ "context": "delete variant dialog title", "string": "Delete Product Variants" }, - "6nSTuC": { - "context": "webhooks section name", - "string": "Webhooks" - }, "6sjBvJ": { "context": "input placeholder", "string": "Search by name, email, etc..." @@ -1612,6 +1608,10 @@ "context": "order history message", "string": "Shipping details was sent to customer" }, + "BFR6CF": { + "context": "webhooks and events section name", + "string": "Webhooks & Events" + }, "BHQrgz": { "context": "number of subcategories", "string": "Subcategories" @@ -2290,9 +2290,6 @@ "context": "label", "string": "Min Delivery Time" }, - "GDJHXl": { - "string": "Local apps are custom webhooks & token pairs that can be used to connect apps and access Saleor API." - }, "GFJabu": { "context": "dialog header", "string": "Delete Variant" @@ -2969,6 +2966,9 @@ "context": "order refund amount", "string": "Authorized Amount" }, + "L/sNGY": { + "string": "Local apps are custom webhooks & token pairs that can be used to connect apps and access Saleor API." + }, "L5io1l": { "context": "returned products list title", "string": "Products returned" @@ -4236,9 +4236,6 @@ "context": "attribute values", "string": "Value {number}" }, - "UxTSw7": { - "string": "Webhooks & Events" - }, "UxdBmI": { "context": "collection availability", "string": "Availability" @@ -4929,6 +4926,9 @@ "context": "note input subtitle", "string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history" }, + "Zz67wc": { + "string": "View and update your webhooks and events." + }, "a+iRI1": { "context": "single gift card title", "string": "{selectedItemsCount,plural,one{Delete Gift Card} other{Delete Gift Cards}}" diff --git a/src/apps/components/AppPageTabs/AppPageTabs.tsx b/src/apps/components/AppPageTabs/AppPageTabs.tsx index 06e3dabe6..782936ddd 100644 --- a/src/apps/components/AppPageTabs/AppPageTabs.tsx +++ b/src/apps/components/AppPageTabs/AppPageTabs.tsx @@ -9,7 +9,6 @@ import { useIntl } from "react-intl"; const TabValue: Record = { SALEOR_APPS: "saleor-apps", THIRD_PARTY: "third-party", - WEBHOOKS_AND_EVENTS: "webhooks-and-events", }; type AllProps = ComponentProps; @@ -23,14 +22,6 @@ export const AppPageTabs = ({ showSaleorApps, ...props }: AvailableProps) => { const intl = useIntl(); return ( - "", onAppInProgressRemove: () => undefined, onAppInstallRetry: () => undefined, - onCustomAppRemove: () => undefined, onInstalledAppRemove: () => undefined, }; @@ -51,7 +48,6 @@ storiesOf("Views / Apps / Apps list", module) appsInProgressList={undefined} disabled={true} installedAppsList={undefined} - customAppsList={undefined} /> )) .add("no data", () => ( @@ -59,6 +55,5 @@ storiesOf("Views / Apps / Apps list", module) {...props} appsInProgressList={undefined} installedAppsList={[]} - customAppsList={[]} /> )); diff --git a/src/apps/components/AppsListPage/AppsListPage.tsx b/src/apps/components/AppsListPage/AppsListPage.tsx index 801fa2b41..d92fd0c8c 100644 --- a/src/apps/components/AppsListPage/AppsListPage.tsx +++ b/src/apps/components/AppsListPage/AppsListPage.tsx @@ -4,7 +4,7 @@ import { useSaleorApps } from "@saleor/apps/hooks/useSaleorApps"; import CardSpacer from "@saleor/components/CardSpacer"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import { AppsInstallationsQuery, AppsListQuery } from "@saleor/graphql"; +import { AppListItemFragment, AppsInstallationsQuery } from "@saleor/graphql"; import useNavigator from "@saleor/hooks/useNavigator"; import { sectionNames } from "@saleor/intl"; import { Button, makeStyles } from "@saleor/macaw-ui"; @@ -14,16 +14,12 @@ import React, { useEffect, useMemo } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import AppsInProgress from "../AppsInProgress/AppsInProgress"; -import CustomApps from "../CustomApps/CustomApps"; import InstalledApps from "../InstalledApps/InstalledApps"; export interface AppsListPageProps extends ListProps { - installedAppsList: AppsListQuery["apps"]["edges"]; - customAppsList: AppsListQuery["apps"]["edges"]; + installedAppsList: AppListItemFragment[]; appsInProgressList?: AppsInstallationsQuery; - getCustomAppHref: (id: string) => string; onInstalledAppRemove: (id: string) => void; - onCustomAppRemove: (id: string) => void; onAppInProgressRemove: (id: string) => void; onAppInstallRetry: (id: string) => void; } @@ -45,11 +41,8 @@ const useStyles = makeStyles( const AppsListPage: React.FC = ({ appsInProgressList, - customAppsList, installedAppsList, - getCustomAppHref, onInstalledAppRemove, - onCustomAppRemove, onAppInProgressRemove, onAppInstallRetry, ...listProps @@ -79,7 +72,7 @@ const AppsListPage: React.FC = ({ installedAppsList?.filter( app => !(fetchedSaleorApps ?? []).find(fetchedApp => - app.node.manifestUrl?.includes(fetchedApp.hostname), + app.manifestUrl?.includes(fetchedApp.hostname), ), ), [installedAppsList, fetchedSaleorApps], @@ -90,7 +83,7 @@ const AppsListPage: React.FC = ({ fetchedSaleorApps ?.map(app => installedAppsList?.find(installedApp => - installedApp.node.manifestUrl?.includes(app.hostname), + installedApp.manifestUrl?.includes(app.hostname), ), ) .filter(Boolean), @@ -135,24 +128,6 @@ const AppsListPage: React.FC = ({ ); } - case "webhooks-and-events": { - return ( - <> -

- -

- - - ); - } case "saleor-apps": { return ( <> diff --git a/src/apps/components/CustomApps/CustomApps.tsx b/src/apps/components/CustomApps/CustomApps.tsx deleted file mode 100644 index ce498710e..000000000 --- a/src/apps/components/CustomApps/CustomApps.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Card, TableBody, TableCell, Typography } from "@material-ui/core"; -import { customAppAddUrl } from "@saleor/apps/urls"; -import { Button } from "@saleor/components/Button"; -import CardTitle from "@saleor/components/CardTitle"; -import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper"; -import TableRowLink from "@saleor/components/TableRowLink"; -import { AppsListQuery } from "@saleor/graphql"; -import { commonMessages } from "@saleor/intl"; -import { DeleteIcon, IconButton, ResponsiveTable } from "@saleor/macaw-ui"; -import { renderCollection } from "@saleor/misc"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { useStyles } from "../../styles"; -import AppsSkeleton from "../AppsSkeleton"; -import DeactivatedText from "../DeactivatedText"; - -export interface CustomAppsProps { - appsList: AppsListQuery["apps"]["edges"]; - getCustomAppHref: (id: string) => string; - onRemove: (id: string) => void; -} - -const CustomApps: React.FC = ({ - appsList, - onRemove, - getCustomAppHref, -}) => { - const intl = useIntl(); - const classes = useStyles({}); - - return ( - - - - - } - title={intl.formatMessage(commonMessages.customApps)} - /> - - - {renderCollection( - appsList, - (app, index) => - app ? ( - - - - {app.node.name} - - {!app.node.isActive && ( -
- -
- )} -
- - - onRemove(app.node.id)} - > - - - - -
- ) : ( - - ), - () => ( - - - - - - - - ), - )} -
-
-
- ); -}; - -CustomApps.displayName = "CustomApps"; -export default CustomApps; diff --git a/src/apps/components/CustomApps/index.ts b/src/apps/components/CustomApps/index.ts deleted file mode 100644 index 4c43c395b..000000000 --- a/src/apps/components/CustomApps/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./CustomApps"; -export { default } from "./CustomApps"; diff --git a/src/apps/components/InstalledApps/InstalledApps.tsx b/src/apps/components/InstalledApps/InstalledApps.tsx index bd71a3e6d..6130a8e84 100644 --- a/src/apps/components/InstalledApps/InstalledApps.tsx +++ b/src/apps/components/InstalledApps/InstalledApps.tsx @@ -14,7 +14,7 @@ import CardTitle from "@saleor/components/CardTitle"; import { IconButton } from "@saleor/components/IconButton"; import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper"; import TableRowLink from "@saleor/components/TableRowLink"; -import { AppListItemFragment, AppsListQuery } from "@saleor/graphql"; +import { AppListItemFragment } from "@saleor/graphql"; import useNavigator from "@saleor/hooks/useNavigator"; import { DeleteIcon, ResponsiveTable } from "@saleor/macaw-ui"; import { renderCollection } from "@saleor/misc"; @@ -27,7 +27,7 @@ import { AppPermissions } from "../AppPermissions/AppPermissions"; import AppsSkeleton from "../AppsSkeleton"; export interface InstalledAppsProps extends ListProps { - appsList: AppsListQuery["apps"]["edges"]; + appsList: AppListItemFragment[]; onRemove: (id: string) => void; displayQuickManifestButton?: boolean; title: string; @@ -80,16 +80,15 @@ const InstalledApps: React.FC = ({ (app, index) => app ? ( - {app.node.name} + {app.name} - {app.node.manifestUrl && - isAppInTunnel(app.node.manifestUrl) ? ( + {app.manifestUrl && isAppInTunnel(app.manifestUrl) ? ( = ({ - {app.node.manifestUrl && ( - + {app.manifestUrl && ( + )} - + onRemove(app.node.id)} + onClick={() => onRemove(app.id)} > diff --git a/src/apps/fixtures.ts b/src/apps/fixtures.ts index 10f6c5d3b..19790a97c 100644 --- a/src/apps/fixtures.ts +++ b/src/apps/fixtures.ts @@ -1,83 +1,74 @@ import { AppFetchMutation, + AppListItemFragment, AppQuery, AppsInstallationsQuery, - AppsListQuery, AppTypeEnum, JobStatusEnum, PermissionEnum, } from "@saleor/graphql"; -export const appsList: AppsListQuery["apps"]["edges"] = [ +export const appsList: AppListItemFragment[] = [ { - __typename: "AppCountableEdge", - node: { - __typename: "App", - id: "QXBwOjE3Ng==", - isActive: true, - name: "app", - type: AppTypeEnum.THIRDPARTY, - appUrl: null, - manifestUrl: "http://localhost:3000/api/manifest", - permissions: [ - { - __typename: "Permission", - code: PermissionEnum.MANAGE_USERS, - name: "Manage customers.", - }, - ], - }, + __typename: "App", + id: "QXBwOjE3Ng==", + isActive: true, + name: "app", + type: AppTypeEnum.THIRDPARTY, + appUrl: null, + manifestUrl: "http://localhost:3000/api/manifest", + permissions: [ + { + __typename: "Permission", + code: PermissionEnum.MANAGE_USERS, + name: "Manage customers.", + }, + ], }, { - __typename: "AppCountableEdge", - node: { - __typename: "App", - id: "QXBwOjE3Ng==", - isActive: false, - name: "app1", - type: AppTypeEnum.THIRDPARTY, - appUrl: "http://localhost:3000", - manifestUrl: "http://localhost:3000/api/manifest", - permissions: [ - { - __typename: "Permission", - code: PermissionEnum.MANAGE_ORDERS, - name: "Manage orders.", - }, - { - __typename: "Permission", - code: PermissionEnum.MANAGE_USERS, - name: "Manage customers.", - }, - ], - }, + __typename: "App", + id: "QXBwOjE3Ng==", + isActive: false, + name: "app1", + type: AppTypeEnum.THIRDPARTY, + appUrl: "http://localhost:3000", + manifestUrl: "http://localhost:3000/api/manifest", + permissions: [ + { + __typename: "Permission", + code: PermissionEnum.MANAGE_ORDERS, + name: "Manage orders.", + }, + { + __typename: "Permission", + code: PermissionEnum.MANAGE_USERS, + name: "Manage customers.", + }, + ], }, ]; -export const customAppsList: AppsListQuery["apps"]["edges"] = [ +export const customAppsList: AppListItemFragment[] = [ { - __typename: "AppCountableEdge", - node: { - __typename: "App", - id: "QXBwOjE3Ng==", - isActive: true, - name: "app custom", - type: AppTypeEnum.LOCAL, - appUrl: null, - manifestUrl: null, - permissions: [ - { - __typename: "Permission", - code: PermissionEnum.MANAGE_ORDERS, - name: "Manage orders.", - }, - { - __typename: "Permission", - code: PermissionEnum.MANAGE_USERS, - name: "Manage customers.", - }, - ], - }, + __typename: "App", + id: "QXBwOjE3Ng==", + isActive: true, + name: "app custom", + type: AppTypeEnum.LOCAL, + appUrl: null, + manifestUrl: null, + permissions: [ + { + __typename: "Permission", + code: PermissionEnum.MANAGE_ORDERS, + name: "Manage orders.", + }, + { + __typename: "Permission", + code: PermissionEnum.MANAGE_USERS, + name: "Manage customers.", + }, + ], }, ]; diff --git a/src/apps/hooks/useAppsPageNavigation.ts b/src/apps/hooks/useAppsPageNavigation.ts index f4ca887f5..0f29f91cf 100644 --- a/src/apps/hooks/useAppsPageNavigation.ts +++ b/src/apps/hooks/useAppsPageNavigation.ts @@ -3,12 +3,9 @@ import useNavigator from "@saleor/hooks/useNavigator"; import { useCallback, useMemo } from "react"; import useRouter from "use-react-router"; -export type AppPagePathSegment = - | "webhooks-and-events" - | "third-party" - | "saleor-apps"; +export type AppPagePathSegment = "third-party" | "saleor-apps"; -const defaultTab: AppPagePathSegment = "webhooks-and-events"; +const defaultTab: AppPagePathSegment = "third-party"; const appTypeQueryParam = "type"; export const useAppsPageNavigation = () => { diff --git a/src/apps/index.tsx b/src/apps/index.tsx index 90a7539e3..e5414e0d6 100644 --- a/src/apps/index.tsx +++ b/src/apps/index.tsx @@ -1,5 +1,5 @@ +import WebhooksRoutes from "@saleor/custom-apps"; import { sectionNames } from "@saleor/intl"; -import WebhooksRoutes from "@saleor/webhooks"; import { parse as parseQs } from "qs"; import React from "react"; import { useIntl } from "react-intl"; @@ -14,16 +14,11 @@ import { AppListUrlQueryParams, appPath, appsListPath, - customAppAddPath, - customAppPath, - CustomAppUrlQueryParams, } from "./urls"; import AppView from "./views/App"; import AppDetailsView from "./views/AppDetails"; import AppInstallView from "./views/AppInstall"; import AppsListView from "./views/AppsList"; -import CustomAppCreateView from "./views/CustomAppCreate"; -import CustomAppDetailsView from "./views/CustomAppDetails"; const AppDetails: React.FC> = ({ match, @@ -47,34 +42,6 @@ const AppInstall: React.FC = props => { return ; }; -interface CustomAppDetailsProps extends RouteComponentProps<{ id?: string }> { - token: string; - onTokenClose: () => void; -} - -const CustomAppDetails: React.FC = ({ - match, - token, - onTokenClose, -}) => { - const qs = parseQs(location.search.substr(1)); - const params: CustomAppUrlQueryParams = qs; - const id = match.params.id; - - if (!id) { - throw new Error("No ID provided"); - } - - return ( - - ); -}; - const AppsList: React.FC = () => { const qs = parseQs(location.search.substr(1)); const params: AppListUrlQueryParams = qs; @@ -83,32 +50,15 @@ const AppsList: React.FC = () => { }; const Component = () => { const intl = useIntl(); - const [token, setToken] = React.useState(null); return ( <> - } - /> - ( - setToken(null)} - /> - )} - /> diff --git a/src/apps/urls.ts b/src/apps/urls.ts index a6de69f84..285a9fd94 100644 --- a/src/apps/urls.ts +++ b/src/apps/urls.ts @@ -9,7 +9,6 @@ export const MANIFEST_ATTR = "manifestUrl"; export type AppListUrlDialog = | "remove" | "remove-app" - | "remove-custom-app" | "app-activate" | "app-deactivate"; @@ -39,24 +38,13 @@ export enum AppListUrlSortField { active = "active", } -export type CustomAppUrlDialog = - | "create-token" - | "remove-webhook" - | "remove-token" - | "app-activate" - | "app-deactivate"; -export type CustomAppUrlQueryParams = Dialog & SingleAction; - export const appsSection = "/apps/"; export const appsListPath = appsSection; -export const customAppListPath = "/apps/custom/"; - export const appDetailsPath = (id: string) => urlJoin(appsSection, id); export const appPath = (id: string) => urlJoin(appsSection, id, "app"); export const appDeepPath = (id: string, subPath: string) => urlJoin(appPath(id), subPath); -export const customAppPath = (id: string) => urlJoin(customAppListPath, id); export const appInstallPath = urlJoin(appsSection, "install"); export const createAppInstallUrl = (manifestUrl: string) => `${appInstallPath}?manifestUrl=${manifestUrl}`; @@ -110,11 +98,6 @@ export const getDashboardUrFromAppCompleteUrl = ( return dashboardUrl; }; -export const customAppUrl = (id: string, params?: CustomAppUrlQueryParams) => - customAppPath(encodeURIComponent(id)) + "?" + stringifyQs(params); -export const customAppAddPath = urlJoin(customAppListPath, "add"); -export const customAppAddUrl = customAppAddPath; - export const appsListUrl = (params?: AppListUrlQueryParams) => appsListPath + "?" + stringifyQs(params); diff --git a/src/apps/views/AppsList/AppsList.tsx b/src/apps/views/AppsList/AppsList.tsx index 9ae4574cf..313b56621 100644 --- a/src/apps/views/AppsList/AppsList.tsx +++ b/src/apps/views/AppsList/AppsList.tsx @@ -4,7 +4,6 @@ import AppDeactivateDialog from "@saleor/apps/components/AppDeactivateDialog"; import { AppListContext, AppListContextValues } from "@saleor/apps/context"; import { AppsInstallationsQuery, - AppsListQuery, AppSortField, AppTypeEnum, JobStatusEnum, @@ -25,8 +24,10 @@ import usePaginator, { createPaginationState, PaginatorContext, } from "@saleor/hooks/usePaginator"; +import { findById } from "@saleor/misc"; import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { mapEdgesToItems } from "@saleor/utils/maps"; import React, { useEffect, useRef } from "react"; import { useIntl } from "react-intl"; @@ -38,15 +39,9 @@ import { AppListUrlDialog, AppListUrlQueryParams, appsListUrl, - customAppUrl, } from "../../urls"; import { messages } from "./messages"; -const getCurrentAppName = ( - id: string, - collection?: AppsListQuery["apps"]["edges"], -) => collection?.find(edge => edge.node.id === id)?.node?.name; - const getAppInProgressName = ( id: string, collection?: AppsInstallationsQuery["appsInstallations"], @@ -102,21 +97,6 @@ export const AppsList: React.FC = ({ params }) => { queryString: params, }); - const { - data: customAppsData, - loading: customAppsLoading, - refetch: customAppsRefetch, - } = useAppsListQuery({ - displayLoader: true, - variables: { - first: 100, - ...queryVariables, - filter: { - type: AppTypeEnum.LOCAL, - }, - }, - }); - const refetchExtensionList = () => { client.refetchQueries({ include: [EXTENSION_LIST_QUERY], @@ -173,11 +153,7 @@ export const AppsList: React.FC = ({ params }) => { const [deleteApp, deleteAppOpts] = useAppDeleteMutation({ onCompleted: data => { if (!data?.appDelete?.errors?.length) { - if (data.appDelete.app.type === AppTypeEnum.LOCAL) { - customAppsRefetch(); - } else { - refetch(); - } + refetch(); closeModal(); refetchExtensionList(); removeAppNotify(); @@ -277,9 +253,6 @@ export const AppsList: React.FC = ({ params }) => { const onAppInstallRetry = (id: string) => retryInstallApp({ variables: { id } }); - const installedApps = data?.apps?.edges; - const customApps = customAppsData?.apps?.edges; - const context: AppListContextValues = React.useMemo( () => ({ activateApp: id => openModal("app-activate", { id }), @@ -288,30 +261,30 @@ export const AppsList: React.FC = ({ params }) => { [activateApp, deactivateApp], ); + const installedApps = mapEdgesToItems(data?.apps); + const currentAppName = findById(params.id, installedApps)?.name; + return ( = ({ params }) => { /> customAppUrl(id)} onInstalledAppRemove={id => openModal("remove-app", { id, }) } - onCustomAppRemove={id => - openModal("remove-custom-app", { - id, - }) - } onAppInProgressRemove={id => openModal("remove", { id, diff --git a/src/channels/pages/ChannelDetailsPage/handlers.ts b/src/channels/pages/ChannelDetailsPage/handlers.ts index 7db87b286..8bdafc392 100644 --- a/src/channels/pages/ChannelDetailsPage/handlers.ts +++ b/src/channels/pages/ChannelDetailsPage/handlers.ts @@ -1,10 +1,7 @@ import { FormData } from "@saleor/channels/components/ChannelForm"; import { SearchData } from "@saleor/hooks/makeTopLevelSearch"; import { getParsedSearchData } from "@saleor/hooks/makeTopLevelSearch/utils"; -import { - getById, - getByUnmatchingId, -} from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById, getByUnmatchingId } from "@saleor/misc"; import { ReorderAction, ReorderEvent } from "@saleor/types"; import { move } from "@saleor/utils/lists"; diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 04179eeb4..6c3aefb82 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -1,7 +1,7 @@ import { useUser } from "@saleor/auth"; import { ChannelFragment, useBaseChannelsQuery } from "@saleor/graphql"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById } from "@saleor/misc"; import { useSaleorConfig } from "@saleor/sdk"; import React from "react"; diff --git a/src/components/AssignVariantDialog/utils.ts b/src/components/AssignVariantDialog/utils.ts index ab16904aa..efba00825 100644 --- a/src/components/AssignVariantDialog/utils.ts +++ b/src/components/AssignVariantDialog/utils.ts @@ -1,8 +1,5 @@ import { SearchProductsQuery } from "@saleor/graphql"; -import { - getById, - getByUnmatchingId, -} from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById, getByUnmatchingId } from "@saleor/misc"; import { RelayToFlat } from "@saleor/types"; export type SearchVariant = RelayToFlat< diff --git a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx index c9a5785ea..de51fd253 100644 --- a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx +++ b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx @@ -1,7 +1,7 @@ import { Card, CardContent, CircularProgress, Modal } from "@material-ui/core"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; +import { getById } from "@saleor/misc"; import ModalTitle from "@saleor/orders/components/OrderDiscountCommonModal/ModalTitle"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import React from "react"; import { useIntl } from "react-intl"; diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 11628adbe..c4b34cff2 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -3,10 +3,12 @@ import { useUser } from "@saleor/auth"; import { channelsListUrl } from "@saleor/channels/urls"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { APP_VERSION as dashboardVersion } from "@saleor/config"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; import { PermissionEnum } from "@saleor/graphql"; import useShop from "@saleor/hooks/useShop"; import Attributes from "@saleor/icons/Attributes"; import Channels from "@saleor/icons/Channels"; +import Miscellaneous from "@saleor/icons/Miscellaneous"; import Navigation from "@saleor/icons/Navigation"; import PageTypes from "@saleor/icons/PageTypes"; import PermissionGroups from "@saleor/icons/PermissionGroups"; @@ -235,6 +237,16 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { url: pluginListUrl(), testId: "configuration-plugins-pages", }, + { + description: intl.formatMessage({ + id: "Zz67wc", + defaultMessage: "View and update your webhooks and events.", + }), + icon: , + title: intl.formatMessage(sectionNames.webhooksAndEvents), + url: CustomAppUrls.resolveAppListUrl(), + testId: "configuration-menu-webhooks-and-events", + }, ], }, ]; diff --git a/src/apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx b/src/custom-apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx similarity index 94% rename from src/apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx rename to src/custom-apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx index ec4dd0ed3..2a6cbf314 100644 --- a/src/apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx +++ b/src/custom-apps/components/CustomAppCreatePage/CustomAppCreatePage.tsx @@ -1,4 +1,3 @@ -import { appsListUrl } from "@saleor/apps/urls"; import AccountPermissions from "@saleor/components/AccountPermissions"; import { Backlink } from "@saleor/components/Backlink"; import Container from "@saleor/components/Container"; @@ -6,6 +5,7 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import Savebar from "@saleor/components/Savebar"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; import { AppErrorFragment, PermissionEnum, @@ -60,7 +60,7 @@ const CustomAppCreatePage: React.FC = props => { > {({ data, change, submit, isSaveDisabled }) => ( - + {intl.formatMessage(sectionNames.apps)} = props => { navigate(appsListUrl())} + onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())} onSubmit={submit} /> diff --git a/src/apps/components/CustomAppCreatePage/index.ts b/src/custom-apps/components/CustomAppCreatePage/index.ts similarity index 100% rename from src/apps/components/CustomAppCreatePage/index.ts rename to src/custom-apps/components/CustomAppCreatePage/index.ts diff --git a/src/apps/components/CustomAppDefaultToken/CustomAppDefaultToken.tsx b/src/custom-apps/components/CustomAppDefaultToken/CustomAppDefaultToken.tsx similarity index 100% rename from src/apps/components/CustomAppDefaultToken/CustomAppDefaultToken.tsx rename to src/custom-apps/components/CustomAppDefaultToken/CustomAppDefaultToken.tsx diff --git a/src/apps/components/CustomAppDefaultToken/index.ts b/src/custom-apps/components/CustomAppDefaultToken/index.ts similarity index 100% rename from src/apps/components/CustomAppDefaultToken/index.ts rename to src/custom-apps/components/CustomAppDefaultToken/index.ts diff --git a/src/apps/components/CustomAppDefaultToken/styles.ts b/src/custom-apps/components/CustomAppDefaultToken/styles.ts similarity index 100% rename from src/apps/components/CustomAppDefaultToken/styles.ts rename to src/custom-apps/components/CustomAppDefaultToken/styles.ts diff --git a/src/apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx b/src/custom-apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx similarity index 94% rename from src/apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx rename to src/custom-apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx index 8f30ad841..d3de2b4a4 100644 --- a/src/apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx +++ b/src/custom-apps/components/CustomAppDetailsPage/CustomAppDetailsPage.tsx @@ -1,4 +1,3 @@ -import { appsListUrl } from "@saleor/apps/urls"; import AccountPermissions from "@saleor/components/AccountPermissions"; import { Backlink } from "@saleor/components/Backlink"; import CardSpacer from "@saleor/components/CardSpacer"; @@ -7,6 +6,8 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import Savebar from "@saleor/components/Savebar"; +import WebhooksList from "@saleor/custom-apps/components/WebhooksList"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; import { AppErrorFragment, AppUpdateMutation, @@ -19,15 +20,14 @@ import { sectionNames } from "@saleor/intl"; import { Button, ConfirmButtonTransitionState } from "@saleor/macaw-ui"; import { getFormErrors } from "@saleor/utils/errors"; import getAppErrorMessage from "@saleor/utils/errors/app"; -import WebhooksList from "@saleor/webhooks/components/WebhooksList"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import activateIcon from "../../../../assets/images/activate-icon.svg"; -import { useStyles } from "../../styles"; import CustomAppDefaultToken from "../CustomAppDefaultToken"; import CustomAppInformation from "../CustomAppInformation"; import CustomAppTokens from "../CustomAppTokens"; +import { useStyles } from "./styles"; export interface CustomAppDetailsPageFormData { hasFullAccess: boolean; @@ -76,7 +76,7 @@ const CustomAppDetailsPage: React.FC = props => { onAppDeactivateOpen, } = props; const intl = useIntl(); - const classes = useStyles({}); + const classes = useStyles(); const navigate = useNavigator(); const webhooks = app?.webhooks; @@ -105,7 +105,7 @@ const CustomAppDetailsPage: React.FC = props => { > {({ data, change, submit, isSaveDisabled }) => ( - + {intl.formatMessage(sectionNames.apps)} @@ -188,7 +188,7 @@ const CustomAppDetailsPage: React.FC = props => { navigate(appsListUrl())} + onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())} onSubmit={submit} /> diff --git a/src/apps/components/CustomAppDetailsPage/index.ts b/src/custom-apps/components/CustomAppDetailsPage/index.ts similarity index 100% rename from src/apps/components/CustomAppDetailsPage/index.ts rename to src/custom-apps/components/CustomAppDetailsPage/index.ts diff --git a/src/custom-apps/components/CustomAppDetailsPage/styles.ts b/src/custom-apps/components/CustomAppDetailsPage/styles.ts new file mode 100644 index 000000000..629d81719 --- /dev/null +++ b/src/custom-apps/components/CustomAppDetailsPage/styles.ts @@ -0,0 +1,12 @@ +import { makeStyles } from "@saleor/macaw-ui"; + +export const useStyles = makeStyles( + theme => ({ + activateButton: { + "& img": { + marginRight: theme.spacing(1), + }, + }, + }), + { name: "CustomAppDetailsPage" }, +); diff --git a/src/apps/components/CustomAppInformation/CustomAppInformation.tsx b/src/custom-apps/components/CustomAppInformation/CustomAppInformation.tsx similarity index 100% rename from src/apps/components/CustomAppInformation/CustomAppInformation.tsx rename to src/custom-apps/components/CustomAppInformation/CustomAppInformation.tsx diff --git a/src/apps/components/CustomAppInformation/index.ts b/src/custom-apps/components/CustomAppInformation/index.ts similarity index 100% rename from src/apps/components/CustomAppInformation/index.ts rename to src/custom-apps/components/CustomAppInformation/index.ts diff --git a/src/custom-apps/components/CustomAppListPage/CustomAppListPage.tsx b/src/custom-apps/components/CustomAppListPage/CustomAppListPage.tsx new file mode 100644 index 000000000..1810c1db2 --- /dev/null +++ b/src/custom-apps/components/CustomAppListPage/CustomAppListPage.tsx @@ -0,0 +1,119 @@ +import { Card, TableBody, TableCell, Typography } from "@material-ui/core"; +import { Button } from "@saleor/components/Button"; +import CardTitle from "@saleor/components/CardTitle"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper"; +import TableRowLink from "@saleor/components/TableRowLink"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; +import { AppListItemFragment } from "@saleor/graphql"; +import { commonMessages, sectionNames } from "@saleor/intl"; +import { DeleteIcon, IconButton, ResponsiveTable } from "@saleor/macaw-ui"; +import { renderCollection } from "@saleor/misc"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import AppsSkeleton from "../../../apps/components/AppsSkeleton"; +import DeactivatedText from "../../../apps/components/DeactivatedText"; +import { useStyles } from "../../../apps/styles"; + +export interface CustomAppListPageProps { + appsList: AppListItemFragment[]; + getCustomAppHref: (id: string) => string; + onRemove: (id: string) => void; +} + +const CustomAppListPage: React.FC = ({ + appsList, + onRemove, + getCustomAppHref, +}) => { + const intl = useIntl(); + const classes = useStyles({}); + + return ( + + +

+ +

+ + + + + } + title={intl.formatMessage(commonMessages.customApps)} + /> + + + {renderCollection( + appsList, + (app, index) => + app ? ( + + + + {app.name} + + {!app.isActive && ( +
+ +
+ )} +
+ + + onRemove(app.id)} + > + + + + +
+ ) : ( + + ), + () => ( + + + + + + + + ), + )} +
+
+
+
+ ); +}; + +CustomAppListPage.displayName = "CustomAppListPage"; +export default CustomAppListPage; diff --git a/src/custom-apps/components/CustomAppListPage/index.ts b/src/custom-apps/components/CustomAppListPage/index.ts new file mode 100644 index 000000000..ce807c855 --- /dev/null +++ b/src/custom-apps/components/CustomAppListPage/index.ts @@ -0,0 +1,2 @@ +export * from "./CustomAppListPage"; +export { default } from "./CustomAppListPage"; diff --git a/src/apps/components/CustomAppTokens/CustomAppTokens.tsx b/src/custom-apps/components/CustomAppTokens/CustomAppTokens.tsx similarity index 100% rename from src/apps/components/CustomAppTokens/CustomAppTokens.tsx rename to src/custom-apps/components/CustomAppTokens/CustomAppTokens.tsx diff --git a/src/apps/components/CustomAppTokens/index.ts b/src/custom-apps/components/CustomAppTokens/index.ts similarity index 100% rename from src/apps/components/CustomAppTokens/index.ts rename to src/custom-apps/components/CustomAppTokens/index.ts diff --git a/src/apps/components/CustomAppTokens/styles.ts b/src/custom-apps/components/CustomAppTokens/styles.ts similarity index 100% rename from src/apps/components/CustomAppTokens/styles.ts rename to src/custom-apps/components/CustomAppTokens/styles.ts diff --git a/src/apps/components/TokenCreateDialog/TokenCreateDialog.tsx b/src/custom-apps/components/TokenCreateDialog/TokenCreateDialog.tsx similarity index 100% rename from src/apps/components/TokenCreateDialog/TokenCreateDialog.tsx rename to src/custom-apps/components/TokenCreateDialog/TokenCreateDialog.tsx diff --git a/src/apps/components/TokenCreateDialog/index.ts b/src/custom-apps/components/TokenCreateDialog/index.ts similarity index 100% rename from src/apps/components/TokenCreateDialog/index.ts rename to src/custom-apps/components/TokenCreateDialog/index.ts diff --git a/src/apps/components/TokenCreateDialog/styles.ts b/src/custom-apps/components/TokenCreateDialog/styles.ts similarity index 100% rename from src/apps/components/TokenCreateDialog/styles.ts rename to src/custom-apps/components/TokenCreateDialog/styles.ts diff --git a/src/apps/components/TokenDeleteDialog/TokenDeleteDialog.stories.tsx b/src/custom-apps/components/TokenDeleteDialog/TokenDeleteDialog.stories.tsx similarity index 100% rename from src/apps/components/TokenDeleteDialog/TokenDeleteDialog.stories.tsx rename to src/custom-apps/components/TokenDeleteDialog/TokenDeleteDialog.stories.tsx diff --git a/src/apps/components/TokenDeleteDialog/TokenDeleteDialog.tsx b/src/custom-apps/components/TokenDeleteDialog/TokenDeleteDialog.tsx similarity index 100% rename from src/apps/components/TokenDeleteDialog/TokenDeleteDialog.tsx rename to src/custom-apps/components/TokenDeleteDialog/TokenDeleteDialog.tsx diff --git a/src/apps/components/TokenDeleteDialog/index.ts b/src/custom-apps/components/TokenDeleteDialog/index.ts similarity index 100% rename from src/apps/components/TokenDeleteDialog/index.ts rename to src/custom-apps/components/TokenDeleteDialog/index.ts diff --git a/src/webhooks/components/WebhookDeleteDialog/WebhookDeleteDialog.stories.tsx b/src/custom-apps/components/WebhookDeleteDialog/WebhookDeleteDialog.stories.tsx similarity index 100% rename from src/webhooks/components/WebhookDeleteDialog/WebhookDeleteDialog.stories.tsx rename to src/custom-apps/components/WebhookDeleteDialog/WebhookDeleteDialog.stories.tsx diff --git a/src/webhooks/components/WebhookDeleteDialog/WebhookDeleteDialog.tsx b/src/custom-apps/components/WebhookDeleteDialog/WebhookDeleteDialog.tsx similarity index 100% rename from src/webhooks/components/WebhookDeleteDialog/WebhookDeleteDialog.tsx rename to src/custom-apps/components/WebhookDeleteDialog/WebhookDeleteDialog.tsx diff --git a/src/webhooks/components/WebhookDeleteDialog/index.ts b/src/custom-apps/components/WebhookDeleteDialog/index.ts similarity index 100% rename from src/webhooks/components/WebhookDeleteDialog/index.ts rename to src/custom-apps/components/WebhookDeleteDialog/index.ts diff --git a/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.stories.tsx b/src/custom-apps/components/WebhookDetailsPage/WebhookDetailsPage.stories.tsx similarity index 100% rename from src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.stories.tsx rename to src/custom-apps/components/WebhookDetailsPage/WebhookDetailsPage.stories.tsx diff --git a/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx b/src/custom-apps/components/WebhookDetailsPage/WebhookDetailsPage.tsx similarity index 81% rename from src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx rename to src/custom-apps/components/WebhookDetailsPage/WebhookDetailsPage.tsx index 97809a50b..6041851cd 100644 --- a/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx +++ b/src/custom-apps/components/WebhookDetailsPage/WebhookDetailsPage.tsx @@ -1,4 +1,3 @@ -import { customAppUrl } from "@saleor/apps/urls"; import { Backlink } from "@saleor/components/Backlink"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; @@ -6,31 +5,33 @@ import FormSpacer from "@saleor/components/FormSpacer"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import Savebar from "@saleor/components/Savebar"; +import WebhookEvents from "@saleor/custom-apps/components/WebhookEvents"; +import WebhookInfo from "@saleor/custom-apps/components/WebhookInfo"; +import WebhookStatus from "@saleor/custom-apps/components/WebhookStatus"; +import { + createAsyncEventsSelectHandler, + createSyncEventsSelectHandler, +} from "@saleor/custom-apps/handlers"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; +import { + mapAsyncEventsToChoices, + mapSyncEventsToChoices, +} from "@saleor/custom-apps/utils"; import { WebhookDetailsFragment, WebhookErrorFragment, WebhookEventTypeAsyncEnum, WebhookEventTypeSyncEnum, } from "@saleor/graphql"; +import { SubmitPromise } from "@saleor/hooks/useForm"; import useNavigator from "@saleor/hooks/useNavigator"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; -import WebhookEvents from "@saleor/webhooks/components/WebhookEvents"; -import WebhookInfo from "@saleor/webhooks/components/WebhookInfo"; -import WebhookStatus from "@saleor/webhooks/components/WebhookStatus"; -import { - createAsyncEventsSelectHandler, - createSyncEventsSelectHandler, -} from "@saleor/webhooks/handlers"; -import { - mapAsyncEventsToChoices, - mapSyncEventsToChoices, -} from "@saleor/webhooks/utils"; import React from "react"; import { useIntl } from "react-intl"; import { getHeaderTitle } from "./messages"; -export interface FormData { +export interface WebhookFormData { syncEvents: WebhookEventTypeSyncEnum[]; asyncEvents: WebhookEventTypeAsyncEnum[]; isActive: boolean; @@ -40,18 +41,18 @@ export interface FormData { } export interface WebhookDetailsPageProps { - appName: string; appId: string; + appName: string; disabled: boolean; errors: WebhookErrorFragment[]; webhook?: WebhookDetailsFragment | null; saveButtonBarState: ConfirmButtonTransitionState; - onSubmit: (data: FormData) => void; + onSubmit: (data: WebhookFormData) => SubmitPromise; } const WebhookDetailsPage: React.FC = ({ - appName, appId, + appName, disabled, errors, webhook, @@ -61,17 +62,19 @@ const WebhookDetailsPage: React.FC = ({ const intl = useIntl(); const navigate = useNavigator(); - const initialForm: FormData = { + const initialForm: WebhookFormData = { syncEvents: webhook?.syncEvents?.map(event => event.eventType) || [], asyncEvents: webhook?.asyncEvents?.map(event => event.eventType) || [], - isActive: webhook?.isActive ?? true, + isActive: !!webhook?.isActive, name: webhook?.name || "", secretKey: webhook?.secretKey || "", targetUrl: webhook?.targetUrl || "", }; + const backUrl = CustomAppUrls.resolveAppUrl(appId); + return ( -
+ {({ data, submit, change }) => { const syncEventsChoices = disabled ? [] @@ -94,7 +97,7 @@ const WebhookDetailsPage: React.FC = ({ return ( - {appName} + {appName}
@@ -124,7 +127,7 @@ const WebhookDetailsPage: React.FC = ({ navigate(customAppUrl(appId))} + onCancel={() => navigate(backUrl)} onSubmit={submit} /> diff --git a/src/webhooks/components/WebhookDetailsPage/index.ts b/src/custom-apps/components/WebhookDetailsPage/index.ts similarity index 100% rename from src/webhooks/components/WebhookDetailsPage/index.ts rename to src/custom-apps/components/WebhookDetailsPage/index.ts diff --git a/src/webhooks/components/WebhooksDetailsPage/messages.ts b/src/custom-apps/components/WebhookDetailsPage/messages.ts similarity index 94% rename from src/webhooks/components/WebhooksDetailsPage/messages.ts rename to src/custom-apps/components/WebhookDetailsPage/messages.ts index a072ea60e..fdb486afd 100644 --- a/src/webhooks/components/WebhooksDetailsPage/messages.ts +++ b/src/custom-apps/components/WebhookDetailsPage/messages.ts @@ -1,6 +1,6 @@ +import { isUnnamed } from "@saleor/custom-apps/utils"; import { WebhookDetailsQuery } from "@saleor/graphql"; import { getStringOrPlaceholder } from "@saleor/misc"; -import { isUnnamed } from "@saleor/webhooks/utils"; import { defineMessages, IntlShape } from "react-intl"; export const messages = defineMessages({ diff --git a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx b/src/custom-apps/components/WebhookEvents/WebhookEvents.tsx similarity index 98% rename from src/webhooks/components/WebhookEvents/WebhookEvents.tsx rename to src/custom-apps/components/WebhookEvents/WebhookEvents.tsx index 83be02c97..1e4391b31 100644 --- a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx +++ b/src/custom-apps/components/WebhookEvents/WebhookEvents.tsx @@ -5,15 +5,15 @@ import Hr from "@saleor/components/Hr"; import MultiAutocompleteSelectField, { MultiAutocompleteChoiceType, } from "@saleor/components/MultiAutocompleteSelectField"; +import { + mapAsyncEventsToChoices, + mapSyncEventsToChoices, +} from "@saleor/custom-apps/utils"; import { WebhookEventTypeAsyncEnum, WebhookEventTypeSyncEnum, } from "@saleor/graphql"; import { ChangeEvent } from "@saleor/hooks/useForm"; -import { - mapAsyncEventsToChoices, - mapSyncEventsToChoices, -} from "@saleor/webhooks/utils"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; diff --git a/src/webhooks/components/WebhookEvents/index.ts b/src/custom-apps/components/WebhookEvents/index.ts similarity index 100% rename from src/webhooks/components/WebhookEvents/index.ts rename to src/custom-apps/components/WebhookEvents/index.ts diff --git a/src/webhooks/components/WebhookEvents/messages.ts b/src/custom-apps/components/WebhookEvents/messages.ts similarity index 100% rename from src/webhooks/components/WebhookEvents/messages.ts rename to src/custom-apps/components/WebhookEvents/messages.ts diff --git a/src/webhooks/components/WebhookInfo/WebhookInfo.tsx b/src/custom-apps/components/WebhookInfo/WebhookInfo.tsx similarity index 98% rename from src/webhooks/components/WebhookInfo/WebhookInfo.tsx rename to src/custom-apps/components/WebhookInfo/WebhookInfo.tsx index 62fd3565a..e57349dd8 100644 --- a/src/webhooks/components/WebhookInfo/WebhookInfo.tsx +++ b/src/custom-apps/components/WebhookInfo/WebhookInfo.tsx @@ -17,7 +17,7 @@ import getWebhookErrorMessage from "@saleor/utils/errors/webhooks"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { WebhookFormData } from "../WebhooksDetailsPage/WebhooksDetailsPage"; +import { WebhookFormData } from "../WebhookDetailsPage"; import { messages } from "./messages"; import { useStyles } from "./styles"; diff --git a/src/webhooks/components/WebhookInfo/index.ts b/src/custom-apps/components/WebhookInfo/index.ts similarity index 100% rename from src/webhooks/components/WebhookInfo/index.ts rename to src/custom-apps/components/WebhookInfo/index.ts diff --git a/src/webhooks/components/WebhookInfo/messages.ts b/src/custom-apps/components/WebhookInfo/messages.ts similarity index 100% rename from src/webhooks/components/WebhookInfo/messages.ts rename to src/custom-apps/components/WebhookInfo/messages.ts diff --git a/src/webhooks/components/WebhookInfo/styles.ts b/src/custom-apps/components/WebhookInfo/styles.ts similarity index 100% rename from src/webhooks/components/WebhookInfo/styles.ts rename to src/custom-apps/components/WebhookInfo/styles.ts diff --git a/src/webhooks/components/WebhookStatus/WebhookStatus.tsx b/src/custom-apps/components/WebhookStatus/WebhookStatus.tsx similarity index 91% rename from src/webhooks/components/WebhookStatus/WebhookStatus.tsx rename to src/custom-apps/components/WebhookStatus/WebhookStatus.tsx index bc0a58ba9..f9cf4b3c5 100644 --- a/src/webhooks/components/WebhookStatus/WebhookStatus.tsx +++ b/src/custom-apps/components/WebhookStatus/WebhookStatus.tsx @@ -5,7 +5,7 @@ import { ChangeEvent } from "@saleor/hooks/useForm"; import React from "react"; import { useIntl } from "react-intl"; -import { FormData } from "../WebhookDetailsPage"; +import { WebhookFormData } from "../WebhookDetailsPage"; import { messages } from "./messages"; interface WebhookStatusProps { @@ -28,7 +28,7 @@ const WebhookStatus: React.FC = ({ {intl.formatMessage(messages.webhookActiveDescription)} = ({ return ( = ({ = () => { + const qs = parseQs(location.search.substr(1)); + const params: CustomAppListUrlQueryParams = qs; + + return ; +}; + +interface CustomAppDetailsProps extends RouteComponentProps<{ id?: string }> { + token: string; + onTokenClose: () => void; +} + +const CustomAppDetails: React.FC = ({ + match, + token, + onTokenClose, +}) => { + const qs = parseQs(location.search.substr(1)); + const params: CustomAppDetailsUrlQueryParams = qs; + const id = match.params.id; + + if (!id) { + throw new Error("No ID provided"); + } + + return ( + + ); +}; + +const CustomAppWebhookCreate: React.FC> = ({ + match, +}) => { + const appId = match.params.appId; + + if (!appId) { + throw new Error("No App ID provided"); + } + + return ; +}; + +const CustomAppWebhookDetails: React.FC> = ({ + match, +}) => { + const id = match.params.id; + + if (!id) { + throw new Error("No ID provided"); + } + + return ; +}; + +const Component = () => { + const intl = useIntl(); + const [token, setToken] = React.useState(null); + + return ( + <> + + + + } + /> + ( + setToken(null)} + /> + )} + /> + + + + + ); +}; + +export default Component; diff --git a/src/custom-apps/messages.ts b/src/custom-apps/messages.ts new file mode 100644 index 000000000..888b9f878 --- /dev/null +++ b/src/custom-apps/messages.ts @@ -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", + }, +}); diff --git a/src/webhooks/mutations.ts b/src/custom-apps/mutations.ts similarity index 100% rename from src/webhooks/mutations.ts rename to src/custom-apps/mutations.ts diff --git a/src/webhooks/queries.ts b/src/custom-apps/queries.ts similarity index 100% rename from src/webhooks/queries.ts rename to src/custom-apps/queries.ts diff --git a/src/custom-apps/urls.ts b/src/custom-apps/urls.ts new file mode 100644 index 000000000..2d103308f --- /dev/null +++ b/src/custom-apps/urls.ts @@ -0,0 +1,70 @@ +import { stringifyQs } from "@saleor/utils/urls"; +import urlJoin from "url-join"; + +import { Dialog, SingleAction } from "../types"; + +export type CustomAppListUrlDialog = "remove-custom-app"; +export type CustomAppListUrlQueryParams = Dialog & + SingleAction; + +export type CustomAppDetailsUrlDialog = + | "create-token" + | "remove-webhook" + | "remove-token" + | "app-activate" + | "app-deactivate"; +export type CustomAppDetailsUrlQueryParams = Dialog & + SingleAction; + +export const CustomAppSections = { + appsSection: "/custom-apps/", + webhooksSection: "/webhooks/", +}; + +export const CustomAppPaths = { + resolveAppPath: (id: string) => urlJoin(CustomAppSections.appsSection, id), + appAddPath: urlJoin(CustomAppSections.appsSection, "add"), + appListPath: CustomAppSections.appsSection, + resolveWebhookPath: (appId: string, id: string) => + urlJoin( + CustomAppSections.appsSection, + appId, + CustomAppSections.webhooksSection, + id, + ), + resolveWebhookAddPath: (appId: string) => + urlJoin( + CustomAppSections.appsSection, + appId, + CustomAppSections.webhooksSection, + "add", + ), +}; + +export const CustomAppUrls = { + appAddUrl: CustomAppPaths.appAddPath, + resolveAppUrl: (id: string, params?: CustomAppDetailsUrlQueryParams) => + CustomAppPaths.resolveAppPath(encodeURIComponent(id)) + + "?" + + stringifyQs(params), + resolveAppListUrl: (params?: CustomAppListUrlQueryParams) => + CustomAppPaths.appListPath + "?" + stringifyQs(params), + resolveWebhookUrl: ( + appId: string, + id: string, + params?: CustomAppDetailsUrlQueryParams, + ) => + CustomAppPaths.resolveWebhookPath( + encodeURIComponent(appId), + encodeURIComponent(id), + ) + + "?" + + stringifyQs(params), + resolveWebhookAddUrl: ( + appId: string, + params?: CustomAppDetailsUrlQueryParams, + ) => + CustomAppPaths.resolveWebhookAddPath(encodeURIComponent(appId)) + + "?" + + stringifyQs(params), +}; diff --git a/src/custom-apps/utils.test.ts b/src/custom-apps/utils.test.ts new file mode 100644 index 000000000..56593ed6e --- /dev/null +++ b/src/custom-apps/utils.test.ts @@ -0,0 +1,121 @@ +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { WebhookEventTypeAsyncEnum } from "@saleor/graphql"; + +import { filterSelectedAsyncEvents, mapAsyncEventsToChoices } from "./utils"; + +describe("Custom Apps mapping events", () => { + it("should return enabled async events choices when not any event selected", () => { + // Arrange + const events: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.ANY_EVENTS, + WebhookEventTypeAsyncEnum.PAGE_CREATED, + WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + ]; + const selectedEvents: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.PAGE_CREATED, + ]; + + // Act + const asyncEvents = mapAsyncEventsToChoices(events, selectedEvents); + + // Assert + const expectedAsyncEvents: MultiAutocompleteChoiceType[] = [ + { + label: WebhookEventTypeAsyncEnum.ANY_EVENTS, + value: WebhookEventTypeAsyncEnum.ANY_EVENTS, + badge: undefined, + disabled: false, + }, + { + label: WebhookEventTypeAsyncEnum.PAGE_CREATED, + value: WebhookEventTypeAsyncEnum.PAGE_CREATED, + badge: undefined, + disabled: false, + }, + { + label: WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + value: WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + badge: undefined, + disabled: false, + }, + ]; + expect(asyncEvents).toHaveLength(3); + expect(asyncEvents).toEqual(expectedAsyncEvents); + }); + + it("should return disabled async events choices when any event selected", () => { + // Arrange + const events: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.ANY_EVENTS, + WebhookEventTypeAsyncEnum.PAGE_CREATED, + WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + ]; + const selectedEvents: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.ANY_EVENTS, + WebhookEventTypeAsyncEnum.PAGE_CREATED, + ]; + + // Act + const asyncEvents = mapAsyncEventsToChoices(events, selectedEvents); + + // Assert + const expectedAsyncEvents: MultiAutocompleteChoiceType[] = [ + { + label: WebhookEventTypeAsyncEnum.ANY_EVENTS, + value: WebhookEventTypeAsyncEnum.ANY_EVENTS, + badge: undefined, + disabled: false, + }, + { + label: WebhookEventTypeAsyncEnum.PAGE_CREATED, + value: WebhookEventTypeAsyncEnum.PAGE_CREATED, + badge: undefined, + disabled: true, + }, + { + label: WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + value: WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + badge: undefined, + disabled: true, + }, + ]; + expect(asyncEvents).toHaveLength(3); + expect(asyncEvents).toEqual(expectedAsyncEvents); + }); +}); + +describe("Custom Apps filtering events", () => { + it("should return selected async event types when not any event selected", () => { + // Arrange + const selectedEvents: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.PAGE_CREATED, + WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + ]; + + // Act + const asyncEvents = filterSelectedAsyncEvents(selectedEvents); + + // Assert + expect(asyncEvents).toHaveLength(2); + expect(asyncEvents).toEqual(selectedEvents); + }); + + it("should return only any async event type when any event selected", () => { + // Arrange + const selectedEvents: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.ANY_EVENTS, + WebhookEventTypeAsyncEnum.PAGE_CREATED, + WebhookEventTypeAsyncEnum.PRODUCT_CREATED, + ]; + + // Act + const asyncEvents = filterSelectedAsyncEvents(selectedEvents); + + // Assert + const expectedAsyncEvents: WebhookEventTypeAsyncEnum[] = [ + WebhookEventTypeAsyncEnum.ANY_EVENTS, + ]; + expect(asyncEvents).toHaveLength(1); + expect(asyncEvents).toEqual(expectedAsyncEvents); + }); +}); diff --git a/src/webhooks/utils.tsx b/src/custom-apps/utils.tsx similarity index 100% rename from src/webhooks/utils.tsx rename to src/custom-apps/utils.tsx diff --git a/src/apps/views/CustomAppCreate/CustomAppCreate.tsx b/src/custom-apps/views/CustomAppCreate/CustomAppCreate.tsx similarity index 94% rename from src/apps/views/CustomAppCreate/CustomAppCreate.tsx rename to src/custom-apps/views/CustomAppCreate/CustomAppCreate.tsx index 53a77f17e..42b64ed95 100644 --- a/src/apps/views/CustomAppCreate/CustomAppCreate.tsx +++ b/src/custom-apps/views/CustomAppCreate/CustomAppCreate.tsx @@ -1,4 +1,5 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; import { AppCreateMutation, useAppCreateMutation } from "@saleor/graphql"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; @@ -11,12 +12,12 @@ import { useIntl } from "react-intl"; import CustomAppCreatePage, { CustomAppCreatePageFormData, } from "../../components/CustomAppCreatePage"; -import { customAppUrl } from "../../urls"; import { messages } from "./messages"; interface CustomAppCreateProps { setToken: (token: string) => void; } + export const CustomAppCreate: React.FC = ({ setToken, }) => { @@ -31,7 +32,7 @@ export const CustomAppCreate: React.FC = ({ status: "success", text: intl.formatMessage(commonMessages.savedChanges), }); - navigate(customAppUrl(data.appCreate.app.id)); + navigate(CustomAppUrls.resolveAppUrl(data.appCreate.app.id)); setToken(data.appCreate.authToken); } }; diff --git a/src/apps/views/CustomAppCreate/index.ts b/src/custom-apps/views/CustomAppCreate/index.ts similarity index 100% rename from src/apps/views/CustomAppCreate/index.ts rename to src/custom-apps/views/CustomAppCreate/index.ts diff --git a/src/apps/views/CustomAppCreate/messages.ts b/src/custom-apps/views/CustomAppCreate/messages.ts similarity index 100% rename from src/apps/views/CustomAppCreate/messages.ts rename to src/custom-apps/views/CustomAppCreate/messages.ts diff --git a/src/apps/views/CustomAppDetails/CustomAppDetails.tsx b/src/custom-apps/views/CustomAppDetails/CustomAppDetails.tsx similarity index 91% rename from src/apps/views/CustomAppDetails/CustomAppDetails.tsx rename to src/custom-apps/views/CustomAppDetails/CustomAppDetails.tsx index 125253660..0783b8bf3 100644 --- a/src/apps/views/CustomAppDetails/CustomAppDetails.tsx +++ b/src/custom-apps/views/CustomAppDetails/CustomAppDetails.tsx @@ -1,11 +1,13 @@ import AppActivateDialog from "@saleor/apps/components/AppActivateDialog"; import AppDeactivateDialog from "@saleor/apps/components/AppDeactivateDialog"; -import TokenCreateDialog from "@saleor/apps/components/TokenCreateDialog"; -import TokenDeleteDialog from "@saleor/apps/components/TokenDeleteDialog"; import { appMessages } from "@saleor/apps/messages"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { getApiUrl } from "@saleor/config"; +import TokenCreateDialog from "@saleor/custom-apps/components/TokenCreateDialog"; +import TokenDeleteDialog from "@saleor/custom-apps/components/TokenDeleteDialog"; +import WebhookDeleteDialog from "@saleor/custom-apps/components/WebhookDeleteDialog"; +import { CustomAppUrls } from "@saleor/custom-apps/urls"; import { AppTokenCreateMutation, AppTokenDeleteMutation, @@ -26,8 +28,6 @@ import { commonMessages } from "@saleor/intl"; import { extractMutationErrors, getStringOrPlaceholder } from "@saleor/misc"; import getAppErrorMessage from "@saleor/utils/errors/app"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; -import WebhookDeleteDialog from "@saleor/webhooks/components/WebhookDeleteDialog"; -import { webhookAddPath } from "@saleor/webhooks/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -35,15 +35,13 @@ import CustomAppDetailsPage, { CustomAppDetailsPageFormData, } from "../../components/CustomAppDetailsPage"; import { - appsListUrl, - customAppUrl, - CustomAppUrlDialog, - CustomAppUrlQueryParams, + CustomAppDetailsUrlDialog, + CustomAppDetailsUrlQueryParams, } from "../../urls"; interface OrderListProps { id: string; - params: CustomAppUrlQueryParams; + params: CustomAppDetailsUrlQueryParams; token: string; onTokenClose: () => void; } @@ -62,9 +60,9 @@ export const CustomAppDetails: React.FC = ({ React.useEffect(() => onTokenClose, []); const [openModal, closeModal] = createDialogActionHandlers< - CustomAppUrlDialog, - CustomAppUrlQueryParams - >(navigate, params => customAppUrl(id, params), params); + CustomAppDetailsUrlDialog, + CustomAppDetailsUrlQueryParams + >(navigate, params => CustomAppUrls.resolveAppUrl(id, params), params); const { data, loading, refetch } = useAppQuery({ displayLoader: true, @@ -117,7 +115,7 @@ export const CustomAppDetails: React.FC = ({ status: "success", text: intl.formatMessage(commonMessages.savedChanges), }); - navigate(customAppUrl(id)); + navigate(CustomAppUrls.resolveAppUrl(id)); closeModal(); refetch(); } @@ -213,7 +211,7 @@ export const CustomAppDetails: React.FC = ({ const currentToken = data?.app?.tokens?.find(token => token.id === params.id); if (customApp === null) { - return ; + return ; } return ( @@ -233,7 +231,7 @@ export const CustomAppDetails: React.FC = ({ id, }) } - webhookCreateHref={webhookAddPath(id)} + webhookCreateHref={CustomAppUrls.resolveWebhookAddUrl(id)} onWebhookRemove={id => openModal("remove-webhook", { id, diff --git a/src/apps/views/CustomAppDetails/index.ts b/src/custom-apps/views/CustomAppDetails/index.ts similarity index 100% rename from src/apps/views/CustomAppDetails/index.ts rename to src/custom-apps/views/CustomAppDetails/index.ts diff --git a/src/custom-apps/views/CustomAppList.tsx b/src/custom-apps/views/CustomAppList.tsx new file mode 100644 index 000000000..1b73793cb --- /dev/null +++ b/src/custom-apps/views/CustomAppList.tsx @@ -0,0 +1,123 @@ +import { useApolloClient } from "@apollo/client"; +import AppDeleteDialog from "@saleor/apps/components/AppDeleteDialog"; +import { EXTENSION_LIST_QUERY } from "@saleor/apps/queries"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { + AppSortField, + AppTypeEnum, + OrderDirection, + useAppDeleteMutation, + useAppsListQuery, +} from "@saleor/graphql"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { sectionNames } from "@saleor/intl"; +import { findById } from "@saleor/misc"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { mapEdgesToItems } from "@saleor/utils/maps"; +import React from "react"; +import { useIntl } from "react-intl"; + +import CustomAppListPage from "../components/CustomAppListPage"; +import { messages } from "../messages"; +import { + CustomAppListUrlDialog, + CustomAppListUrlQueryParams, + CustomAppUrls, +} from "../urls"; + +interface CustomAppListProps { + params: CustomAppListUrlQueryParams; +} + +export const CustomAppList: React.FC = ({ params }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + const client = useApolloClient(); + + const [openModal, closeModal] = createDialogActionHandlers< + CustomAppListUrlDialog, + CustomAppListUrlQueryParams + >(navigate, CustomAppUrls.resolveAppListUrl, params); + + const removeAppNotify = () => { + notify({ + status: "success", + text: intl.formatMessage(messages.appRemoved), + }); + }; + + const refetchExtensionList = () => { + client.refetchQueries({ + include: [EXTENSION_LIST_QUERY], + }); + }; + + const queryVariables = { + sort: { + direction: OrderDirection.DESC, + field: AppSortField.CREATION_DATE, + }, + }; + + const { data: customAppsData, refetch: customAppsRefetch } = useAppsListQuery( + { + displayLoader: true, + variables: { + first: 100, + ...queryVariables, + filter: { + type: AppTypeEnum.LOCAL, + }, + }, + }, + ); + + const [deleteApp, deleteAppOpts] = useAppDeleteMutation({ + onCompleted: data => { + if (!data?.appDelete?.errors?.length) { + customAppsRefetch(); + closeModal(); + refetchExtensionList(); + removeAppNotify(); + } + }, + }); + + const handleRemoveConfirm = () => + deleteApp({ + variables: { + id: params.id, + }, + }); + + const customApps = mapEdgesToItems(customAppsData?.apps); + const currentAppName = findById(params.id, customApps)?.name; + + return ( + <> + + + CustomAppUrls.resolveAppUrl(id)} + onRemove={id => + openModal("remove-custom-app", { + id, + }) + } + /> + + ); +}; + +CustomAppList.displayName = "CustomAppList"; +export default CustomAppList; diff --git a/src/webhooks/views/WebhooksCreate.tsx b/src/custom-apps/views/CustomAppWebhookCreate.tsx similarity index 75% rename from src/webhooks/views/WebhooksCreate.tsx rename to src/custom-apps/views/CustomAppWebhookCreate.tsx index 7d4de0a2c..c660ada75 100644 --- a/src/webhooks/views/WebhooksCreate.tsx +++ b/src/custom-apps/views/CustomAppWebhookCreate.tsx @@ -11,19 +11,23 @@ import { extractMutationErrors } from "@saleor/misc"; import React from "react"; import { useIntl } from "react-intl"; -import WebhookDetailsPage, { FormData } from "../components/WebhookDetailsPage"; -import { webhookUrl } from "../urls"; +import WebhookDetailsPage, { + WebhookFormData, +} from "../components/WebhookDetailsPage"; +import { CustomAppUrls } from "../urls"; -export interface WebhooksCreateProps { - id: string; +export interface CustomAppWebhookCreateProps { + appId: string; } -export const WebhooksCreate: React.FC = ({ id }) => { +export const CustomAppWebhookCreate: React.FC = ({ + appId, +}) => { const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); - const { data } = useAppQuery({ variables: { id } }); + const { data } = useAppQuery({ variables: { id: appId } }); const [webhookCreate, webhookCreateOpts] = useWebhookCreateMutation({ onCompleted: data => { @@ -33,17 +37,17 @@ export const WebhooksCreate: React.FC = ({ id }) => { status: "success", text: intl.formatMessage(commonMessages.savedChanges), }); - navigate(webhookUrl(webhook.id)); + navigate(CustomAppUrls.resolveWebhookUrl(appId, webhook.id)); } }, }); - const handleSubmit = (data: FormData) => + const handleSubmit = (data: WebhookFormData) => extractMutationErrors( webhookCreate({ variables: { input: { - app: id, + app: appId, syncEvents: data.syncEvents, asyncEvents: data.asyncEvents.includes( WebhookEventTypeAsyncEnum.ANY_EVENTS, @@ -69,8 +73,8 @@ export const WebhooksCreate: React.FC = ({ id }) => { })} /> = ({ id }) => { ); }; -WebhooksCreate.displayName = "WebhooksCreate"; -export default WebhooksCreate; +CustomAppWebhookCreate.displayName = "CustomAppWebhookCreate"; +export default CustomAppWebhookCreate; diff --git a/src/webhooks/views/WebhooksDetails.tsx b/src/custom-apps/views/CustomAppWebhookDetails.tsx similarity index 82% rename from src/webhooks/views/WebhooksDetails.tsx rename to src/custom-apps/views/CustomAppWebhookDetails.tsx index 655beba72..7c66a19ca 100644 --- a/src/webhooks/views/WebhooksDetails.tsx +++ b/src/custom-apps/views/CustomAppWebhookDetails.tsx @@ -1,4 +1,3 @@ -import { appsListUrl } from "@saleor/apps/urls"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { @@ -12,14 +11,18 @@ import React from "react"; import { useIntl } from "react-intl"; import { extractMutationErrors, getStringOrPlaceholder } from "../../misc"; -import WebhookDetailsPage from "../components/WebhookDetailsPage"; -import { WebhookFormData } from "../components/WebhooksDetailsPage/WebhooksDetailsPage"; +import WebhookDetailsPage, { + WebhookFormData, +} from "../components/WebhookDetailsPage"; +import { CustomAppUrls } from "../urls"; -export interface WebhooksDetailsProps { +export interface CustomAppWebhookDetailsProps { id: string; } -export const WebhooksDetails: React.FC = ({ id }) => { +export const CustomAppWebhookDetails: React.FC = ({ + id, +}) => { const notify = useNotifier(); const intl = useIntl(); @@ -64,7 +67,7 @@ export const WebhooksDetails: React.FC = ({ id }) => { }), ); if (!webhook && !loading) { - return ; + return ; } return ( @@ -85,5 +88,5 @@ export const WebhooksDetails: React.FC = ({ id }) => { ); }; -WebhooksDetails.displayName = "WebhooksDetails"; -export default WebhooksDetails; +CustomAppWebhookDetails.displayName = "CustomAppWebhookDetails"; +export default CustomAppWebhookDetails; diff --git a/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx b/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx index 4bc858077..1e4b3cfa6 100644 --- a/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx +++ b/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx @@ -10,7 +10,7 @@ import DeleteWarningDialogConsentContent from "@saleor/components/TypeDeleteWarn import { GiftCardsListConsumerProps } from "@saleor/giftCards/GiftCardsList/providers/GiftCardListProvider"; import { ExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import { GiftCardDataFragment } from "@saleor/graphql"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById } from "@saleor/misc"; import React, { useEffect, useState } from "react"; import { useIntl } from "react-intl"; diff --git a/src/graphql/hooks.generated.ts b/src/graphql/hooks.generated.ts index 6c326b03c..152be51b7 100644 --- a/src/graphql/hooks.generated.ts +++ b/src/graphql/hooks.generated.ts @@ -5341,6 +5341,155 @@ export function useCheckOrderInvoicesStatusLazyQuery(baseOptions?: ApolloReactHo export type CheckOrderInvoicesStatusQueryHookResult = ReturnType; export type CheckOrderInvoicesStatusLazyQueryHookResult = ReturnType; export type CheckOrderInvoicesStatusQueryResult = Apollo.QueryResult; +export const WebhookCreateDocument = gql` + mutation WebhookCreate($input: WebhookCreateInput!) { + webhookCreate(input: $input) { + errors { + ...WebhookError + } + webhook { + ...WebhookDetails + } + } +} + ${WebhookErrorFragmentDoc} +${WebhookDetailsFragmentDoc}`; +export type WebhookCreateMutationFn = Apollo.MutationFunction; + +/** + * __useWebhookCreateMutation__ + * + * To run a mutation, you first call `useWebhookCreateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useWebhookCreateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [webhookCreateMutation, { data, loading, error }] = useWebhookCreateMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useWebhookCreateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(WebhookCreateDocument, options); + } +export type WebhookCreateMutationHookResult = ReturnType; +export type WebhookCreateMutationResult = Apollo.MutationResult; +export type WebhookCreateMutationOptions = Apollo.BaseMutationOptions; +export const WebhookUpdateDocument = gql` + mutation WebhookUpdate($id: ID!, $input: WebhookUpdateInput!) { + webhookUpdate(id: $id, input: $input) { + errors { + ...WebhookError + } + webhook { + ...WebhookDetails + } + } +} + ${WebhookErrorFragmentDoc} +${WebhookDetailsFragmentDoc}`; +export type WebhookUpdateMutationFn = Apollo.MutationFunction; + +/** + * __useWebhookUpdateMutation__ + * + * To run a mutation, you first call `useWebhookUpdateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useWebhookUpdateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [webhookUpdateMutation, { data, loading, error }] = useWebhookUpdateMutation({ + * variables: { + * id: // value for 'id' + * input: // value for 'input' + * }, + * }); + */ +export function useWebhookUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(WebhookUpdateDocument, options); + } +export type WebhookUpdateMutationHookResult = ReturnType; +export type WebhookUpdateMutationResult = Apollo.MutationResult; +export type WebhookUpdateMutationOptions = Apollo.BaseMutationOptions; +export const WebhookDeleteDocument = gql` + mutation WebhookDelete($id: ID!) { + webhookDelete(id: $id) { + errors { + ...WebhookError + } + } +} + ${WebhookErrorFragmentDoc}`; +export type WebhookDeleteMutationFn = Apollo.MutationFunction; + +/** + * __useWebhookDeleteMutation__ + * + * To run a mutation, you first call `useWebhookDeleteMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useWebhookDeleteMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [webhookDeleteMutation, { data, loading, error }] = useWebhookDeleteMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useWebhookDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(WebhookDeleteDocument, options); + } +export type WebhookDeleteMutationHookResult = ReturnType; +export type WebhookDeleteMutationResult = Apollo.MutationResult; +export type WebhookDeleteMutationOptions = Apollo.BaseMutationOptions; +export const WebhookDetailsDocument = gql` + query WebhookDetails($id: ID!) { + webhook(id: $id) { + ...WebhookDetails + } +} + ${WebhookDetailsFragmentDoc}`; + +/** + * __useWebhookDetailsQuery__ + * + * To run a query within a React component, call `useWebhookDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useWebhookDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useWebhookDetailsQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useWebhookDetailsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(WebhookDetailsDocument, options); + } +export function useWebhookDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(WebhookDetailsDocument, options); + } +export type WebhookDetailsQueryHookResult = ReturnType; +export type WebhookDetailsLazyQueryHookResult = ReturnType; +export type WebhookDetailsQueryResult = Apollo.QueryResult; export const UpdateCustomerDocument = gql` mutation UpdateCustomer($id: ID!, $input: CustomerInput!) { customerUpdate(id: $id, input: $input) { @@ -16970,153 +17119,4 @@ export function useWarehousesCountLazyQuery(baseOptions?: ApolloReactHooks.LazyQ } export type WarehousesCountQueryHookResult = ReturnType; export type WarehousesCountLazyQueryHookResult = ReturnType; -export type WarehousesCountQueryResult = Apollo.QueryResult; -export const WebhookCreateDocument = gql` - mutation WebhookCreate($input: WebhookCreateInput!) { - webhookCreate(input: $input) { - errors { - ...WebhookError - } - webhook { - ...WebhookDetails - } - } -} - ${WebhookErrorFragmentDoc} -${WebhookDetailsFragmentDoc}`; -export type WebhookCreateMutationFn = Apollo.MutationFunction; - -/** - * __useWebhookCreateMutation__ - * - * To run a mutation, you first call `useWebhookCreateMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useWebhookCreateMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [webhookCreateMutation, { data, loading, error }] = useWebhookCreateMutation({ - * variables: { - * input: // value for 'input' - * }, - * }); - */ -export function useWebhookCreateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(WebhookCreateDocument, options); - } -export type WebhookCreateMutationHookResult = ReturnType; -export type WebhookCreateMutationResult = Apollo.MutationResult; -export type WebhookCreateMutationOptions = Apollo.BaseMutationOptions; -export const WebhookUpdateDocument = gql` - mutation WebhookUpdate($id: ID!, $input: WebhookUpdateInput!) { - webhookUpdate(id: $id, input: $input) { - errors { - ...WebhookError - } - webhook { - ...WebhookDetails - } - } -} - ${WebhookErrorFragmentDoc} -${WebhookDetailsFragmentDoc}`; -export type WebhookUpdateMutationFn = Apollo.MutationFunction; - -/** - * __useWebhookUpdateMutation__ - * - * To run a mutation, you first call `useWebhookUpdateMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useWebhookUpdateMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [webhookUpdateMutation, { data, loading, error }] = useWebhookUpdateMutation({ - * variables: { - * id: // value for 'id' - * input: // value for 'input' - * }, - * }); - */ -export function useWebhookUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(WebhookUpdateDocument, options); - } -export type WebhookUpdateMutationHookResult = ReturnType; -export type WebhookUpdateMutationResult = Apollo.MutationResult; -export type WebhookUpdateMutationOptions = Apollo.BaseMutationOptions; -export const WebhookDeleteDocument = gql` - mutation WebhookDelete($id: ID!) { - webhookDelete(id: $id) { - errors { - ...WebhookError - } - } -} - ${WebhookErrorFragmentDoc}`; -export type WebhookDeleteMutationFn = Apollo.MutationFunction; - -/** - * __useWebhookDeleteMutation__ - * - * To run a mutation, you first call `useWebhookDeleteMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useWebhookDeleteMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [webhookDeleteMutation, { data, loading, error }] = useWebhookDeleteMutation({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useWebhookDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(WebhookDeleteDocument, options); - } -export type WebhookDeleteMutationHookResult = ReturnType; -export type WebhookDeleteMutationResult = Apollo.MutationResult; -export type WebhookDeleteMutationOptions = Apollo.BaseMutationOptions; -export const WebhookDetailsDocument = gql` - query WebhookDetails($id: ID!) { - webhook(id: $id) { - ...WebhookDetails - } -} - ${WebhookDetailsFragmentDoc}`; - -/** - * __useWebhookDetailsQuery__ - * - * To run a query within a React component, call `useWebhookDetailsQuery` and pass it any options that fit your needs. - * When your component renders, `useWebhookDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useWebhookDetailsQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useWebhookDetailsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useQuery(WebhookDetailsDocument, options); - } -export function useWebhookDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useLazyQuery(WebhookDetailsDocument, options); - } -export type WebhookDetailsQueryHookResult = ReturnType; -export type WebhookDetailsLazyQueryHookResult = ReturnType; -export type WebhookDetailsQueryResult = Apollo.QueryResult; \ No newline at end of file +export type WarehousesCountQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/src/graphql/types.generated.ts b/src/graphql/types.generated.ts index 78f91d933..de219f0f7 100644 --- a/src/graphql/types.generated.ts +++ b/src/graphql/types.generated.ts @@ -6722,6 +6722,35 @@ export type CheckOrderInvoicesStatusQueryVariables = Exact<{ export type CheckOrderInvoicesStatusQuery = { __typename: 'Query', order: { __typename: 'Order', id: string, invoices: Array<{ __typename: 'Invoice', id: string, number: string | null, createdAt: any, url: string | null, status: JobStatusEnum }> } | null }; +export type WebhookCreateMutationVariables = Exact<{ + input: WebhookCreateInput; +}>; + + +export type WebhookCreateMutation = { __typename: 'Mutation', webhookCreate: { __typename: 'WebhookCreate', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }>, webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null } | null }; + +export type WebhookUpdateMutationVariables = Exact<{ + id: Scalars['ID']; + input: WebhookUpdateInput; +}>; + + +export type WebhookUpdateMutation = { __typename: 'Mutation', webhookUpdate: { __typename: 'WebhookUpdate', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }>, webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null } | null }; + +export type WebhookDeleteMutationVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type WebhookDeleteMutation = { __typename: 'Mutation', webhookDelete: { __typename: 'WebhookDelete', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }> } | null }; + +export type WebhookDetailsQueryVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type WebhookDetailsQuery = { __typename: 'Query', webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null }; + export type UpdateCustomerMutationVariables = Exact<{ id: Scalars['ID']; input: CustomerInput; @@ -9404,32 +9433,3 @@ export type WarehousesCountQueryVariables = Exact<{ [key: string]: never; }>; export type WarehousesCountQuery = { __typename: 'Query', warehouses: { __typename: 'WarehouseCountableConnection', totalCount: number | null } | null }; - -export type WebhookCreateMutationVariables = Exact<{ - input: WebhookCreateInput; -}>; - - -export type WebhookCreateMutation = { __typename: 'Mutation', webhookCreate: { __typename: 'WebhookCreate', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }>, webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null } | null }; - -export type WebhookUpdateMutationVariables = Exact<{ - id: Scalars['ID']; - input: WebhookUpdateInput; -}>; - - -export type WebhookUpdateMutation = { __typename: 'Mutation', webhookUpdate: { __typename: 'WebhookUpdate', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }>, webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null } | null }; - -export type WebhookDeleteMutationVariables = Exact<{ - id: Scalars['ID']; -}>; - - -export type WebhookDeleteMutation = { __typename: 'Mutation', webhookDelete: { __typename: 'WebhookDelete', errors: Array<{ __typename: 'WebhookError', code: WebhookErrorCode, field: string | null, message: string | null }> } | null }; - -export type WebhookDetailsQueryVariables = Exact<{ - id: Scalars['ID']; -}>; - - -export type WebhookDetailsQuery = { __typename: 'Query', webhook: { __typename: 'Webhook', secretKey: string | null, targetUrl: string, id: string, name: string, isActive: boolean, syncEvents: Array<{ __typename: 'WebhookEventSync', eventType: WebhookEventTypeSyncEnum }>, asyncEvents: Array<{ __typename: 'WebhookEventAsync', eventType: WebhookEventTypeAsyncEnum }>, app: { __typename: 'App', id: string, name: string | null } } | null }; diff --git a/src/icons/Miscellaneous.tsx b/src/icons/Miscellaneous.tsx new file mode 100644 index 000000000..b2eaf2ec1 --- /dev/null +++ b/src/icons/Miscellaneous.tsx @@ -0,0 +1,36 @@ +import { createSvgIcon, SvgIconProps } from "@material-ui/core"; +import React from "react"; + +const Miscellaneous = createSvgIcon( + <> + + + + + , + "Miscellaneous", +); + +export default (props: SvgIconProps) => ( + +); diff --git a/src/index.tsx b/src/index.tsx index c047b2a8e..50243094e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -42,6 +42,8 @@ import { getConfigMenuItemsPermissions } from "./configuration/utils"; import AppStateProvider from "./containers/AppState"; import BackgroundTasksProvider from "./containers/BackgroundTasks"; import ServiceWorker from "./containers/ServiceWorker/ServiceWorker"; +import CustomAppsSection from "./custom-apps"; +import { CustomAppSections } from "./custom-apps/urls"; import { CustomerSection } from "./customers"; import DiscountSection from "./discounts"; import GiftCardSection from "./giftCards"; @@ -281,6 +283,10 @@ const Routes: React.FC = () => { path="/configuration" component={ConfigurationSection} /> + diff --git a/src/intl.ts b/src/intl.ts index 5288786a9..9fe13eeb9 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -501,10 +501,10 @@ export const sectionNames = defineMessages({ defaultMessage: "Warehouses", description: "warehouses section name", }, - webhooks: { - id: "6nSTuC", - defaultMessage: "Webhooks", - description: "webhooks section name", + webhooksAndEvents: { + id: "BFR6CF", + defaultMessage: "Webhooks & Events", + description: "webhooks and events section name", }, }); diff --git a/src/misc.ts b/src/misc.ts index bb1165d23..7e4ae0713 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -7,6 +7,7 @@ import { PaymentChargeStatusEnum, } from "@saleor/graphql"; import { ConfirmButtonTransitionState, ThemeType } from "@saleor/macaw-ui"; +import { Node, SlugNode } from "@saleor/types"; import uniqBy from "lodash/uniqBy"; import moment from "moment-timezone"; import { IntlShape } from "react-intl"; @@ -541,5 +542,15 @@ export const isInDevelopment = export type WithOptional = Omit & Partial>; -export const getBySlug = (slugToCompare: string) => (obj: { slug: string }) => +export const getBySlug = (slugToCompare: string) => (obj: SlugNode) => obj.slug === slugToCompare; + +export const getById = (idToCompare: string) => (obj: Node) => + obj.id === idToCompare; + +export const getByUnmatchingId = (idToCompare: string) => (obj: { + id: string; +}) => obj.id !== idToCompare; + +export const findById = (id: string, list?: T[]) => + list?.find(getById(id)); diff --git a/src/navigation/views/MenuList/MenuList.tsx b/src/navigation/views/MenuList/MenuList.tsx index 0efc75a6c..c73a0b036 100644 --- a/src/navigation/views/MenuList/MenuList.tsx +++ b/src/navigation/views/MenuList/MenuList.tsx @@ -17,8 +17,7 @@ import usePaginator, { PaginatorContext, } from "@saleor/hooks/usePaginator"; import { buttonMessages, commonMessages } from "@saleor/intl"; -import { getStringOrPlaceholder, maybe } from "@saleor/misc"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById, getStringOrPlaceholder, maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; import { mapEdgesToItems } from "@saleor/utils/maps"; diff --git a/src/orders/components/OrderChangeWarehouseDialog/OrderChangeWarehouseDialog.tsx b/src/orders/components/OrderChangeWarehouseDialog/OrderChangeWarehouseDialog.tsx index b7d20b76c..628be5f71 100644 --- a/src/orders/components/OrderChangeWarehouseDialog/OrderChangeWarehouseDialog.tsx +++ b/src/orders/components/OrderChangeWarehouseDialog/OrderChangeWarehouseDialog.tsx @@ -25,13 +25,13 @@ import { SearchIcon, useElementScroll, } from "@saleor/macaw-ui"; +import { getById } from "@saleor/misc"; import { getLineAvailableQuantityInWarehouse } from "@saleor/orders/utils/data"; import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; import { mapEdgesToItems } from "@saleor/utils/maps"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { getById } from "../OrderReturnPage/utils"; import { changeWarehouseDialogMessages as messages } from "./messages"; import { useStyles } from "./styles"; diff --git a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressEdit.tsx b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressEdit.tsx index 1abb754e2..baa31a0e4 100644 --- a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressEdit.tsx +++ b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressEdit.tsx @@ -12,10 +12,10 @@ import { OrderErrorFragment, } from "@saleor/graphql"; import { FormChange } from "@saleor/hooks/useForm"; +import { getById } from "@saleor/misc"; import React from "react"; import { useIntl } from "react-intl"; -import { getById } from "../OrderReturnPage/utils"; import { AddressInputOptionEnum } from "./form"; import { addressEditMessages } from "./messages"; import { useStyles } from "./styles"; diff --git a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesEditDialog.tsx b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesEditDialog.tsx index 686510564..0705b93a0 100644 --- a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesEditDialog.tsx +++ b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesEditDialog.tsx @@ -24,12 +24,11 @@ import { SubmitPromise } from "@saleor/hooks/useForm"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import { buttonMessages } from "@saleor/intl"; import { ConfirmButtonTransitionState, DialogHeader } from "@saleor/macaw-ui"; -import { transformAddressToAddressInput } from "@saleor/misc"; +import { getById, transformAddressToAddressInput } from "@saleor/misc"; import { mapCountriesToChoices } from "@saleor/utils/maps"; import React from "react"; import { FormattedMessage, MessageDescriptor, useIntl } from "react-intl"; -import { getById } from "../OrderReturnPage/utils"; import OrderCustomerAddressesEditForm, { AddressInputOptionEnum, OrderCustomerAddressesEditFormData, diff --git a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesSearch.tsx b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesSearch.tsx index 2fc6df805..e6d8e0fe4 100644 --- a/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesSearch.tsx +++ b/src/orders/components/OrderCustomerAddressesEditDialog/OrderCustomerAddressesSearch.tsx @@ -17,10 +17,10 @@ import { ConfirmButtonTransitionState, SearchIcon, } from "@saleor/macaw-ui"; +import { getById } from "@saleor/misc"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { getById } from "../OrderReturnPage/utils"; import { dialogMessages as messages } from "./messages"; import { useStyles } from "./styles"; import { parseQuery, stringifyAddress } from "./utils"; diff --git a/src/orders/components/OrderCustomerAddressesEditDialog/utils.ts b/src/orders/components/OrderCustomerAddressesEditDialog/utils.ts index 987a160a7..b092a4183 100644 --- a/src/orders/components/OrderCustomerAddressesEditDialog/utils.ts +++ b/src/orders/components/OrderCustomerAddressesEditDialog/utils.ts @@ -8,9 +8,8 @@ import { OrderErrorFragment, } from "@saleor/graphql"; import { FormChange } from "@saleor/hooks/useForm"; -import { flatten } from "@saleor/misc"; +import { flatten, getById } from "@saleor/misc"; -import { getById } from "../OrderReturnPage/utils"; import { OrderCustomerAddressesEditData, OrderCustomerAddressesEditHandlers, diff --git a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx index c7983dbf0..3169789dd 100644 --- a/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx +++ b/src/orders/components/OrderReturnPage/OrderReturnRefundItemsCard/ReturnItemsCard.tsx @@ -18,14 +18,13 @@ import { } from "@saleor/graphql"; import { FormsetChange } from "@saleor/hooks/useFormset"; import { makeStyles, ResponsiveTable } from "@saleor/macaw-ui"; -import { renderCollection } from "@saleor/misc"; +import { getById, renderCollection } from "@saleor/misc"; import React, { CSSProperties } from "react"; import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import OrderCardTitle from "../../OrderCardTitle"; import { FormsetQuantityData, FormsetReplacementData } from "../form"; import { - getById, getQuantityDataFromItems, getReplacementDataFromItems, } from "../utils"; diff --git a/src/orders/components/OrderReturnPage/form.tsx b/src/orders/components/OrderReturnPage/form.tsx index f0230860f..89e0de7e9 100644 --- a/src/orders/components/OrderReturnPage/form.tsx +++ b/src/orders/components/OrderReturnPage/form.tsx @@ -9,11 +9,11 @@ import useFormset, { FormsetData, } from "@saleor/hooks/useFormset"; import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit"; +import { getById } from "@saleor/misc"; import React, { useEffect } from "react"; import { OrderRefundAmountCalculationMode } from "../OrderRefundPage/form"; import { - getById, getLineItem, getOrderUnfulfilledLines, getParsedLineData, diff --git a/src/orders/components/OrderReturnPage/utils.tsx b/src/orders/components/OrderReturnPage/utils.tsx index ddf16373e..c39a949da 100644 --- a/src/orders/components/OrderReturnPage/utils.tsx +++ b/src/orders/components/OrderReturnPage/utils.tsx @@ -1,4 +1,5 @@ import { FulfillmentStatus, OrderDetailsFragment } from "@saleor/graphql"; +import { getById } from "@saleor/misc"; import { Node } from "@saleor/types"; import { @@ -100,13 +101,6 @@ export const getParsedLines = ( quantity, })); -export const getById = (idToCompare: string) => (obj: Node) => - obj.id === idToCompare; - -export const getByUnmatchingId = (idToCompare: string) => (obj: { - id: string; -}) => obj.id !== idToCompare; - const isIncludedInIds = function( arrayToCompare: string[] | T[], obj: Node, diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts index 37076baa1..a4ab6de2e 100644 --- a/src/orders/utils/data.ts +++ b/src/orders/utils/data.ts @@ -14,7 +14,7 @@ import { WarehouseFragment, } from "@saleor/graphql"; import { FormsetData } from "@saleor/hooks/useFormset"; -import { findInEnum } from "@saleor/misc"; +import { findInEnum, getById } from "@saleor/misc"; import { LineItemData, @@ -23,7 +23,6 @@ import { import { getAllOrderFulfilledLines, getAllOrderWaitingLines, - getById, } from "../components/OrderReturnPage/utils"; export type OrderWithTotalAndTotalCaptured = Pick< diff --git a/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx b/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx index d8c7e6e81..b57b63f6a 100644 --- a/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx +++ b/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx @@ -11,12 +11,17 @@ import { useWarehouseListQuery, } from "@saleor/graphql"; import useNavigator from "@saleor/hooks/useNavigator"; +import { + extractMutationErrors, + getById, + getMutationState, + getStringOrPlaceholder, +} from "@saleor/misc"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types"; import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog"; import OrderFulfillStockExceededDialog from "@saleor/orders/components/OrderFulfillStockExceededDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import { transformFuflillmentLinesToStockFormsetData } from "@saleor/orders/utils/data"; import { PartialMutationProviderOutput } from "@saleor/types"; import { mapEdgesToItems } from "@saleor/utils/maps"; @@ -24,11 +29,6 @@ import React from "react"; import { useIntl } from "react-intl"; import { customerUrl } from "../../../../customers/urls"; -import { - extractMutationErrors, - getMutationState, - getStringOrPlaceholder, -} from "../../../../misc"; import { productUrl } from "../../../../products/urls"; import OrderAddressFields from "../../../components/OrderAddressFields/OrderAddressFields"; import OrderCancelDialog from "../../../components/OrderCancelDialog"; diff --git a/src/orders/views/OrderReturn/utils.tsx b/src/orders/views/OrderReturn/utils.tsx index 9d52be50d..c4c98f840 100644 --- a/src/orders/views/OrderReturn/utils.tsx +++ b/src/orders/views/OrderReturn/utils.tsx @@ -4,12 +4,12 @@ import { OrderReturnLineInput, OrderReturnProductsInput, } from "@saleor/graphql"; +import { getById } from "@saleor/misc"; import { OrderRefundAmountCalculationMode } from "@saleor/orders/components/OrderRefundPage/form"; import { FormsetQuantityData, OrderReturnFormData, } from "@saleor/orders/components/OrderReturnPage/form"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; class ReturnFormDataParser { private order: OrderDetailsFragment; diff --git a/src/products/components/OrderDiscountProviders/OrderLineDiscountProvider.tsx b/src/products/components/OrderDiscountProviders/OrderLineDiscountProvider.tsx index 3a01691f0..9b715877b 100644 --- a/src/products/components/OrderDiscountProviders/OrderLineDiscountProvider.tsx +++ b/src/products/components/OrderDiscountProviders/OrderLineDiscountProvider.tsx @@ -6,8 +6,8 @@ import { import useNotifier from "@saleor/hooks/useNotifier"; import { getDefaultNotifierSuccessErrorData } from "@saleor/hooks/useNotifier/utils"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; +import { getById } from "@saleor/misc"; import { OrderDiscountCommonInput } from "@saleor/orders/components/OrderDiscountCommonModal/types"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import React, { createContext, useState } from "react"; import { useIntl } from "react-intl"; diff --git a/src/shipping/views/RateUpdate.tsx b/src/shipping/views/RateUpdate.tsx index b1a2d3207..100cee379 100644 --- a/src/shipping/views/RateUpdate.tsx +++ b/src/shipping/views/RateUpdate.tsx @@ -27,10 +27,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { PaginatorContext } from "@saleor/hooks/usePaginator"; import { commonMessages, sectionNames } from "@saleor/intl"; -import { - getById, - getByUnmatchingId, -} from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById, getByUnmatchingId } from "@saleor/misc"; import useProductSearch from "@saleor/searches/useProductSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; import ShippingMethodProductsAddDialog from "@saleor/shipping/components/ShippingMethodProductsAddDialog"; diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index afa1777bd..624b2b14a 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -21,7 +21,12 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { + extractMutationErrors, + findValueInEnum, + getById, + getStringOrPlaceholder, +} from "@saleor/misc"; import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; import ShippingZoneAddWarehouseDialog from "@saleor/shipping/components/ShippingZoneAddWarehouseDialog"; @@ -37,11 +42,6 @@ import { diff } from "fast-array-diff"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { - extractMutationErrors, - findValueInEnum, - getStringOrPlaceholder, -} from "../../../misc"; import ShippingZoneDetailsPage from "../../components/ShippingZoneDetailsPage"; import { ShippingZoneUpdateFormData } from "../../components/ShippingZoneDetailsPage/types"; import { diff --git a/src/shipping/views/ShippingZonesList.tsx b/src/shipping/views/ShippingZonesList.tsx index e549261c8..36b226cf7 100644 --- a/src/shipping/views/ShippingZonesList.tsx +++ b/src/shipping/views/ShippingZonesList.tsx @@ -21,10 +21,10 @@ import { commonMessages } from "@saleor/intl"; import { DeleteIcon, IconButton } from "@saleor/macaw-ui"; import { extractMutationErrors, + getById, getStringOrPlaceholder, maybe, } from "@saleor/misc"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import { mapEdgesToItems } from "@saleor/utils/maps"; diff --git a/src/taxes/pages/TaxClassesPage/TaxClassesPage.tsx b/src/taxes/pages/TaxClassesPage/TaxClassesPage.tsx index c06b84de1..b38cba767 100644 --- a/src/taxes/pages/TaxClassesPage/TaxClassesPage.tsx +++ b/src/taxes/pages/TaxClassesPage/TaxClassesPage.tsx @@ -28,8 +28,8 @@ import { PageTabs, SearchIcon, } from "@saleor/macaw-ui"; +import { getById } from "@saleor/misc"; import { parseQuery } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/utils"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import { taxesMessages } from "@saleor/taxes/messages"; import { TaxClassesPageFormData } from "@saleor/taxes/types"; import { useAutofocus } from "@saleor/taxes/utils/useAutofocus"; diff --git a/src/warehouses/views/WarehouseList/WarehouseList.tsx b/src/warehouses/views/WarehouseList/WarehouseList.tsx index 1e627039f..5f8c3c1fc 100644 --- a/src/warehouses/views/WarehouseList/WarehouseList.tsx +++ b/src/warehouses/views/WarehouseList/WarehouseList.tsx @@ -17,8 +17,7 @@ import usePaginator, { PaginatorContext, } from "@saleor/hooks/usePaginator"; import { commonMessages, sectionNames } from "@saleor/intl"; -import { getMutationStatus, maybe } from "@saleor/misc"; -import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { getById, getMutationStatus, maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; diff --git a/src/webhooks/components/WebhookDetailsPage/messages.ts b/src/webhooks/components/WebhookDetailsPage/messages.ts deleted file mode 100644 index a072ea60e..000000000 --- a/src/webhooks/components/WebhookDetailsPage/messages.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { WebhookDetailsQuery } from "@saleor/graphql"; -import { getStringOrPlaceholder } from "@saleor/misc"; -import { isUnnamed } from "@saleor/webhooks/utils"; -import { defineMessages, IntlShape } from "react-intl"; - -export const messages = defineMessages({ - header: { - id: "snUby7", - defaultMessage: "Unnamed Webhook Details", - description: "header", - }, - headerNamed: { - id: "OPtrMg", - defaultMessage: "{webhookName} Details", - description: "header", - }, - headerCreate: { - id: "Ryh3iR", - defaultMessage: "Create Webhook", - description: "header", - }, -}); - -export const getHeaderTitle = ( - intl: IntlShape, - webhook?: WebhookDetailsQuery["webhook"], -) => { - if (!webhook) { - return intl.formatMessage(messages.headerCreate); - } - if (isUnnamed(webhook)) { - return intl.formatMessage(messages.header); - } - return intl.formatMessage(messages.headerNamed, { - webhookName: getStringOrPlaceholder(webhook?.name), - }); -}; diff --git a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx b/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx deleted file mode 100644 index 264cdf68b..000000000 --- a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { appsListUrl, customAppUrl } from "@saleor/apps/urls"; -import { Backlink } from "@saleor/components/Backlink"; -import Container from "@saleor/components/Container"; -import Form from "@saleor/components/Form"; -import FormSpacer from "@saleor/components/FormSpacer"; -import Grid from "@saleor/components/Grid"; -import PageHeader from "@saleor/components/PageHeader"; -import Savebar from "@saleor/components/Savebar"; -import { - WebhookDetailsFragment, - WebhookErrorFragment, - WebhookEventTypeAsyncEnum, - WebhookEventTypeSyncEnum, -} from "@saleor/graphql"; -import { SubmitPromise } from "@saleor/hooks/useForm"; -import useNavigator from "@saleor/hooks/useNavigator"; -import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; -import WebhookEvents from "@saleor/webhooks/components/WebhookEvents"; -import WebhookInfo from "@saleor/webhooks/components/WebhookInfo"; -import WebhookStatus from "@saleor/webhooks/components/WebhookStatus"; -import { - createAsyncEventsSelectHandler, - createSyncEventsSelectHandler, -} from "@saleor/webhooks/handlers"; -import { - mapAsyncEventsToChoices, - mapSyncEventsToChoices, -} from "@saleor/webhooks/utils"; -import React from "react"; -import { useIntl } from "react-intl"; - -import { getHeaderTitle } from "./messages"; - -export interface WebhookFormData { - syncEvents: WebhookEventTypeSyncEnum[]; - asyncEvents: WebhookEventTypeAsyncEnum[]; - isActive: boolean; - name: string; - secretKey: string | null; - targetUrl: string; -} - -export interface WebhookDetailsPageProps { - appName: string; - disabled: boolean; - errors: WebhookErrorFragment[]; - webhook?: WebhookDetailsFragment; - saveButtonBarState: ConfirmButtonTransitionState; - onSubmit: (data: WebhookFormData) => SubmitPromise; -} - -const WebhookDetailsPage: React.FC = ({ - appName, - disabled, - errors, - webhook, - saveButtonBarState, - onSubmit, -}) => { - const intl = useIntl(); - const navigate = useNavigator(); - - const initialForm: WebhookFormData = { - syncEvents: webhook?.syncEvents?.map(event => event.eventType) || [], - asyncEvents: webhook?.asyncEvents?.map(event => event.eventType) || [], - isActive: !!webhook?.isActive, - name: webhook?.name || "", - secretKey: webhook?.secretKey || "", - targetUrl: webhook?.targetUrl || "", - }; - - const backUrl = webhook ? customAppUrl(webhook.app.id) : appsListUrl(); - - return ( - - {({ data, submit, change }) => { - const syncEventsChoices = disabled - ? [] - : mapSyncEventsToChoices(Object.values(WebhookEventTypeSyncEnum)); - const asyncEventsChoices = disabled - ? [] - : mapAsyncEventsToChoices( - Object.values(WebhookEventTypeAsyncEnum), - data.asyncEvents, - ); - - const handleSyncEventsSelect = createSyncEventsSelectHandler( - change, - data.syncEvents, - ); - const handleAsyncEventsSelect = createAsyncEventsSelectHandler( - change, - data.asyncEvents, - ); - - return ( - - {appName} - - -
- -
-
- - - -
-
- navigate(backUrl)} - onSubmit={submit} - /> -
- ); - }} - - ); -}; -WebhookDetailsPage.displayName = "WebhookDetailsPage"; -export default WebhookDetailsPage; diff --git a/src/webhooks/index.tsx b/src/webhooks/index.tsx deleted file mode 100644 index c2df97108..000000000 --- a/src/webhooks/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import WebhooksCreateView from "@saleor/webhooks/views/WebhooksCreate"; -import React from "react"; -import { Route, RouteComponentProps } from "react-router-dom"; - -import { webhookAddPath, webhookPath } from "./urls"; -import WebhooksDetails from "./views/WebhooksDetails"; - -const WebhookDetails: React.FC> = ({ match }) => ( - -); - -const WebhookCreate: React.FC> = ({ match }) => ( - -); - -const Component = () => ( - <> - - - -); - -export default Component; diff --git a/src/webhooks/urls.ts b/src/webhooks/urls.ts deleted file mode 100644 index a7d363e00..000000000 --- a/src/webhooks/urls.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { appsSection, customAppListPath } from "@saleor/apps/urls"; -import { stringifyQs } from "@saleor/utils/urls"; -import urlJoin from "url-join"; - -import { ActiveTab, Dialog, SingleAction, TabActionDialog } from "../types"; - -export const webhookSection = "/webhooks/"; -export const webhookListPath = webhookSection; - -export type WebhookListUrlDialog = "remove" | TabActionDialog; - -export type WebhookListUrlQueryParams = ActiveTab & - Dialog & - SingleAction; - -export const webhookPath = (id: string) => - urlJoin(appsSection, webhookSection, id); - -export type WebhookUrlDialog = "remove"; -export type WebhookUrlQueryParams = Dialog & SingleAction; -export const webhookUrl = (id: string, params?: WebhookUrlQueryParams) => - webhookPath(encodeURIComponent(id)) + "?" + stringifyQs(params); - -export const webhookAddPath = (id: string) => - urlJoin(customAppListPath, id, webhookSection, "add"); -export const webhookAddUrl = webhookAddPath;