diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index eb0eafe33..1b4c9abd9 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -286,6 +286,14 @@ export enum VoucherTypeEnum { SPECIFIC_PRODUCT = "SPECIFIC_PRODUCT", } +export enum WebhookErrorCode { + GRAPHQL_ERROR = "GRAPHQL_ERROR", + INVALID = "INVALID", + NOT_FOUND = "NOT_FOUND", + REQUIRED = "REQUIRED", + UNIQUE = "UNIQUE", +} + export enum WebhookEventTypeEnum { ANY_EVENTS = "ANY_EVENTS", CUSTOMER_CREATED = "CUSTOMER_CREATED", diff --git a/src/webhooks/components/WebhookCreatePage/WebhookCreatePage.tsx b/src/webhooks/components/WebhookCreatePage/WebhookCreatePage.tsx index 95568c615..4aaddff03 100644 --- a/src/webhooks/components/WebhookCreatePage/WebhookCreatePage.tsx +++ b/src/webhooks/components/WebhookCreatePage/WebhookCreatePage.tsx @@ -9,12 +9,12 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { SearchServiceAccount_search_edges_node } from "@saleor/containers/SearchServiceAccount/types/SearchServiceAccount"; import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; -import { UserError } from "@saleor/types"; import { WebhookEventTypeEnum } from "@saleor/types/globalTypes"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import WebhookEvents from "@saleor/webhooks/components/WebhookEvents"; import WebhookInfo from "@saleor/webhooks/components/WebhookInfo"; import WebhookStatus from "@saleor/webhooks/components/WebhookStatus"; +import { WebhookCreate_webhookCreate_webhookErrors } from "@saleor/webhooks/types/WebhookCreate"; import React from "react"; import { useIntl } from "react-intl"; @@ -31,7 +31,7 @@ export interface FormData { export interface WebhookCreatePageProps { disabled: boolean; - errors: UserError[]; + errors: WebhookCreate_webhookCreate_webhookErrors[]; services?: SearchServiceAccount_search_edges_node[]; saveButtonBarState: ConfirmButtonTransitionState; fetchServiceAccounts: (data: string) => void; @@ -39,9 +39,9 @@ export interface WebhookCreatePageProps { onSubmit: (data: FormData) => void; } -const WebhookCreatePage: React.StatelessComponent = ({ +const WebhookCreatePage: React.FC = ({ disabled, - errors, + errors: apiErrors, saveButtonBarState, services, fetchServiceAccounts, @@ -72,7 +72,7 @@ const WebhookCreatePage: React.StatelessComponent = ({ ); return ( -
+ {({ data, errors, hasChanged, submit, change }) => { const handleServiceSelect = createSingleAutocompleteSelectHandler( change, @@ -98,6 +98,7 @@ const WebhookCreatePage: React.StatelessComponent = ({ serviceDisplayValue={selectedServiceAcccount} services={servicesChoiceList} fetchServiceAccounts={fetchServiceAccounts} + apiErrors={apiErrors} errors={errors} serviceOnChange={handleServiceSelect} onChange={change} diff --git a/src/webhooks/components/WebhookCreatePage/WebhooksCreatePage.stories.tsx b/src/webhooks/components/WebhookCreatePage/WebhooksCreatePage.stories.tsx index dc9e5320c..a927deeef 100644 --- a/src/webhooks/components/WebhookCreatePage/WebhooksCreatePage.stories.tsx +++ b/src/webhooks/components/WebhookCreatePage/WebhooksCreatePage.stories.tsx @@ -2,7 +2,7 @@ import { storiesOf } from "@storybook/react"; import React from "react"; import Decorator from "@saleor/storybook/Decorator"; -import { formError } from "@saleor/storybook/misc"; +import { WebhookErrorCode } from "@saleor/types/globalTypes"; import WebhookCreatePage, { WebhookCreatePageProps } from "./WebhookCreatePage"; const props: WebhookCreatePageProps = { @@ -21,6 +21,15 @@ storiesOf("Views / Webhooks / Create webhook", module) .add("form errors", () => ( formError(field))} + errors={[ + { + code: WebhookErrorCode.INVALID, + field: null + } + ].map(error => ({ + __typename: "WebhookError", + message: "Generic form error", + ...error + }))} /> )); diff --git a/src/webhooks/components/WebhookInfo/WebhookInfo.tsx b/src/webhooks/components/WebhookInfo/WebhookInfo.tsx index 387f1d6dc..e1687dbe9 100644 --- a/src/webhooks/components/WebhookInfo/WebhookInfo.tsx +++ b/src/webhooks/components/WebhookInfo/WebhookInfo.tsx @@ -4,7 +4,7 @@ import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import makeStyles from "@material-ui/styles/makeStyles"; import React from "react"; -import { useIntl } from "react-intl"; +import { IntlShape, useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; @@ -15,9 +15,12 @@ import SingleAutocompleteSelectField, { import { ChangeEvent } from "@saleor/hooks/useForm"; import { commonMessages } from "@saleor/intl"; import { FormErrors } from "@saleor/types"; +import { WebhookErrorCode } from "@saleor/types/globalTypes"; +import { WebhookCreate_webhookCreate_webhookErrors } from "@saleor/webhooks/types/WebhookCreate"; import { FormData } from "../WebhooksDetailsPage"; interface WebhookInfoProps { + apiErrors: WebhookCreate_webhookCreate_webhookErrors[]; data: FormData; disabled: boolean; serviceDisplayValue: string; @@ -40,6 +43,7 @@ const useStyles = makeStyles(() => ({ })); const WebhookInfo: React.StatelessComponent = ({ + apiErrors, data, disabled, services, @@ -51,6 +55,18 @@ const WebhookInfo: React.StatelessComponent = ({ }) => { const classes = useStyles({}); const intl = useIntl(); + const translatedErrors = translateErrors(intl); + const serviceAccountsError = + apiErrors.filter(error => error.field === null).length > 0; + + function translateErrors(intl: IntlShape) { + return { + [WebhookErrorCode.INVALID]: intl.formatMessage({ + defaultMessage: "Missing token or serviceAccount", + description: "webhook service account error" + }) + }; + } return ( @@ -92,6 +108,14 @@ const WebhookInfo: React.StatelessComponent = ({ label={intl.formatMessage({ defaultMessage: "Assign to Service Account" })} + error={serviceAccountsError} + helperText={ + serviceAccountsError && + intl.formatMessage({ + defaultMessage: "Missing token or serviceAccount", + description: "webhook service account error" + }) + } name="serviceAccount" onChange={serviceOnChange} value={data.serviceAccount} @@ -136,6 +160,18 @@ const WebhookInfo: React.StatelessComponent = ({ value={data.secretKey} onChange={onChange} /> + {apiErrors.length > 0 && ( + <> + + {apiErrors + .filter(error => error.field === null) + .map(error => ( + + {translatedErrors[error.code]} + + ))} + + )} ); diff --git a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.stories.tsx b/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.stories.tsx index 11d17e6b5..5e30a9cc9 100644 --- a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.stories.tsx +++ b/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.stories.tsx @@ -2,7 +2,7 @@ import { storiesOf } from "@storybook/react"; import React from "react"; import Decorator from "@saleor/storybook/Decorator"; -import { formError } from "@saleor/storybook/misc"; +import { WebhookErrorCode } from "@saleor/types/globalTypes"; import WebhooksDetailsPage, { WebhooksDetailsPageProps } from "./WebhooksDetailsPage"; @@ -32,6 +32,15 @@ storiesOf("Views / Webhooks / Webhook details", module) .add("form errors", () => ( formError(field))} + errors={[ + { + code: WebhookErrorCode.INVALID, + field: null + } + ].map(error => ({ + __typename: "WebhookError", + message: "Generic form error", + ...error + }))} /> )); diff --git a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx b/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx index 9d6c54a18..ba88c0932 100644 --- a/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx +++ b/src/webhooks/components/WebhooksDetailsPage/WebhooksDetailsPage.tsx @@ -10,12 +10,12 @@ import { SearchServiceAccount_search_edges_node } from "@saleor/containers/Searc import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; -import { UserError } from "@saleor/types"; import { WebhookEventTypeEnum } from "@saleor/types/globalTypes"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import WebhookEvents from "@saleor/webhooks/components/WebhookEvents"; import WebhookInfo from "@saleor/webhooks/components/WebhookInfo"; import WebhookStatus from "@saleor/webhooks/components/WebhookStatus"; +import { WebhookCreate_webhookCreate_webhookErrors } from "@saleor/webhooks/types/WebhookCreate"; import { WebhookDetails_webhook } from "@saleor/webhooks/types/WebhookDetails"; import React from "react"; @@ -34,7 +34,7 @@ export interface FormData { export interface WebhooksDetailsPageProps { disabled: boolean; - errors: UserError[]; + errors: WebhookCreate_webhookCreate_webhookErrors[]; webhook: WebhookDetails_webhook; services?: SearchServiceAccount_search_edges_node[]; saveButtonBarState: ConfirmButtonTransitionState; @@ -46,7 +46,7 @@ export interface WebhooksDetailsPageProps { const WebhooksDetailsPage: React.FC = ({ disabled, - errors, + errors: apiErrors, webhook, saveButtonBarState, services, @@ -83,7 +83,7 @@ const WebhooksDetailsPage: React.FC = ({ [] ); return ( - + {({ data, errors, hasChanged, submit, change }) => { const handleServiceSelect = createSingleAutocompleteSelectHandler( change, @@ -109,6 +109,7 @@ const WebhooksDetailsPage: React.FC = ({
{ - if (data.webhookCreate.errors.length === 0) { + if (data.webhookCreate.webhookErrors.length === 0) { notify({ text: intl.formatMessage(commonMessages.savedChanges) }); @@ -64,7 +64,7 @@ export const WebhooksCreate: React.StatelessComponent< const formTransitionState = getMutationState( webhookCreateOpts.called, webhookCreateOpts.loading, - maybe(() => webhookCreateOpts.data.webhookCreate.errors) + maybe(() => webhookCreateOpts.data.webhookCreate.webhookErrors) ); return ( @@ -78,7 +78,7 @@ export const WebhooksCreate: React.StatelessComponent< webhookCreateOpts.data.webhookCreate.errors, + () => webhookCreateOpts.data.webhookCreate.webhookErrors, [] )} fetchServiceAccounts={searchServiceAccount} diff --git a/src/webhooks/views/WebhooksDetails.tsx b/src/webhooks/views/WebhooksDetails.tsx index c7fffc2f6..68b231851 100644 --- a/src/webhooks/views/WebhooksDetails.tsx +++ b/src/webhooks/views/WebhooksDetails.tsx @@ -63,7 +63,7 @@ export const WebhooksDetails: React.FC = ({ }; const onWebhookUpdate = (data: WebhookUpdate) => { - if (data.webhookUpdate.errors.length === 0) { + if (data.webhookUpdate.webhookErrors.length === 0) { notify({ text: intl.formatMessage(commonMessages.savedChanges) }); @@ -83,7 +83,9 @@ export const WebhooksDetails: React.FC = ({ const formTransitionState = getMutationState( webhookUpdateOpts.called, webhookUpdateOpts.loading, - maybe(() => webhookUpdateOpts.data.webhookUpdate.errors) + maybe( + () => webhookUpdateOpts.data.webhookUpdate.webhookErrors + ) ); const handleRemoveConfirm = () => @@ -94,7 +96,7 @@ export const WebhooksDetails: React.FC = ({ }); const formErrors = maybe( - () => webhookUpdateOpts.data.webhookUpdate.errors, + () => webhookUpdateOpts.data.webhookUpdate.webhookErrors, [] );