Extract "webhooks & events" to separate page (#2818)

* Extract "webhooks & events" to separate page

* Create separate /custom-app/ path for custom apps with webhooks

* Change all /webhooks/ paths to /custom-apps/

* Update messages

* Update generated graphql types

* Create findById util

* Refactor consts and resolvers for custom app urls

* Fix app graphql fragment

* Update Miscellaneous icon for Webhooks and Events

* Add tests for custom apps utils

* Fix dark-mode Miscellaneous icon for Webhooks and Events

* adjustments for autotests

Co-authored-by: karolm-saleor <karol.macheta@saleor.io>
This commit is contained in:
Dawid 2022-12-15 14:51:05 +01:00 committed by GitHub
parent ef35e5149b
commit bba95a8fb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 1053 additions and 910 deletions

View file

@ -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 {

View file

@ -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)

View file

@ -1,4 +1,3 @@
export const APPS_LIST = {
createLocalAppButton: '[data-test-id="create-app"]',
webhookAndEventsTab: '[id="WEBHOOKS_AND_EVENTS"]',
};

View file

@ -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}`;

View file

@ -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}}"

View file

@ -9,7 +9,6 @@ import { useIntl } from "react-intl";
const TabValue: Record<string, AppPagePathSegment> = {
SALEOR_APPS: "saleor-apps",
THIRD_PARTY: "third-party",
WEBHOOKS_AND_EVENTS: "webhooks-and-events",
};
type AllProps = ComponentProps<typeof PageTabs>;
@ -23,14 +22,6 @@ export const AppPageTabs = ({ showSaleorApps, ...props }: AvailableProps) => {
const intl = useIntl();
return (
<PageTabs {...props}>
<PageTab
value={TabValue.WEBHOOKS_AND_EVENTS}
id="WEBHOOKS_AND_EVENTS"
label={intl.formatMessage({
defaultMessage: "Webhooks & Events",
id: "UxTSw7",
})}
/>
<PageTab
value={TabValue.THIRD_PARTY}
label={intl.formatMessage({

View file

@ -11,7 +11,7 @@ import { PaginatorContextDecorator } from "@saleor/storybook/PaginatorContextDec
import { storiesOf } from "@storybook/react";
import React from "react";
import { appsInProgress, appsList, customAppsList } from "../../fixtures";
import { appsInProgress, appsList } from "../../fixtures";
import AppsListPage, { AppsListPageProps } from "./AppsListPage";
const props: AppsListPageProps = {
@ -24,13 +24,10 @@ const props: AppsListPageProps = {
__typename: "Query",
appsInstallations: appsInProgress,
},
customAppsList,
disabled: false,
installedAppsList: appsList,
getCustomAppHref: () => "",
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={[]}
/>
));

View file

@ -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<AppsListPageProps> = ({
appsInProgressList,
customAppsList,
installedAppsList,
getCustomAppHref,
onInstalledAppRemove,
onCustomAppRemove,
onAppInProgressRemove,
onAppInstallRetry,
...listProps
@ -79,7 +72,7 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
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<AppsListPageProps> = ({
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<AppsListPageProps> = ({
</>
);
}
case "webhooks-and-events": {
return (
<>
<p>
<FormattedMessage
defaultMessage="Local apps are custom webhooks & token pairs that can be used to
connect apps and access Saleor API."
id="GDJHXl"
/>
</p>
<CustomApps
appsList={customAppsList}
getCustomAppHref={getCustomAppHref}
onRemove={onCustomAppRemove}
/>
</>
);
}
case "saleor-apps": {
return (
<>

View file

@ -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<CustomAppsProps> = ({
appsList,
onRemove,
getCustomAppHref,
}) => {
const intl = useIntl();
const classes = useStyles({});
return (
<Card className={classes.customApps}>
<CardTitle
toolbar={
<Button
variant="secondary"
href={customAppAddUrl}
data-test-id="create-app"
>
<FormattedMessage
id="XB2Jj9"
defaultMessage="Create App"
description="create app button"
/>
</Button>
}
title={intl.formatMessage(commonMessages.customApps)}
/>
<ResponsiveTable>
<TableBody>
{renderCollection(
appsList,
(app, index) =>
app ? (
<TableRowLink
key={app.node.id}
className={classes.tableRow}
href={getCustomAppHref(app.node.id)}
>
<TableCell className={classes.colName}>
<span data-tc="name" className={classes.appName}>
{app.node.name}
</span>
{!app.node.isActive && (
<div className={classes.statusWrapper}>
<DeactivatedText />
</div>
)}
</TableCell>
<TableCell className={classes.colAction}>
<TableButtonWrapper>
<IconButton
variant="secondary"
color="primary"
onClick={() => onRemove(app.node.id)}
>
<DeleteIcon />
</IconButton>
</TableButtonWrapper>
</TableCell>
</TableRowLink>
) : (
<AppsSkeleton key={index} />
),
() => (
<TableRowLink className={classes.tableRow}>
<TableCell className={classes.colName}>
<Typography className={classes.text} variant="body2">
<FormattedMessage
id="voRaz3"
defaultMessage="Your custom-created apps will be shown here."
description="custom apps content"
/>
</Typography>
</TableCell>
</TableRowLink>
),
)}
</TableBody>
</ResponsiveTable>
</Card>
);
};
CustomApps.displayName = "CustomApps";
export default CustomApps;

View file

@ -1,2 +0,0 @@
export * from "./CustomApps";
export { default } from "./CustomApps";

View file

@ -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<InstalledAppsProps> = ({
(app, index) =>
app ? (
<TableRowLink
key={app.node.id}
key={app.id}
className={classes.tableRow}
href={appUrl(app.node.id)}
href={appUrl(app.id)}
>
<TableCell className={classes.colName}>
<span data-tc="name" className={classes.appName}>
{app.node.name}
{app.name}
</span>
{app.node.manifestUrl &&
isAppInTunnel(app.node.manifestUrl) ? (
{app.manifestUrl && isAppInTunnel(app.manifestUrl) ? (
<Typography variant="caption">
<FormattedMessage
defaultMessage="(TUNNEL - DEVELOPMENT)"
@ -100,23 +99,21 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
</TableCell>
<TableCell className={classes.colAction}>
{app.node.manifestUrl && (
<AppManifestTableDisplay
manifestUrl={app.node.manifestUrl}
/>
{app.manifestUrl && (
<AppManifestTableDisplay manifestUrl={app.manifestUrl} />
)}
<TableButtonWrapper>
<Switch
checked={app.node.isActive}
onChange={getHandleToggle(app.node)}
checked={app.isActive}
onChange={getHandleToggle(app)}
/>
</TableButtonWrapper>
<AppPermissions permissions={app.node.permissions} />
<AppPermissions permissions={app.permissions} />
<TableButtonWrapper>
<IconButton
variant="secondary"
color="primary"
onClick={() => onRemove(app.node.id)}
onClick={() => onRemove(app.id)}
>
<DeleteIcon />
</IconButton>

View file

@ -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.",
},
],
},
];

View file

@ -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 = () => {

View file

@ -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<RouteComponentProps<{ id: string }>> = ({
match,
@ -47,34 +42,6 @@ const AppInstall: React.FC<RouteComponentProps> = props => {
return <AppInstallView params={params} {...props} />;
};
interface CustomAppDetailsProps extends RouteComponentProps<{ id?: string }> {
token: string;
onTokenClose: () => void;
}
const CustomAppDetails: React.FC<CustomAppDetailsProps> = ({
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 (
<CustomAppDetailsView
id={decodeURIComponent(match.params.id)}
params={params}
token={token}
onTokenClose={onTokenClose}
/>
);
};
const AppsList: React.FC<RouteComponentProps> = () => {
const qs = parseQs(location.search.substr(1));
const params: AppListUrlQueryParams = qs;
@ -83,32 +50,15 @@ const AppsList: React.FC<RouteComponentProps> = () => {
};
const Component = () => {
const intl = useIntl();
const [token, setToken] = React.useState<string>(null);
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.apps)} />
<Switch>
<Route exact path={appsListPath} component={AppsList} />
<Route
exact
path={customAppAddPath}
render={() => <CustomAppCreateView setToken={setToken} />}
/>
<Route exact path={appInstallPath} component={AppInstall} />
<Route exact path={appDetailsPath(":id")} component={AppDetails} />
<Route path={appPath(":id")} component={App} />
<Route
exact
path={customAppPath(":id")}
render={props => (
<CustomAppDetails
{...props}
token={token}
onTokenClose={() => setToken(null)}
/>
)}
/>
<WebhooksRoutes />
</Switch>
</>

View file

@ -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<CustomAppUrlDialog> & 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);

View file

@ -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<AppsListProps> = ({ 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<AppsListProps> = ({ 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<AppsListProps> = ({ 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<AppsListProps> = ({ params }) => {
[activateApp, deactivateApp],
);
const installedApps = mapEdgesToItems(data?.apps);
const currentAppName = findById(params.id, installedApps)?.name;
return (
<AppListContext.Provider value={context}>
<PaginatorContext.Provider value={paginationValues}>
<AppDeleteDialog
confirmButtonState={deleteAppOpts.status}
name={getCurrentAppName(
params.id,
action === "remove-app" ? installedApps : customApps,
)}
name={currentAppName}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
type={action === "remove-app" ? "EXTERNAL" : "CUSTOM"}
open={action === "remove-app" || action === "remove-custom-app"}
type="EXTERNAL"
open={action === "remove-app"}
/>
<AppActivateDialog
confirmButtonState={activateAppResult.status}
name={getCurrentAppName(params.id, installedApps)}
name={currentAppName}
onClose={closeModal}
onConfirm={handleActivateAppConfirm}
open={params.action === "app-activate"}
/>
<AppDeactivateDialog
confirmButtonState={deactivateAppResult.status}
name={getCurrentAppName(params.id, installedApps)}
name={currentAppName}
onClose={closeModal}
onConfirm={handleDeactivateAppConfirm}
open={params.action === "app-deactivate"}
@ -328,23 +301,16 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
/>
<AppsListPage
installedAppsList={installedApps}
customAppsList={customApps}
appsInProgressList={appsInProgressData}
disabled={loading || customAppsLoading}
disabled={loading}
settings={settings}
onUpdateListSettings={updateListSettings}
onAppInstallRetry={onAppInstallRetry}
getCustomAppHref={id => customAppUrl(id)}
onInstalledAppRemove={id =>
openModal("remove-app", {
id,
})
}
onCustomAppRemove={id =>
openModal("remove-custom-app", {
id,
})
}
onAppInProgressRemove={id =>
openModal("remove", {
id,

View file

@ -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";

View file

@ -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";

View file

@ -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<

View file

@ -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";

View file

@ -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: <Miscellaneous />,
title: intl.formatMessage(sectionNames.webhooksAndEvents),
url: CustomAppUrls.resolveAppListUrl(),
testId: "configuration-menu-webhooks-and-events",
},
],
},
];

View file

@ -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<CustomAppCreatePageProps> = props => {
>
{({ data, change, submit, isSaveDisabled }) => (
<Container>
<Backlink href={appsListUrl()}>
<Backlink href={CustomAppUrls.resolveAppListUrl()}>
{intl.formatMessage(sectionNames.apps)}
</Backlink>
<PageHeader
@ -102,7 +102,7 @@ const CustomAppCreatePage: React.FC<CustomAppCreatePageProps> = props => {
<Savebar
disabled={isSaveDisabled}
state={saveButtonBarState}
onCancel={() => navigate(appsListUrl())}
onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())}
onSubmit={submit}
/>
</Container>

View file

@ -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<CustomAppDetailsPageProps> = 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<CustomAppDetailsPageProps> = props => {
>
{({ data, change, submit, isSaveDisabled }) => (
<Container>
<Backlink href={appsListUrl()}>
<Backlink href={CustomAppUrls.resolveAppListUrl()}>
{intl.formatMessage(sectionNames.apps)}
</Backlink>
<PageHeader title={app?.name}>
@ -188,7 +188,7 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
<Savebar
disabled={isSaveDisabled}
state={saveButtonBarState}
onCancel={() => navigate(appsListUrl())}
onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())}
onSubmit={submit}
/>
</Container>

View file

@ -0,0 +1,12 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
activateButton: {
"& img": {
marginRight: theme.spacing(1),
},
},
}),
{ name: "CustomAppDetailsPage" },
);

View file

@ -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<CustomAppListPageProps> = ({
appsList,
onRemove,
getCustomAppHref,
}) => {
const intl = useIntl();
const classes = useStyles({});
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.webhooksAndEvents)} />
<p>
<FormattedMessage
defaultMessage="Local apps are custom webhooks & token pairs that can be used to
connect apps and access Saleor API."
id="L/sNGY"
/>
</p>
<Card className={classes.customApps}>
<CardTitle
toolbar={
<Button
variant="secondary"
href={CustomAppUrls.appAddUrl}
data-test-id="create-app"
>
<FormattedMessage
id="XB2Jj9"
defaultMessage="Create App"
description="create app button"
/>
</Button>
}
title={intl.formatMessage(commonMessages.customApps)}
/>
<ResponsiveTable>
<TableBody>
{renderCollection(
appsList,
(app, index) =>
app ? (
<TableRowLink
key={app.id}
className={classes.tableRow}
href={getCustomAppHref(app.id)}
>
<TableCell className={classes.colName}>
<span data-tc="name" className={classes.appName}>
{app.name}
</span>
{!app.isActive && (
<div className={classes.statusWrapper}>
<DeactivatedText />
</div>
)}
</TableCell>
<TableCell className={classes.colAction}>
<TableButtonWrapper>
<IconButton
variant="secondary"
color="primary"
onClick={() => onRemove(app.id)}
>
<DeleteIcon />
</IconButton>
</TableButtonWrapper>
</TableCell>
</TableRowLink>
) : (
<AppsSkeleton key={index} />
),
() => (
<TableRowLink className={classes.tableRow}>
<TableCell className={classes.colName}>
<Typography className={classes.text} variant="body2">
<FormattedMessage
id="voRaz3"
defaultMessage="Your custom-created apps will be shown here."
description="custom apps content"
/>
</Typography>
</TableCell>
</TableRowLink>
),
)}
</TableBody>
</ResponsiveTable>
</Card>
</Container>
);
};
CustomAppListPage.displayName = "CustomAppListPage";
export default CustomAppListPage;

View file

@ -0,0 +1,2 @@
export * from "./CustomAppListPage";
export { default } from "./CustomAppListPage";

View file

@ -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<any[]>;
}
const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
appName,
appId,
appName,
disabled,
errors,
webhook,
@ -61,17 +62,19 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
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 (
<Form initial={initialForm} onSubmit={onSubmit}>
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
{({ data, submit, change }) => {
const syncEventsChoices = disabled
? []
@ -94,7 +97,7 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
return (
<Container>
<Backlink href={customAppUrl(appId)}>{appName}</Backlink>
<Backlink href={backUrl}>{appName}</Backlink>
<PageHeader title={getHeaderTitle(intl, webhook)} />
<Grid variant="uniform">
<div>
@ -124,7 +127,7 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
<Savebar
disabled={disabled}
state={saveButtonBarState}
onCancel={() => navigate(customAppUrl(appId))}
onCancel={() => navigate(backUrl)}
onSubmit={submit}
/>
</Container>

View file

@ -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({

View file

@ -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";

View file

@ -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";

View file

@ -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<WebhookStatusProps> = ({
{intl.formatMessage(messages.webhookActiveDescription)}
</Typography>
<ControlledCheckbox
name={"isActive" as keyof FormData}
name={"isActive" as keyof WebhookFormData}
label={intl.formatMessage(messages.webhookActive)}
checked={data}
onChange={onChange}

View file

@ -6,6 +6,8 @@ import Skeleton from "@saleor/components/Skeleton";
import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper";
import TableCellHeader from "@saleor/components/TableCellHeader";
import TableRowLink from "@saleor/components/TableRowLink";
import { CustomAppUrls } from "@saleor/custom-apps/urls";
import { isUnnamed } from "@saleor/custom-apps/utils";
import { WebhookFragment } from "@saleor/graphql";
import {
commonMessages,
@ -14,8 +16,6 @@ import {
} from "@saleor/intl";
import { DeleteIcon, IconButton, Pill } from "@saleor/macaw-ui";
import { renderCollection, stopPropagation } from "@saleor/misc";
import { webhookPath } from "@saleor/webhooks/urls";
import { isUnnamed } from "@saleor/webhooks/utils";
import clsx from "clsx";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -41,7 +41,7 @@ const WebhooksList: React.FC<WebhooksListProps> = ({
return (
<Card>
<CardTitle
title={intl.formatMessage(sectionNames.webhooks)}
title={intl.formatMessage(sectionNames.webhooksAndEvents)}
toolbar={
!!createHref && (
<Button
@ -75,7 +75,10 @@ const WebhooksList: React.FC<WebhooksListProps> = ({
<TableRowLink
hover={!!webhook}
className={!!webhook ? classes.tableRow : undefined}
href={webhook && webhookPath(webhook.id)}
href={
webhook &&
CustomAppUrls.resolveWebhookUrl(webhook.app.id, webhook.id)
}
key={webhook ? webhook.id : "skeleton"}
>
<TableCell

122
src/custom-apps/index.tsx Normal file
View file

@ -0,0 +1,122 @@
import { WindowTitle } from "@saleor/components/WindowTitle";
import { sectionNames } from "@saleor/intl";
import { parse as parseQs } from "qs";
import React from "react";
import { useIntl } from "react-intl";
import { Route, RouteComponentProps, Switch } from "react-router-dom";
import {
CustomAppDetailsUrlQueryParams,
CustomAppListUrlQueryParams,
CustomAppPaths,
} from "./urls";
import CustomAppCreateView from "./views/CustomAppCreate";
import CustomAppDetailsView from "./views/CustomAppDetails";
import CustomAppListView from "./views/CustomAppList";
import CustomAppWebhookCreateView from "./views/CustomAppWebhookCreate";
import CustomAppWebhookDetailsView from "./views/CustomAppWebhookDetails";
const CustomAppList: React.FC<RouteComponentProps> = () => {
const qs = parseQs(location.search.substr(1));
const params: CustomAppListUrlQueryParams = qs;
return <CustomAppListView params={params} />;
};
interface CustomAppDetailsProps extends RouteComponentProps<{ id?: string }> {
token: string;
onTokenClose: () => void;
}
const CustomAppDetails: React.FC<CustomAppDetailsProps> = ({
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 (
<CustomAppDetailsView
id={decodeURIComponent(id)}
params={params}
token={token}
onTokenClose={onTokenClose}
/>
);
};
const CustomAppWebhookCreate: React.FC<RouteComponentProps<any>> = ({
match,
}) => {
const appId = match.params.appId;
if (!appId) {
throw new Error("No App ID provided");
}
return <CustomAppWebhookCreateView appId={decodeURIComponent(appId)} />;
};
const CustomAppWebhookDetails: React.FC<RouteComponentProps<any>> = ({
match,
}) => {
const id = match.params.id;
if (!id) {
throw new Error("No ID provided");
}
return <CustomAppWebhookDetailsView id={decodeURIComponent(id)} />;
};
const Component = () => {
const intl = useIntl();
const [token, setToken] = React.useState<string>(null);
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.webhooksAndEvents)} />
<Switch>
<Route
exact
path={CustomAppPaths.appListPath}
component={CustomAppList}
/>
<Route
exact
path={CustomAppPaths.appAddPath}
render={() => <CustomAppCreateView setToken={setToken} />}
/>
<Route
exact
path={CustomAppPaths.resolveAppPath(":id")}
render={props => (
<CustomAppDetails
{...props}
token={token}
onTokenClose={() => setToken(null)}
/>
)}
/>
<Route
exact
path={CustomAppPaths.resolveWebhookAddPath(":appId")}
component={CustomAppWebhookCreate}
/>
<Route
exact
path={CustomAppPaths.resolveWebhookPath(":appId", ":id")}
component={CustomAppWebhookDetails}
/>
</Switch>
</>
);
};
export default Component;

View file

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

70
src/custom-apps/urls.ts Normal file
View file

@ -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<CustomAppListUrlDialog> &
SingleAction;
export type CustomAppDetailsUrlDialog =
| "create-token"
| "remove-webhook"
| "remove-token"
| "app-activate"
| "app-deactivate";
export type CustomAppDetailsUrlQueryParams = Dialog<CustomAppDetailsUrlDialog> &
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),
};

View file

@ -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);
});
});

View file

@ -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<CustomAppCreateProps> = ({
setToken,
}) => {
@ -31,7 +32,7 @@ export const CustomAppCreate: React.FC<CustomAppCreateProps> = ({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges),
});
navigate(customAppUrl(data.appCreate.app.id));
navigate(CustomAppUrls.resolveAppUrl(data.appCreate.app.id));
setToken(data.appCreate.authToken);
}
};

View file

@ -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<OrderListProps> = ({
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<OrderListProps> = ({
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<OrderListProps> = ({
const currentToken = data?.app?.tokens?.find(token => token.id === params.id);
if (customApp === null) {
return <NotFoundPage backHref={appsListUrl()} />;
return <NotFoundPage backHref={CustomAppUrls.resolveAppListUrl()} />;
}
return (
@ -233,7 +231,7 @@ export const CustomAppDetails: React.FC<OrderListProps> = ({
id,
})
}
webhookCreateHref={webhookAddPath(id)}
webhookCreateHref={CustomAppUrls.resolveWebhookAddUrl(id)}
onWebhookRemove={id =>
openModal("remove-webhook", {
id,

View file

@ -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<CustomAppListProps> = ({ 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 (
<>
<WindowTitle title={intl.formatMessage(sectionNames.webhooksAndEvents)} />
<AppDeleteDialog
confirmButtonState={deleteAppOpts.status}
name={currentAppName}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
type="CUSTOM"
open={params.action === "remove-custom-app"}
/>
<CustomAppListPage
appsList={customApps}
getCustomAppHref={id => CustomAppUrls.resolveAppUrl(id)}
onRemove={id =>
openModal("remove-custom-app", {
id,
})
}
/>
</>
);
};
CustomAppList.displayName = "CustomAppList";
export default CustomAppList;

View file

@ -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<WebhooksCreateProps> = ({ id }) => {
export const CustomAppWebhookCreate: React.FC<CustomAppWebhookCreateProps> = ({
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<WebhooksCreateProps> = ({ 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<WebhooksCreateProps> = ({ id }) => {
})}
/>
<WebhookDetailsPage
appId={appId}
appName={data?.app?.name ?? ""}
appId={id}
disabled={false}
errors={webhookCreateOpts.data?.webhookCreate?.errors ?? []}
onSubmit={handleSubmit}
@ -80,5 +84,5 @@ export const WebhooksCreate: React.FC<WebhooksCreateProps> = ({ id }) => {
);
};
WebhooksCreate.displayName = "WebhooksCreate";
export default WebhooksCreate;
CustomAppWebhookCreate.displayName = "CustomAppWebhookCreate";
export default CustomAppWebhookCreate;

View file

@ -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<WebhooksDetailsProps> = ({ id }) => {
export const CustomAppWebhookDetails: React.FC<CustomAppWebhookDetailsProps> = ({
id,
}) => {
const notify = useNotifier();
const intl = useIntl();
@ -64,7 +67,7 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({ id }) => {
}),
);
if (!webhook && !loading) {
return <NotFoundPage backHref={appsListUrl()} />;
return <NotFoundPage backHref={CustomAppUrls.resolveAppListUrl()} />;
}
return (
@ -85,5 +88,5 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({ id }) => {
);
};
WebhooksDetails.displayName = "WebhooksDetails";
export default WebhooksDetails;
CustomAppWebhookDetails.displayName = "CustomAppWebhookDetails";
export default CustomAppWebhookDetails;

View file

@ -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";

View file

@ -5341,6 +5341,155 @@ export function useCheckOrderInvoicesStatusLazyQuery(baseOptions?: ApolloReactHo
export type CheckOrderInvoicesStatusQueryHookResult = ReturnType<typeof useCheckOrderInvoicesStatusQuery>;
export type CheckOrderInvoicesStatusLazyQueryHookResult = ReturnType<typeof useCheckOrderInvoicesStatusLazyQuery>;
export type CheckOrderInvoicesStatusQueryResult = Apollo.QueryResult<Types.CheckOrderInvoicesStatusQuery, Types.CheckOrderInvoicesStatusQueryVariables>;
export const WebhookCreateDocument = gql`
mutation WebhookCreate($input: WebhookCreateInput!) {
webhookCreate(input: $input) {
errors {
...WebhookError
}
webhook {
...WebhookDetails
}
}
}
${WebhookErrorFragmentDoc}
${WebhookDetailsFragmentDoc}`;
export type WebhookCreateMutationFn = Apollo.MutationFunction<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>;
/**
* __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<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>(WebhookCreateDocument, options);
}
export type WebhookCreateMutationHookResult = ReturnType<typeof useWebhookCreateMutation>;
export type WebhookCreateMutationResult = Apollo.MutationResult<Types.WebhookCreateMutation>;
export type WebhookCreateMutationOptions = Apollo.BaseMutationOptions<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>;
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<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>;
/**
* __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<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>(WebhookUpdateDocument, options);
}
export type WebhookUpdateMutationHookResult = ReturnType<typeof useWebhookUpdateMutation>;
export type WebhookUpdateMutationResult = Apollo.MutationResult<Types.WebhookUpdateMutation>;
export type WebhookUpdateMutationOptions = Apollo.BaseMutationOptions<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>;
export const WebhookDeleteDocument = gql`
mutation WebhookDelete($id: ID!) {
webhookDelete(id: $id) {
errors {
...WebhookError
}
}
}
${WebhookErrorFragmentDoc}`;
export type WebhookDeleteMutationFn = Apollo.MutationFunction<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>;
/**
* __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<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>(WebhookDeleteDocument, options);
}
export type WebhookDeleteMutationHookResult = ReturnType<typeof useWebhookDeleteMutation>;
export type WebhookDeleteMutationResult = Apollo.MutationResult<Types.WebhookDeleteMutation>;
export type WebhookDeleteMutationOptions = Apollo.BaseMutationOptions<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>;
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<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>(WebhookDetailsDocument, options);
}
export function useWebhookDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>(WebhookDetailsDocument, options);
}
export type WebhookDetailsQueryHookResult = ReturnType<typeof useWebhookDetailsQuery>;
export type WebhookDetailsLazyQueryHookResult = ReturnType<typeof useWebhookDetailsLazyQuery>;
export type WebhookDetailsQueryResult = Apollo.QueryResult<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>;
export const UpdateCustomerDocument = gql`
mutation UpdateCustomer($id: ID!, $input: CustomerInput!) {
customerUpdate(id: $id, input: $input) {
@ -16971,152 +17120,3 @@ export function useWarehousesCountLazyQuery(baseOptions?: ApolloReactHooks.LazyQ
export type WarehousesCountQueryHookResult = ReturnType<typeof useWarehousesCountQuery>;
export type WarehousesCountLazyQueryHookResult = ReturnType<typeof useWarehousesCountLazyQuery>;
export type WarehousesCountQueryResult = Apollo.QueryResult<Types.WarehousesCountQuery, Types.WarehousesCountQueryVariables>;
export const WebhookCreateDocument = gql`
mutation WebhookCreate($input: WebhookCreateInput!) {
webhookCreate(input: $input) {
errors {
...WebhookError
}
webhook {
...WebhookDetails
}
}
}
${WebhookErrorFragmentDoc}
${WebhookDetailsFragmentDoc}`;
export type WebhookCreateMutationFn = Apollo.MutationFunction<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>;
/**
* __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<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>(WebhookCreateDocument, options);
}
export type WebhookCreateMutationHookResult = ReturnType<typeof useWebhookCreateMutation>;
export type WebhookCreateMutationResult = Apollo.MutationResult<Types.WebhookCreateMutation>;
export type WebhookCreateMutationOptions = Apollo.BaseMutationOptions<Types.WebhookCreateMutation, Types.WebhookCreateMutationVariables>;
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<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>;
/**
* __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<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>(WebhookUpdateDocument, options);
}
export type WebhookUpdateMutationHookResult = ReturnType<typeof useWebhookUpdateMutation>;
export type WebhookUpdateMutationResult = Apollo.MutationResult<Types.WebhookUpdateMutation>;
export type WebhookUpdateMutationOptions = Apollo.BaseMutationOptions<Types.WebhookUpdateMutation, Types.WebhookUpdateMutationVariables>;
export const WebhookDeleteDocument = gql`
mutation WebhookDelete($id: ID!) {
webhookDelete(id: $id) {
errors {
...WebhookError
}
}
}
${WebhookErrorFragmentDoc}`;
export type WebhookDeleteMutationFn = Apollo.MutationFunction<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>;
/**
* __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<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useMutation<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>(WebhookDeleteDocument, options);
}
export type WebhookDeleteMutationHookResult = ReturnType<typeof useWebhookDeleteMutation>;
export type WebhookDeleteMutationResult = Apollo.MutationResult<Types.WebhookDeleteMutation>;
export type WebhookDeleteMutationOptions = Apollo.BaseMutationOptions<Types.WebhookDeleteMutation, Types.WebhookDeleteMutationVariables>;
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<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>(WebhookDetailsDocument, options);
}
export function useWebhookDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>(WebhookDetailsDocument, options);
}
export type WebhookDetailsQueryHookResult = ReturnType<typeof useWebhookDetailsQuery>;
export type WebhookDetailsLazyQueryHookResult = ReturnType<typeof useWebhookDetailsLazyQuery>;
export type WebhookDetailsQueryResult = Apollo.QueryResult<Types.WebhookDetailsQuery, Types.WebhookDetailsQueryVariables>;

View file

@ -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 };

View file

@ -0,0 +1,36 @@
import { createSvgIcon, SvgIconProps } from "@material-ui/core";
import React from "react";
const Miscellaneous = createSvgIcon(
<>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.00037 2.375C3.1029 2.375 2.37537 3.10254 2.37537 4L2.37537 9C2.37537 9.89746 3.1029 10.625 4.00037 10.625H9.00037C9.89783 10.625 10.6254 9.89746 10.6254 9V4C10.6254 3.10254 9.89783 2.375 9.00037 2.375L4.00037 2.375ZM3.62537 4C3.62537 3.79289 3.79326 3.625 4.00037 3.625L9.00037 3.625C9.20747 3.625 9.37537 3.79289 9.37537 4V9C9.37537 9.20711 9.20747 9.375 9.00037 9.375H4.00037C3.79326 9.375 3.62537 9.20711 3.62537 9L3.62537 4Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15.0004 2.375C14.1029 2.375 13.3754 3.10254 13.3754 4V9C13.3754 9.89746 14.1029 10.625 15.0004 10.625H20.0004C20.8978 10.625 21.6254 9.89746 21.6254 9L21.6254 4C21.6254 3.10254 20.8978 2.375 20.0004 2.375L15.0004 2.375ZM14.6254 4C14.6254 3.79289 14.7933 3.625 15.0004 3.625L20.0004 3.625C20.2075 3.625 20.3754 3.79289 20.3754 4L20.3754 9C20.3754 9.20711 20.2075 9.375 20.0004 9.375H15.0004C14.7933 9.375 14.6254 9.20711 14.6254 9V4Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.37537 15C2.37537 14.1025 3.1029 13.375 4.00037 13.375H9.00037C9.89783 13.375 10.6254 14.1025 10.6254 15V20C10.6254 20.8975 9.89783 21.625 9.00037 21.625H4.00037C3.1029 21.625 2.37537 20.8975 2.37537 20L2.37537 15ZM4.00037 14.625C3.79326 14.625 3.62537 14.7929 3.62537 15L3.62537 20C3.62537 20.2071 3.79326 20.375 4.00037 20.375H9.00037C9.20747 20.375 9.37537 20.2071 9.37537 20V15C9.37537 14.7929 9.20747 14.625 9.00037 14.625H4.00037Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15.0004 13.375C14.1029 13.375 13.3754 14.1025 13.3754 15V20C13.3754 20.8975 14.1029 21.625 15.0004 21.625H20.0004C20.8978 21.625 21.6254 20.8975 21.6254 20L21.6254 15C21.6254 14.1025 20.8978 13.375 20.0004 13.375H15.0004ZM14.6254 15C14.6254 14.7929 14.7933 14.625 15.0004 14.625H20.0004C20.2075 14.625 20.3754 14.7929 20.3754 15L20.3754 20C20.3754 20.2071 20.2075 20.375 20.0004 20.375H15.0004C14.7933 20.375 14.6254 20.2071 14.6254 20V15Z"
fill="currentColor"
/>
</>,
"Miscellaneous",
);
export default (props: SvgIconProps) => (
<Miscellaneous {...props} viewBox="0 0 24 24" fill="none" />
);

View file

@ -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}
/>
<SectionRoute
path={CustomAppSections.appsSection}
component={CustomAppsSection}
/>
<Route component={NotFound} />
</Switch>
</ErrorBoundary>

View file

@ -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",
},
});

View file

@ -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<T, K extends keyof T> = Omit<T, K> &
Partial<Pick<T, K>>;
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 = <T extends Node>(id: string, list?: T[]) =>
list?.find(getById(id));

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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,

View file

@ -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";

View file

@ -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,

View file

@ -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";

View file

@ -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,

View file

@ -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<T extends Node>(
arrayToCompare: string[] | T[],
obj: Node,

View file

@ -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<

View file

@ -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";

View file

@ -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;

View file

@ -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";

Some files were not shown because too many files have changed in this diff Show more