Use error formatting in page views

This commit is contained in:
dominik-zeglen 2020-03-17 11:57:02 +01:00
parent 78b3feb656
commit e1f1a217da
12 changed files with 107 additions and 44 deletions

View file

@ -19,8 +19,8 @@ import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard"; import VisibilityCard from "@saleor/components/VisibilityCard";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { PageErrorFragment } from "@saleor/pages/types/PageErrorFragment";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { UserError } from "../../../types";
import { PageDetails_page } from "../../types/PageDetails"; import { PageDetails_page } from "../../types/PageDetails";
import PageInfo from "../PageInfo"; import PageInfo from "../PageInfo";
import PageSlug from "../PageSlug"; import PageSlug from "../PageSlug";
@ -37,7 +37,7 @@ export interface FormData {
export interface PageDetailsPageProps { export interface PageDetailsPageProps {
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: PageErrorFragment[];
page: PageDetails_page; page: PageDetails_page;
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;

View file

@ -9,16 +9,17 @@ import CardTitle from "@saleor/components/CardTitle";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import RichTextEditor from "@saleor/components/RichTextEditor"; import RichTextEditor from "@saleor/components/RichTextEditor";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { PageErrorFragment } from "@saleor/pages/types/PageErrorFragment";
import getPageErrorMessage from "@saleor/utils/errors/page";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { UserError } from "../../../types";
import { PageDetails_page } from "../../types/PageDetails"; import { PageDetails_page } from "../../types/PageDetails";
import { FormData } from "../PageDetailsPage"; import { FormData } from "../PageDetailsPage";
export interface PageInfoProps { export interface PageInfoProps {
data: FormData; data: FormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: PageErrorFragment[];
page: PageDetails_page; page: PageDetails_page;
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -34,10 +35,12 @@ const useStyles = makeStyles(
const PageInfo: React.FC<PageInfoProps> = props => { const PageInfo: React.FC<PageInfoProps> = props => {
const { data, disabled, errors, page, onChange } = props; const { data, disabled, errors, page, onChange } = props;
const classes = useStyles(props);
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["title", "contentJson"], errors);
return ( return (
<Card className={classes.root}> <Card className={classes.root}>
<CardTitle <CardTitle
@ -46,9 +49,9 @@ const PageInfo: React.FC<PageInfoProps> = props => {
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "title")} error={!!formErrors.title}
fullWidth fullWidth
helperText={getFieldError(errors, "title")?.message} helperText={getPageErrorMessage(formErrors.title, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Title", defaultMessage: "Title",
description: "page title" description: "page title"
@ -60,8 +63,8 @@ const PageInfo: React.FC<PageInfoProps> = props => {
<FormSpacer /> <FormSpacer />
<RichTextEditor <RichTextEditor
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "contentJson")} error={!!formErrors.contentJson}
helperText={getFieldError(errors, "contentJson")?.message} helperText={getPageErrorMessage(formErrors.contentJson, intl)}
initial={maybe(() => JSON.parse(page.contentJson))} initial={maybe(() => JSON.parse(page.contentJson))}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Content", defaultMessage: "Content",

View file

@ -6,14 +6,15 @@ import { useIntl } from "react-intl";
import slugify from "slugify"; import slugify from "slugify";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import getPageErrorMessage from "@saleor/utils/errors/page";
import { PageErrorFragment } from "@saleor/pages/types/PageErrorFragment";
import { FormData } from "../PageDetailsPage"; import { FormData } from "../PageDetailsPage";
export interface PageSlugProps { export interface PageSlugProps {
data: FormData; data: FormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: PageErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -25,6 +26,8 @@ const PageSlug: React.FC<PageSlugProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["slug"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -36,13 +39,13 @@ const PageSlug: React.FC<PageSlugProps> = ({
<TextField <TextField
name={"slug" as keyof FormData} name={"slug" as keyof FormData}
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "slug")} error={!!formErrors.slug}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Slug", defaultMessage: "Slug",
description: "page internal name" description: "page internal name"
})} })}
helperText={ helperText={
getFieldError(errors, "slug")?.message || getPageErrorMessage(formErrors.slug, intl) ||
intl.formatMessage({ intl.formatMessage({
defaultMessage: defaultMessage:
"If empty, URL will be autogenerated from Page Name" "If empty, URL will be autogenerated from Page Name"

View file

@ -14,13 +14,20 @@ import { PageCreate, PageCreateVariables } from "./types/PageCreate";
import { PageRemove, PageRemoveVariables } from "./types/PageRemove"; import { PageRemove, PageRemoveVariables } from "./types/PageRemove";
import { PageUpdate, PageUpdateVariables } from "./types/PageUpdate"; import { PageUpdate, PageUpdateVariables } from "./types/PageUpdate";
const pageErrorFragment = gql`
fragment PageErrorFragment on PageError {
code
field
}
`;
const pageCreate = gql` const pageCreate = gql`
${pageDetailsFragment} ${pageDetailsFragment}
${pageErrorFragment}
mutation PageCreate($input: PageInput!) { mutation PageCreate($input: PageInput!) {
pageCreate(input: $input) { pageCreate(input: $input) {
errors { errors: pageErrors {
field ...PageErrorFragment
message
} }
page { page {
...PageDetailsFragment ...PageDetailsFragment
@ -34,11 +41,11 @@ export const TypedPageCreate = TypedMutation<PageCreate, PageCreateVariables>(
const pageUpdate = gql` const pageUpdate = gql`
${pageDetailsFragment} ${pageDetailsFragment}
${pageErrorFragment}
mutation PageUpdate($id: ID!, $input: PageInput!) { mutation PageUpdate($id: ID!, $input: PageInput!) {
pageUpdate(id: $id, input: $input) { pageUpdate(id: $id, input: $input) {
errors { errors: pageErrors {
field ...PageErrorFragment
message
} }
page { page {
...PageDetailsFragment ...PageDetailsFragment
@ -51,11 +58,11 @@ export const TypedPageUpdate = TypedMutation<PageUpdate, PageUpdateVariables>(
); );
const pageRemove = gql` const pageRemove = gql`
${pageErrorFragment}
mutation PageRemove($id: ID!) { mutation PageRemove($id: ID!) {
pageDelete(id: $id) { pageDelete(id: $id) {
errors { errors: pageErrors {
field ...PageErrorFragment
message
} }
} }
} }

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { PageInput } from "./../../types/globalTypes"; import { PageInput, PageErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: PageCreate // GraphQL mutation operation: PageCreate
// ==================================================== // ====================================================
export interface PageCreate_pageCreate_errors { export interface PageCreate_pageCreate_errors {
__typename: "Error"; __typename: "PageError";
code: PageErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface PageCreate_pageCreate_page { export interface PageCreate_pageCreate_page {

View file

@ -0,0 +1,15 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { PageErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: PageErrorFragment
// ====================================================
export interface PageErrorFragment {
__typename: "PageError";
code: PageErrorCode;
field: string | null;
}

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { PageErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: PageRemove // GraphQL mutation operation: PageRemove
// ==================================================== // ====================================================
export interface PageRemove_pageDelete_errors { export interface PageRemove_pageDelete_errors {
__typename: "Error"; __typename: "PageError";
code: PageErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface PageRemove_pageDelete { export interface PageRemove_pageDelete {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { PageInput } from "./../../types/globalTypes"; import { PageInput, PageErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: PageUpdate // GraphQL mutation operation: PageUpdate
// ==================================================== // ====================================================
export interface PageUpdate_pageUpdate_errors { export interface PageUpdate_pageUpdate_errors {
__typename: "Error"; __typename: "PageError";
code: PageErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface PageUpdate_pageUpdate_page { export interface PageUpdate_pageUpdate_page {

View file

@ -4,7 +4,6 @@ import { useIntl } from "react-intl";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { maybe } from "../../misc";
import PageDetailsPage from "../components/PageDetailsPage"; import PageDetailsPage from "../components/PageDetailsPage";
import { TypedPageCreate } from "../mutations"; import { TypedPageCreate } from "../mutations";
import { PageCreate as PageCreateData } from "../types/PageCreate"; import { PageCreate as PageCreateData } from "../types/PageCreate";
@ -42,7 +41,7 @@ export const PageCreate: React.FC<PageCreateProps> = () => {
/> />
<PageDetailsPage <PageDetailsPage
disabled={pageCreateOpts.loading} disabled={pageCreateOpts.loading}
errors={maybe(() => pageCreateOpts.data.pageCreate.errors, [])} errors={pageCreateOpts.data?.pageCreate.errors || []}
saveButtonBarState={pageCreateOpts.status} saveButtonBarState={pageCreateOpts.status}
page={null} page={null}
onBack={() => navigate(pageListUrl())} onBack={() => navigate(pageListUrl())}

View file

@ -6,7 +6,8 @@ import ActionDialog from "@saleor/components/ActionDialog";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { maybe } from "../../misc"; import { commonMessages } from "@saleor/intl";
import { maybe, getStringOrPlaceholder } from "../../misc";
import { PageInput } from "../../types/globalTypes"; import { PageInput } from "../../types/globalTypes";
import PageDetailsPage, { FormData } from "../components/PageDetailsPage"; import PageDetailsPage, { FormData } from "../components/PageDetailsPage";
import { TypedPageRemove, TypedPageUpdate } from "../mutations"; import { TypedPageRemove, TypedPageUpdate } from "../mutations";
@ -43,9 +44,7 @@ export const PageDetails: React.FC<PageDetailsProps> = ({ id, params }) => {
const handlePageRemove = (data: PageRemove) => { const handlePageRemove = (data: PageRemove) => {
if (data.pageDelete.errors.length === 0) { if (data.pageDelete.errors.length === 0) {
notify({ notify({
text: intl.formatMessage({ text: intl.formatMessage(commonMessages.savedChanges)
defaultMessage: "Removed page"
})
}); });
navigate(pageListUrl()); navigate(pageListUrl());
} }
@ -63,12 +62,9 @@ export const PageDetails: React.FC<PageDetailsProps> = ({ id, params }) => {
/> />
<PageDetailsPage <PageDetailsPage
disabled={pageDetails.loading} disabled={pageDetails.loading}
errors={maybe( errors={pageUpdateOpts.data?.pageUpdate.errors || []}
() => pageUpdateOpts.data.pageUpdate.errors,
[]
)}
saveButtonBarState={pageUpdateOpts.status} saveButtonBarState={pageUpdateOpts.status}
page={maybe(() => pageDetails.data.page)} page={pageDetails.data?.page}
onBack={() => navigate(pageListUrl())} onBack={() => navigate(pageListUrl())}
onRemove={() => onRemove={() =>
navigate( navigate(
@ -104,7 +100,9 @@ export const PageDetails: React.FC<PageDetailsProps> = ({ id, params }) => {
values={{ values={{
title: ( title: (
<strong> <strong>
{maybe(() => pageDetails.data.page.title, "...")} {getStringOrPlaceholder(
pageDetails.data?.page?.title
)}
</strong> </strong>
) )
}} }}

View file

@ -519,6 +519,14 @@ export enum OrderStatusFilter {
UNFULFILLED = "UNFULFILLED", UNFULFILLED = "UNFULFILLED",
} }
export enum PageErrorCode {
GRAPHQL_ERROR = "GRAPHQL_ERROR",
INVALID = "INVALID",
NOT_FOUND = "NOT_FOUND",
REQUIRED = "REQUIRED",
UNIQUE = "UNIQUE",
}
export enum PageSortField { export enum PageSortField {
CREATION_DATE = "CREATION_DATE", CREATION_DATE = "CREATION_DATE",
PUBLICATION_DATE = "PUBLICATION_DATE", PUBLICATION_DATE = "PUBLICATION_DATE",

28
src/utils/errors/page.ts Normal file
View file

@ -0,0 +1,28 @@
import { IntlShape } from "react-intl";
import { PageErrorFragment } from "@saleor/pages/types/PageErrorFragment";
import { PageErrorCode } from "@saleor/types/globalTypes";
import { commonMessages } from "@saleor/intl";
import commonErrorMessages from "./common";
function getPageErrorMessage(
err: Omit<PageErrorFragment, "__typename"> | undefined,
intl: IntlShape
): string {
if (err) {
switch (err.code) {
case PageErrorCode.GRAPHQL_ERROR:
return intl.formatMessage(commonErrorMessages.graphqlError);
case PageErrorCode.REQUIRED:
return intl.formatMessage(commonMessages.requiredField);
case PageErrorCode.INVALID:
return intl.formatMessage(commonErrorMessages.invalid);
default:
return intl.formatMessage(commonErrorMessages.unknownError);
}
}
return undefined;
}
export default getPageErrorMessage;