Revert "Use error formatting in product type section"

This commit is contained in:
Dominik Żegleń 2020-03-11 10:56:04 +01:00 committed by GitHub
parent 90e5b317d4
commit bf92214fc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 417 additions and 466 deletions

View file

@ -10,17 +10,16 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import SingleSelectField from "@saleor/components/SingleSelectField"; import SingleSelectField from "@saleor/components/SingleSelectField";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
import { getProductErrorMessage, getFormErrors } from "@saleor/utils/errors"; import { getFieldError } from "@saleor/utils/errors";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { AttributePageFormData } from "../AttributePage"; import { AttributePageFormData } from "../AttributePage";
import { getAttributeSlugErrorMessage } from "../../errors";
export interface AttributeDetailsProps { export interface AttributeDetailsProps {
canChangeType: boolean; canChangeType: boolean;
data: AttributePageFormData; data: AttributePageFormData;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -49,8 +48,6 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
} }
]; ];
const formErrors = getFormErrors(["name", "slug", "inputType"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -59,21 +56,21 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.name} error={!!getFieldError(errors, "name")}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Default Label", defaultMessage: "Default Label",
description: "attribute's label" description: "attribute's label"
})} })}
name={"name" as keyof AttributePageFormData} name={"name" as keyof AttributePageFormData}
fullWidth fullWidth
helperText={getProductErrorMessage(formErrors.name, intl)} helperText={getFieldError(errors, "name")?.message}
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer /> <FormSpacer />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.slug} error={!!getFieldError(errors, "slug")}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Attribute Code", defaultMessage: "Attribute Code",
description: "attribute's slug short code label" description: "attribute's slug short code label"
@ -82,7 +79,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
placeholder={slugify(data.name).toLowerCase()} placeholder={slugify(data.name).toLowerCase()}
fullWidth fullWidth
helperText={ helperText={
getAttributeSlugErrorMessage(formErrors.slug, intl) || getFieldError(errors, "slug")?.message ||
intl.formatMessage({ intl.formatMessage({
defaultMessage: defaultMessage:
"This is used internally. Make sure you dont use spaces", "This is used internally. Make sure you dont use spaces",
@ -96,8 +93,8 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<SingleSelectField <SingleSelectField
choices={inputTypeChoices} choices={inputTypeChoices}
disabled={disabled || !canChangeType} disabled={disabled || !canChangeType}
error={!!formErrors.inputType} error={!!getFieldError(errors, "inputType")}
hint={getProductErrorMessage(formErrors.inputType, intl)} hint={getFieldError(errors, "inputType")?.message}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Catalog Input type for Store Owner", defaultMessage: "Catalog Input type for Store Owner",
description: "attribute's editor component" description: "attribute's editor component"

View file

@ -12,9 +12,8 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ReorderAction } from "@saleor/types"; import { ReorderAction, UserError } from "@saleor/types";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { import {
AttributeDetailsFragment, AttributeDetailsFragment,
AttributeDetailsFragment_values AttributeDetailsFragment_values
@ -26,7 +25,7 @@ import AttributeValues from "../AttributeValues";
export interface AttributePageProps { export interface AttributePageProps {
attribute: AttributeDetailsFragment | null; attribute: AttributeDetailsFragment | null;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: UserError[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
values: AttributeDetailsFragment_values[]; values: AttributeDetailsFragment_values[];
onBack: () => void; onBack: () => void;

View file

@ -11,14 +11,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; import { UserError } from "@saleor/types";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { getFieldError } from "@saleor/utils/errors";
import { AttributePageFormData } from "../AttributePage"; import { AttributePageFormData } from "../AttributePage";
export interface AttributePropertiesProps { export interface AttributePropertiesProps {
data: AttributePageFormData; data: AttributePageFormData;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -30,8 +30,6 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["storefrontSearchPosition"], errors);
return ( return (
<Card> <Card>
<CardTitle title={intl.formatMessage(commonMessages.properties)} /> <CardTitle title={intl.formatMessage(commonMessages.properties)} />
@ -88,12 +86,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
{data.filterableInStorefront && ( {data.filterableInStorefront && (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.storefrontSearchPosition} error={!!getFieldError(errors, "storefrontSearchPosition")}
fullWidth fullWidth
helperText={getProductErrorMessage( helperText={
formErrors.storefrontSearchPosition, getFieldError(errors, "storefrontSearchPosition")?.message
intl }
)}
name={"storefrontSearchPosition" as keyof AttributePageFormData} name={"storefrontSearchPosition" as keyof AttributePageFormData}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Position in faceted navigation", defaultMessage: "Position in faceted navigation",

View file

@ -14,9 +14,8 @@ import Form from "@saleor/components/Form";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { getFormErrors } from "@saleor/utils/errors"; import { UserError } from "@saleor/types";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { getFieldError } from "@saleor/utils/errors";
import { getAttributeValueErrorMessage } from "@saleor/attributes/errors";
import { AttributeDetails_attribute_values } from "../../types/AttributeDetails"; import { AttributeDetails_attribute_values } from "../../types/AttributeDetails";
export interface AttributeValueEditDialogFormData { export interface AttributeValueEditDialogFormData {
@ -26,7 +25,7 @@ export interface AttributeValueEditDialogProps {
attributeValue: AttributeDetails_attribute_values | null; attributeValue: AttributeDetails_attribute_values | null;
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: UserError[];
open: boolean; open: boolean;
onSubmit: (data: AttributeValueEditDialogFormData) => void; onSubmit: (data: AttributeValueEditDialogFormData) => void;
onClose: () => void; onClose: () => void;
@ -46,7 +45,6 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
name: maybe(() => attributeValue.name, "") name: maybe(() => attributeValue.name, "")
}; };
const errors = useModalDialogErrors(apiErrors, open); const errors = useModalDialogErrors(apiErrors, open);
const formErrors = getFormErrors(["name"], errors);
return ( return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm"> <Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
@ -70,12 +68,9 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
<TextField <TextField
autoFocus autoFocus
disabled={disabled} disabled={disabled}
error={!!formErrors.name} error={!!getFieldError(errors, "name")}
fullWidth fullWidth
helperText={getAttributeValueErrorMessage( helperText={getFieldError(errors, "name")?.message}
formErrors.name,
intl
)}
name={"name" as keyof AttributeValueEditDialogFormData} name={"name" as keyof AttributeValueEditDialogFormData}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Name", defaultMessage: "Name",

View file

@ -1,38 +0,0 @@
import { IntlShape, defineMessages } from "react-intl";
import { ProductErrorCode } from "@saleor/types/globalTypes";
import { getProductErrorMessage } from "@saleor/utils/errors";
import { ProductErrorFragment } from "./types/ProductErrorFragment";
const messages = defineMessages({
attributeSlugUnique: {
defaultMessage: "Attribute with this slug already exists"
},
attributeValueAlreadyExists: {
defaultMessage: "This value already exists within this attribute"
}
});
export function getAttributeSlugErrorMessage(
err: ProductErrorFragment,
intl: IntlShape
): string {
switch (err?.code) {
case ProductErrorCode.UNIQUE:
return intl.formatMessage(messages.attributeSlugUnique);
default:
return getProductErrorMessage(err, intl);
}
}
export function getAttributeValueErrorMessage(
err: ProductErrorFragment,
intl: IntlShape
): string {
switch (err?.code) {
case ProductErrorCode.ALREADY_EXISTS:
return intl.formatMessage(messages.attributeValueAlreadyExists);
default:
return getProductErrorMessage(err, intl);
}
}

View file

@ -35,19 +35,12 @@ import {
AttributeValueUpdateVariables AttributeValueUpdateVariables
} from "./types/AttributeValueUpdate"; } from "./types/AttributeValueUpdate";
export const productErrorFragment = gql`
fragment ProductErrorFragment on ProductError {
code
field
}
`;
const attributeBulkDelete = gql` const attributeBulkDelete = gql`
${productErrorFragment}
mutation AttributeBulkDelete($ids: [ID!]!) { mutation AttributeBulkDelete($ids: [ID!]!) {
attributeBulkDelete(ids: $ids) { attributeBulkDelete(ids: $ids) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
} }
} }
@ -58,11 +51,11 @@ export const AttributeBulkDeleteMutation = TypedMutation<
>(attributeBulkDelete); >(attributeBulkDelete);
const attributeDelete = gql` const attributeDelete = gql`
${productErrorFragment}
mutation AttributeDelete($id: ID!) { mutation AttributeDelete($id: ID!) {
attributeDelete(id: $id) { attributeDelete(id: $id) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
} }
} }
@ -74,15 +67,15 @@ export const AttributeDeleteMutation = TypedMutation<
export const attributeUpdateMutation = gql` export const attributeUpdateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) { mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) {
attributeUpdate(id: $id, input: $input) { attributeUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -93,15 +86,15 @@ export const AttributeUpdateMutation = TypedMutation<
const attributeValueDelete = gql` const attributeValueDelete = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueDelete($id: ID!) { mutation AttributeValueDelete($id: ID!) {
attributeValueDelete(id: $id) { attributeValueDelete(id: $id) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -112,15 +105,15 @@ export const AttributeValueDeleteMutation = TypedMutation<
export const attributeValueUpdateMutation = gql` export const attributeValueUpdateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) { mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueUpdate(id: $id, input: $input) { attributeValueUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -131,15 +124,15 @@ export const AttributeValueUpdateMutation = TypedMutation<
export const attributeValueCreateMutation = gql` export const attributeValueCreateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) { mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueCreate(attribute: $id, input: $input) { attributeValueCreate(attribute: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -150,15 +143,15 @@ export const AttributeValueCreateMutation = TypedMutation<
export const attributeCreateMutation = gql` export const attributeCreateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeCreate($input: AttributeCreateInput!) { mutation AttributeCreate($input: AttributeCreateInput!) {
attributeCreate(input: $input) { attributeCreate(input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -168,18 +161,18 @@ export const AttributeCreateMutation = TypedMutation<
>(attributeCreateMutation); >(attributeCreateMutation);
const attributeValueReorderMutation = gql` const attributeValueReorderMutation = gql`
${productErrorFragment}
mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) { mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) {
attributeReorderValues(attributeId: $id, moves: [$move]) { attributeReorderValues(attributeId: $id, moves: [$move]) {
errors {
field
message
}
attribute { attribute {
id id
values { values {
id id
} }
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;

View file

@ -2,16 +2,14 @@
/* 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 { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeBulkDelete // GraphQL mutation operation: AttributeBulkDelete
// ==================================================== // ====================================================
export interface AttributeBulkDelete_attributeBulkDelete_errors { export interface AttributeBulkDelete_attributeBulkDelete_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface AttributeBulkDelete_attributeBulkDelete { export interface AttributeBulkDelete_attributeBulkDelete {

View file

@ -2,12 +2,18 @@
/* 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 { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeCreate // GraphQL mutation operation: AttributeCreate
// ==================================================== // ====================================================
export interface AttributeCreate_attributeCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeCreate_attributeCreate_attribute_values { export interface AttributeCreate_attributeCreate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -31,16 +37,10 @@ export interface AttributeCreate_attributeCreate_attribute {
values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null;
} }
export interface AttributeCreate_attributeCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeCreate_attributeCreate { export interface AttributeCreate_attributeCreate {
__typename: "AttributeCreate"; __typename: "AttributeCreate";
attribute: AttributeCreate_attributeCreate_attribute | null;
errors: AttributeCreate_attributeCreate_errors[]; errors: AttributeCreate_attributeCreate_errors[];
attribute: AttributeCreate_attributeCreate_attribute | null;
} }
export interface AttributeCreate { export interface AttributeCreate {

View file

@ -2,16 +2,14 @@
/* 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 { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeDelete // GraphQL mutation operation: AttributeDelete
// ==================================================== // ====================================================
export interface AttributeDelete_attributeDelete_errors { export interface AttributeDelete_attributeDelete_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface AttributeDelete_attributeDelete { export interface AttributeDelete_attributeDelete {

View file

@ -2,12 +2,18 @@
/* 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 { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeUpdate // GraphQL mutation operation: AttributeUpdate
// ==================================================== // ====================================================
export interface AttributeUpdate_attributeUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeUpdate_attributeUpdate_attribute_values { export interface AttributeUpdate_attributeUpdate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -31,16 +37,10 @@ export interface AttributeUpdate_attributeUpdate_attribute {
values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null;
} }
export interface AttributeUpdate_attributeUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeUpdate_attributeUpdate { export interface AttributeUpdate_attributeUpdate {
__typename: "AttributeUpdate"; __typename: "AttributeUpdate";
attribute: AttributeUpdate_attributeUpdate_attribute | null;
errors: AttributeUpdate_attributeUpdate_errors[]; errors: AttributeUpdate_attributeUpdate_errors[];
attribute: AttributeUpdate_attributeUpdate_attribute | null;
} }
export interface AttributeUpdate { export interface AttributeUpdate {

View file

@ -2,12 +2,18 @@
/* 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 { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueCreate // GraphQL mutation operation: AttributeValueCreate
// ==================================================== // ====================================================
export interface AttributeValueCreate_attributeValueCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueCreate_attributeValueCreate_attribute_values { export interface AttributeValueCreate_attributeValueCreate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -31,16 +37,10 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null;
} }
export interface AttributeValueCreate_attributeValueCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueCreate_attributeValueCreate { export interface AttributeValueCreate_attributeValueCreate {
__typename: "AttributeValueCreate"; __typename: "AttributeValueCreate";
attribute: AttributeValueCreate_attributeValueCreate_attribute | null;
errors: AttributeValueCreate_attributeValueCreate_errors[]; errors: AttributeValueCreate_attributeValueCreate_errors[];
attribute: AttributeValueCreate_attributeValueCreate_attribute | null;
} }
export interface AttributeValueCreate { export interface AttributeValueCreate {

View file

@ -2,12 +2,18 @@
/* 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 { AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueDelete // GraphQL mutation operation: AttributeValueDelete
// ==================================================== // ====================================================
export interface AttributeValueDelete_attributeValueDelete_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueDelete_attributeValueDelete_attribute_values { export interface AttributeValueDelete_attributeValueDelete_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -31,16 +37,10 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null;
} }
export interface AttributeValueDelete_attributeValueDelete_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueDelete_attributeValueDelete { export interface AttributeValueDelete_attributeValueDelete {
__typename: "AttributeValueDelete"; __typename: "AttributeValueDelete";
attribute: AttributeValueDelete_attributeValueDelete_attribute | null;
errors: AttributeValueDelete_attributeValueDelete_errors[]; errors: AttributeValueDelete_attributeValueDelete_errors[];
attribute: AttributeValueDelete_attributeValueDelete_attribute | null;
} }
export interface AttributeValueDelete { export interface AttributeValueDelete {

View file

@ -2,12 +2,18 @@
/* 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 { ReorderInput, ProductErrorCode } from "./../../types/globalTypes"; import { ReorderInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueReorder // GraphQL mutation operation: AttributeValueReorder
// ==================================================== // ====================================================
export interface AttributeValueReorder_attributeReorderValues_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueReorder_attributeReorderValues_attribute_values { export interface AttributeValueReorder_attributeReorderValues_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -19,16 +25,10 @@ export interface AttributeValueReorder_attributeReorderValues_attribute {
values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null; values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null;
} }
export interface AttributeValueReorder_attributeReorderValues_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueReorder_attributeReorderValues { export interface AttributeValueReorder_attributeReorderValues {
__typename: "AttributeReorderValues"; __typename: "AttributeReorderValues";
attribute: AttributeValueReorder_attributeReorderValues_attribute | null;
errors: AttributeValueReorder_attributeReorderValues_errors[]; errors: AttributeValueReorder_attributeReorderValues_errors[];
attribute: AttributeValueReorder_attributeReorderValues_attribute | null;
} }
export interface AttributeValueReorder { export interface AttributeValueReorder {

View file

@ -2,12 +2,18 @@
/* 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 { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeValueUpdate // GraphQL mutation operation: AttributeValueUpdate
// ==================================================== // ====================================================
export interface AttributeValueUpdate_attributeValueUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueUpdate_attributeValueUpdate_attribute_values { export interface AttributeValueUpdate_attributeValueUpdate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -31,16 +37,10 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null;
} }
export interface AttributeValueUpdate_attributeValueUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueUpdate_attributeValueUpdate { export interface AttributeValueUpdate_attributeValueUpdate {
__typename: "AttributeValueUpdate"; __typename: "AttributeValueUpdate";
attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null;
errors: AttributeValueUpdate_attributeValueUpdate_errors[]; errors: AttributeValueUpdate_attributeValueUpdate_errors[];
attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null;
} }
export interface AttributeValueUpdate { export interface AttributeValueUpdate {

View file

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

View file

@ -5,7 +5,7 @@ import slugify from "slugify";
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 "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ReorderEvent } from "@saleor/types"; import { ReorderEvent, UserError } from "@saleor/types";
import { import {
add, add,
isSelected, isSelected,
@ -14,8 +14,6 @@ import {
updateAtIndex updateAtIndex
} from "@saleor/utils/lists"; } from "@saleor/utils/lists";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { ProductErrorCode } from "@saleor/types/globalTypes";
import AttributePage from "../../components/AttributePage"; import AttributePage from "../../components/AttributePage";
import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog";
import AttributeValueEditDialog, { import AttributeValueEditDialog, {
@ -35,12 +33,6 @@ interface AttributeDetailsProps {
params: AttributeAddUrlQueryParams; params: AttributeAddUrlQueryParams;
} }
const attributeValueAlreadyExistsError: ProductErrorFragment = {
__typename: "ProductError",
code: ProductErrorCode.ALREADY_EXISTS,
field: "name"
};
function areValuesEqual( function areValuesEqual(
a: AttributeValueEditDialogFormData, a: AttributeValueEditDialogFormData,
b: AttributeValueEditDialogFormData b: AttributeValueEditDialogFormData
@ -56,9 +48,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
const [values, setValues] = React.useState< const [values, setValues] = React.useState<
AttributeValueEditDialogFormData[] AttributeValueEditDialogFormData[]
>([]); >([]);
const [valueErrors, setValueErrors] = React.useState<ProductErrorFragment[]>( const [valueErrors, setValueErrors] = React.useState<UserError[]>([]);
[]
);
const id = params.id ? parseInt(params.id, 0) : undefined; const id = params.id ? parseInt(params.id, 0) : undefined;
@ -67,8 +57,6 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
AttributeAddUrlQueryParams AttributeAddUrlQueryParams
>(navigate, attributeAddUrl, params); >(navigate, attributeAddUrl, params);
React.useEffect(() => setValueErrors([]), [params.action]);
const handleValueDelete = () => { const handleValueDelete = () => {
setValues(remove(values[params.id], values, areValuesEqual)); setValues(remove(values[params.id], values, areValuesEqual));
closeModal(); closeModal();
@ -85,7 +73,20 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
}; };
const handleValueUpdate = (input: AttributeValueEditDialogFormData) => { const handleValueUpdate = (input: AttributeValueEditDialogFormData) => {
if (isSelected(input, values, areValuesEqual)) { if (isSelected(input, values, areValuesEqual)) {
setValueErrors([attributeValueAlreadyExistsError]); setValueErrors([
{
field: "name",
message: intl.formatMessage(
{
defaultMessage: "A value named {name} already exists",
description: "attribute value edit error"
},
{
name: input.name
}
)
}
]);
} else { } else {
setValues(updateAtIndex(input, values, id)); setValues(updateAtIndex(input, values, id));
closeModal(); closeModal();
@ -93,7 +94,20 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
}; };
const handleValueCreate = (input: AttributeValueEditDialogFormData) => { const handleValueCreate = (input: AttributeValueEditDialogFormData) => {
if (isSelected(input, values, areValuesEqual)) { if (isSelected(input, values, areValuesEqual)) {
setValueErrors([attributeValueAlreadyExistsError]); setValueErrors([
{
field: "name",
message: intl.formatMessage(
{
defaultMessage: "A value named {name} already exists",
description: "attribute value edit error"
},
{
name: input.name
}
)
}
]);
} else { } else {
setValues(add(input, values)); setValues(add(input, values));
closeModal(); closeModal();
@ -109,7 +123,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
<AttributePage <AttributePage
attribute={null} attribute={null}
disabled={false} disabled={false}
errors={attributeCreateOpts.data?.attributeCreate.errors || []} errors={maybe(
() => attributeCreateOpts.data.attributeCreate.errors,
[]
)}
onBack={() => navigate(attributeListUrl())} onBack={() => navigate(attributeListUrl())}
onDelete={undefined} onDelete={undefined}
onSubmit={input => onSubmit={input =>

View file

@ -8,7 +8,6 @@ import { maybe } from "@saleor/misc";
import { ReorderEvent } from "@saleor/types"; import { ReorderEvent } from "@saleor/types";
import { move } from "@saleor/utils/lists"; import { move } from "@saleor/utils/lists";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { getProductErrorMessage } from "@saleor/utils/errors";
import AttributeDeleteDialog from "../../components/AttributeDeleteDialog"; import AttributeDeleteDialog from "../../components/AttributeDeleteDialog";
import AttributePage from "../../components/AttributePage"; import AttributePage from "../../components/AttributePage";
import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog";
@ -96,10 +95,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const handleValueReorderMutation = (data: AttributeValueReorder) => { const handleValueReorderMutation = (data: AttributeValueReorder) => {
if (data.attributeReorderValues.errors.length !== 0) { if (data.attributeReorderValues.errors.length !== 0) {
notify({ notify({
text: getProductErrorMessage( text: data.attributeReorderValues.errors[0].message
data.attributeReorderValues.errors[0],
intl
)
}); });
} }
}; };
@ -159,10 +155,12 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
<AttributePage <AttributePage
attribute={maybe(() => data.attribute)} attribute={maybe(() => data.attribute)}
disabled={loading} disabled={loading}
errors={ errors={maybe(
() =>
attributeUpdateOpts.data attributeUpdateOpts.data
?.attributeUpdate.errors || [] .attributeUpdate.errors,
} []
)}
onBack={() => onBack={() =>
navigate(attributeListUrl()) navigate(attributeListUrl())
} }
@ -255,10 +253,12 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueCreateOpts.status attributeValueCreateOpts.status
} }
disabled={loading} disabled={loading}
errors={ errors={maybe(
() =>
attributeValueCreateOpts.data attributeValueCreateOpts.data
?.attributeValueCreate.errors || [] .attributeValueCreate.errors,
} []
)}
open={params.action === "add-value"} open={params.action === "add-value"}
onClose={closeModal} onClose={closeModal}
onSubmit={input => onSubmit={input =>
@ -280,10 +280,12 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueUpdateOpts.status attributeValueUpdateOpts.status
} }
disabled={loading} disabled={loading}
errors={ errors={maybe(
() =>
attributeValueUpdateOpts.data attributeValueUpdateOpts.data
?.attributeValueUpdate.errors || [] .attributeValueUpdate.errors,
} []
)}
open={params.action === "edit-value"} open={params.action === "edit-value"}
onClose={closeModal} onClose={closeModal}
onSubmit={input => onSubmit={input =>

View file

@ -30,8 +30,6 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc"; import { maybe, renderCollection } from "@saleor/misc";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { getProductErrorMessage } from "@saleor/utils/errors";
import { SearchAttributes_productType_availableAttributes_edges_node } from "../../hooks/useAvailableAttributeSearch/types/SearchAttributes"; import { SearchAttributes_productType_availableAttributes_edges_node } from "../../hooks/useAvailableAttributeSearch/types/SearchAttributes";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -63,7 +61,7 @@ const useStyles = makeStyles(
export interface AssignAttributeDialogProps extends FetchMoreProps { export interface AssignAttributeDialogProps extends FetchMoreProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
errors: ProductErrorFragment[]; errors: string[];
open: boolean; open: boolean;
attributes: SearchAttributes_productType_availableAttributes_edges_node[]; attributes: SearchAttributes_productType_availableAttributes_edges_node[];
selected: string[]; selected: string[];
@ -190,7 +188,7 @@ const AssignAttributeDialog: React.FC<AssignAttributeDialogProps> = ({
<DialogContent> <DialogContent>
{errors.map((error, errorIndex) => ( {errors.map((error, errorIndex) => (
<DialogContentText color="error" key={errorIndex}> <DialogContentText color="error" key={errorIndex}>
{getProductErrorMessage(error, intl)} {error}
</DialogContentText> </DialogContentText>
))} ))}
</DialogContent> </DialogContent>

View file

@ -0,0 +1,112 @@
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Form from "@saleor/components/Form";
import { FormSpacer } from "@saleor/components/FormSpacer";
import ListField from "@saleor/components/ListField";
import { buttonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
export interface FormData {
name: string;
values: Array<{
label: string;
value: string;
}>;
}
export interface ProductTypeAttributeEditDialogProps {
disabled: boolean;
errors: UserError[];
name: string;
opened: boolean;
title: string;
values: Array<{
label: string;
value: string;
}>;
onClose: () => void;
onConfirm: (data: FormData) => void;
}
const ProductTypeAttributeEditDialog: React.FC<ProductTypeAttributeEditDialogProps> = ({
disabled,
errors,
name,
opened,
title,
values,
onClose,
onConfirm
}) => {
const intl = useIntl();
const initialForm: FormData = {
name: name || "",
values: values || []
};
return (
<Dialog onClose={onClose} open={opened}>
<Form initial={initialForm} onSubmit={onConfirm}>
{({ change, data }) => (
<>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<TextField
disabled={disabled}
error={!!getFieldError(errors, "name")}
fullWidth
label={intl.formatMessage({
defaultMessage: "Attribute name"
})}
helperText={getFieldError(errors, "name")?.message}
name="name"
value={data.name}
onChange={change}
/>
<FormSpacer />
<ListField
autoComplete="off"
disabled={disabled}
error={
!!getFieldError(errors, "values") ||
!!getFieldError(errors, "addValues") ||
!!getFieldError(errors, "removeValues")
}
fullWidth
name="values"
label={intl.formatMessage({
defaultMessage: "Attribute values"
})}
helperText={
getFieldError(errors, "values") ||
getFieldError(errors, "addValues") ||
getFieldError(errors, "removeValues")
}
values={data.values}
onChange={change}
/>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<Button color="primary" variant="contained" type="submit">
<FormattedMessage {...buttonMessages.confirm} />
</Button>
</DialogActions>
</>
)}
</Form>
</Dialog>
);
};
ProductTypeAttributeEditDialog.displayName = "ProductTypeAttributeEditDialog";
export default ProductTypeAttributeEditDialog;

View file

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

View file

@ -13,8 +13,8 @@ import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { ProductTypeDetails_taxTypes } from "@saleor/productTypes/types/ProductTypeDetails"; import { ProductTypeDetails_taxTypes } from "@saleor/productTypes/types/ProductTypeDetails";
import { UserError } from "@saleor/types";
import { WeightUnitsEnum } from "@saleor/types/globalTypes"; import { WeightUnitsEnum } from "@saleor/types/globalTypes";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import ProductTypeDetails from "../ProductTypeDetails/ProductTypeDetails"; import ProductTypeDetails from "../ProductTypeDetails/ProductTypeDetails";
import ProductTypeShipping from "../ProductTypeShipping/ProductTypeShipping"; import ProductTypeShipping from "../ProductTypeShipping/ProductTypeShipping";
import ProductTypeTaxes from "../ProductTypeTaxes/ProductTypeTaxes"; import ProductTypeTaxes from "../ProductTypeTaxes/ProductTypeTaxes";
@ -27,7 +27,7 @@ export interface ProductTypeForm {
} }
export interface ProductTypeCreatePageProps { export interface ProductTypeCreatePageProps {
errors: ProductErrorFragment[]; errors: UserError[];
defaultWeightUnit: WeightUnitsEnum; defaultWeightUnit: WeightUnitsEnum;
disabled: boolean; disabled: boolean;
pageTitle: string; pageTitle: string;
@ -106,7 +106,6 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
disabled={disabled} disabled={disabled}
data={data} data={data}
defaultWeightUnit={defaultWeightUnit} defaultWeightUnit={defaultWeightUnit}
errors={errors}
onChange={change} onChange={change}
/> />
</div> </div>

View file

@ -7,8 +7,8 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { UserError } from "@saleor/types";
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; import { getFieldError } from "@saleor/utils/errors";
const useStyles = makeStyles( const useStyles = makeStyles(
{ {
@ -24,17 +24,15 @@ interface ProductTypeDetailsProps {
name: string; name: string;
}; };
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[]; errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const ProductTypeDetails: React.FC<ProductTypeDetailsProps> = props => { const ProductTypeDetails: React.FC<ProductTypeDetailsProps> = props => {
const { data, disabled, errors, onChange } = props; const { data, disabled, errors, onChange } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl();
const formErrors = getFormErrors(["name"], errors); const intl = useIntl();
return ( return (
<Card className={classes.root}> <Card className={classes.root}>
@ -44,9 +42,9 @@ const ProductTypeDetails: React.FC<ProductTypeDetailsProps> = props => {
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.name} error={!!getFieldError(errors, "name")}
fullWidth fullWidth
helperText={getProductErrorMessage(formErrors.name, intl)} helperText={getFieldError(errors, "name")?.message}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Product Type Name" defaultMessage: "Product Type Name"
})} })}

View file

@ -14,9 +14,8 @@ import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ListActions, ReorderEvent } from "@saleor/types"; import { ListActions, ReorderEvent, UserError } from "@saleor/types";
import { AttributeTypeEnum, WeightUnitsEnum } from "@saleor/types/globalTypes"; import { AttributeTypeEnum, WeightUnitsEnum } from "@saleor/types/globalTypes";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { import {
ProductTypeDetails_productType, ProductTypeDetails_productType,
ProductTypeDetails_taxTypes ProductTypeDetails_taxTypes
@ -42,7 +41,7 @@ export interface ProductTypeForm {
} }
export interface ProductTypeDetailsPageProps { export interface ProductTypeDetailsPageProps {
errors: ProductErrorFragment[]; errors: UserError[];
productType: ProductTypeDetails_productType; productType: ProductTypeDetails_productType;
defaultWeightUnit: WeightUnitsEnum; defaultWeightUnit: WeightUnitsEnum;
disabled: boolean; disabled: boolean;
@ -201,7 +200,6 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
disabled={disabled} disabled={disabled}
data={data} data={data}
defaultWeightUnit={defaultWeightUnit} defaultWeightUnit={defaultWeightUnit}
errors={errors}
onChange={change} onChange={change}
/> />
</div> </div>

View file

@ -6,8 +6,6 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
import { WeightUnitsEnum } from "../../../types/globalTypes"; import { WeightUnitsEnum } from "../../../types/globalTypes";
interface ProductTypeShippingProps { interface ProductTypeShippingProps {
@ -17,7 +15,6 @@ interface ProductTypeShippingProps {
}; };
defaultWeightUnit: WeightUnitsEnum; defaultWeightUnit: WeightUnitsEnum;
disabled: boolean; disabled: boolean;
errors: ProductErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -25,13 +22,10 @@ const ProductTypeShipping: React.FC<ProductTypeShippingProps> = ({
data, data,
defaultWeightUnit, defaultWeightUnit,
disabled, disabled,
errors,
onChange onChange
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["weight"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -54,19 +48,15 @@ const ProductTypeShipping: React.FC<ProductTypeShippingProps> = ({
{data.isShippingRequired && ( {data.isShippingRequired && (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.weight}
InputProps={{ endAdornment: defaultWeightUnit }} InputProps={{ endAdornment: defaultWeightUnit }}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Weight" defaultMessage: "Weight"
})} })}
name="weight" name="weight"
helperText={ helperText={intl.formatMessage({
getProductErrorMessage(formErrors.weight, intl) ||
intl.formatMessage({
defaultMessage: defaultMessage:
"Used to calculate rates for shipping for products of this product type, when specific weight is not given" "Used to calculate rates for shipping for products of this product type, when specific weight is not given"
}) })}
}
type="number" type="number"
value={data.weight} value={data.weight}
onChange={onChange} onChange={onChange}

View file

@ -1,6 +1,5 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { productErrorFragment } from "@saleor/attributes/mutations";
import { TypedMutation } from "../mutations"; import { TypedMutation } from "../mutations";
import { productTypeDetailsFragment } from "./queries"; import { productTypeDetailsFragment } from "./queries";
import { import {
@ -33,11 +32,11 @@ import {
} from "./types/UnassignAttribute"; } from "./types/UnassignAttribute";
export const productTypeDeleteMutation = gql` export const productTypeDeleteMutation = gql`
${productErrorFragment}
mutation ProductTypeDelete($id: ID!) { mutation ProductTypeDelete($id: ID!) {
productTypeDelete(id: $id) { productTypeDelete(id: $id) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
id id
@ -51,11 +50,11 @@ export const TypedProductTypeDeleteMutation = TypedMutation<
>(productTypeDeleteMutation); >(productTypeDeleteMutation);
export const productTypeBulkDeleteMutation = gql` export const productTypeBulkDeleteMutation = gql`
${productErrorFragment}
mutation ProductTypeBulkDelete($ids: [ID]!) { mutation ProductTypeBulkDelete($ids: [ID]!) {
productTypeBulkDelete(ids: $ids) { productTypeBulkDelete(ids: $ids) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
} }
} }
@ -66,12 +65,12 @@ export const TypedProductTypeBulkDeleteMutation = TypedMutation<
>(productTypeBulkDeleteMutation); >(productTypeBulkDeleteMutation);
export const productTypeUpdateMutation = gql` export const productTypeUpdateMutation = gql`
${productErrorFragment}
${productTypeDetailsFragment} ${productTypeDetailsFragment}
mutation ProductTypeUpdate($id: ID!, $input: ProductTypeInput!) { mutation ProductTypeUpdate($id: ID!, $input: ProductTypeInput!) {
productTypeUpdate(id: $id, input: $input) { productTypeUpdate(id: $id, input: $input) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
...ProductTypeDetailsFragment ...ProductTypeDetailsFragment
@ -85,12 +84,12 @@ export const TypedProductTypeUpdateMutation = TypedMutation<
>(productTypeUpdateMutation); >(productTypeUpdateMutation);
export const assignAttributeMutation = gql` export const assignAttributeMutation = gql`
${productErrorFragment}
${productTypeDetailsFragment} ${productTypeDetailsFragment}
mutation AssignAttribute($id: ID!, $operations: [AttributeAssignInput!]!) { mutation AssignAttribute($id: ID!, $operations: [AttributeAssignInput!]!) {
attributeAssign(productTypeId: $id, operations: $operations) { attributeAssign(productTypeId: $id, operations: $operations) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
...ProductTypeDetailsFragment ...ProductTypeDetailsFragment
@ -104,12 +103,12 @@ export const TypedAssignAttributeMutation = TypedMutation<
>(assignAttributeMutation); >(assignAttributeMutation);
export const unassignAttributeMutation = gql` export const unassignAttributeMutation = gql`
${productErrorFragment}
${productTypeDetailsFragment} ${productTypeDetailsFragment}
mutation UnassignAttribute($id: ID!, $ids: [ID]!) { mutation UnassignAttribute($id: ID!, $ids: [ID]!) {
attributeUnassign(productTypeId: $id, attributeIds: $ids) { attributeUnassign(productTypeId: $id, attributeIds: $ids) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
...ProductTypeDetailsFragment ...ProductTypeDetailsFragment
@ -123,12 +122,12 @@ export const TypedUnassignAttributeMutation = TypedMutation<
>(unassignAttributeMutation); >(unassignAttributeMutation);
export const productTypeCreateMutation = gql` export const productTypeCreateMutation = gql`
${productErrorFragment}
${productTypeDetailsFragment} ${productTypeDetailsFragment}
mutation ProductTypeCreate($input: ProductTypeInput!) { mutation ProductTypeCreate($input: ProductTypeInput!) {
productTypeCreate(input: $input) { productTypeCreate(input: $input) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
...ProductTypeDetailsFragment ...ProductTypeDetailsFragment
@ -142,7 +141,6 @@ export const TypedProductTypeCreateMutation = TypedMutation<
>(productTypeCreateMutation); >(productTypeCreateMutation);
const productTypeAttributeReorder = gql` const productTypeAttributeReorder = gql`
${productErrorFragment}
${productTypeDetailsFragment} ${productTypeDetailsFragment}
mutation ProductTypeAttributeReorder( mutation ProductTypeAttributeReorder(
$move: ReorderInput! $move: ReorderInput!
@ -154,8 +152,9 @@ const productTypeAttributeReorder = gql`
productTypeId: $productTypeId productTypeId: $productTypeId
type: $type type: $type
) { ) {
errors: productErrors { errors {
...ProductErrorFragment field
message
} }
productType { productType {
...ProductTypeDetailsFragment ...ProductTypeDetailsFragment

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 { AttributeAssignInput, ProductErrorCode } from "./../../types/globalTypes"; import { AttributeAssignInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AssignAttribute // GraphQL mutation operation: AssignAttribute
// ==================================================== // ====================================================
export interface AssignAttribute_attributeAssign_errors { export interface AssignAttribute_attributeAssign_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface AssignAttribute_attributeAssign_productType_taxType { export interface AssignAttribute_attributeAssign_productType_taxType {

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 { ReorderInput, AttributeTypeEnum, ProductErrorCode } from "./../../types/globalTypes"; import { ReorderInput, AttributeTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeAttributeReorder // GraphQL mutation operation: ProductTypeAttributeReorder
// ==================================================== // ====================================================
export interface ProductTypeAttributeReorder_productTypeReorderAttributes_errors { export interface ProductTypeAttributeReorder_productTypeReorderAttributes_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_taxType { export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_taxType {

View file

@ -2,16 +2,14 @@
/* 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 { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeBulkDelete // GraphQL mutation operation: ProductTypeBulkDelete
// ==================================================== // ====================================================
export interface ProductTypeBulkDelete_productTypeBulkDelete_errors { export interface ProductTypeBulkDelete_productTypeBulkDelete_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface ProductTypeBulkDelete_productTypeBulkDelete { export interface ProductTypeBulkDelete_productTypeBulkDelete {

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 { ProductTypeInput, ProductErrorCode } from "./../../types/globalTypes"; import { ProductTypeInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeCreate // GraphQL mutation operation: ProductTypeCreate
// ==================================================== // ====================================================
export interface ProductTypeCreate_productTypeCreate_errors { export interface ProductTypeCreate_productTypeCreate_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface ProductTypeCreate_productTypeCreate_productType_taxType { export interface ProductTypeCreate_productTypeCreate_productType_taxType {

View file

@ -2,16 +2,14 @@
/* 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 { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeDelete // GraphQL mutation operation: ProductTypeDelete
// ==================================================== // ====================================================
export interface ProductTypeDelete_productTypeDelete_errors { export interface ProductTypeDelete_productTypeDelete_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface ProductTypeDelete_productTypeDelete_productType { export interface ProductTypeDelete_productTypeDelete_productType {

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 { ProductTypeInput, ProductErrorCode } from "./../../types/globalTypes"; import { ProductTypeInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeUpdate // GraphQL mutation operation: ProductTypeUpdate
// ==================================================== // ====================================================
export interface ProductTypeUpdate_productTypeUpdate_errors { export interface ProductTypeUpdate_productTypeUpdate_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface ProductTypeUpdate_productTypeUpdate_productType_taxType { export interface ProductTypeUpdate_productTypeUpdate_productType_taxType {

View file

@ -2,16 +2,14 @@
/* 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 { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UnassignAttribute // GraphQL mutation operation: UnassignAttribute
// ==================================================== // ====================================================
export interface UnassignAttribute_attributeUnassign_errors { export interface UnassignAttribute_attributeUnassign_errors {
__typename: "ProductError"; __typename: "Error";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface UnassignAttribute_attributeUnassign_productType_taxType { export interface UnassignAttribute_attributeUnassign_productType_taxType {

View file

@ -57,9 +57,10 @@ export const ProductTypeCreate: React.FC = () => {
<ProductTypeCreatePage <ProductTypeCreatePage
defaultWeightUnit={maybe(() => data.shop.defaultWeightUnit)} defaultWeightUnit={maybe(() => data.shop.defaultWeightUnit)}
disabled={loading} disabled={loading}
errors={ errors={maybe(
createProductTypeOpts.data?.productTypeCreate.errors || [] () => createProductTypeOpts.data.productTypeCreate.errors,
} []
)}
pageTitle={intl.formatMessage({ pageTitle={intl.formatMessage({
defaultMessage: "Create Product Type", defaultMessage: "Create Product Type",
description: "header", description: "header",

View file

@ -1,19 +1,19 @@
import React from "react"; import React from "react";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { UserError } from "../../../types";
interface ProductTypeUpdateErrorsState { interface ProductTypeUpdateErrorsState {
addAttributeErrors: ProductErrorFragment[]; addAttributeErrors: UserError[];
editAttributeErrors: ProductErrorFragment[]; editAttributeErrors: UserError[];
formErrors: ProductErrorFragment[]; formErrors: UserError[];
} }
interface ProductTypeUpdateErrorsProps { interface ProductTypeUpdateErrorsProps {
children: (props: { children: (props: {
errors: ProductTypeUpdateErrorsState; errors: ProductTypeUpdateErrorsState;
set: { set: {
addAttributeErrors: (errors: ProductErrorFragment[]) => void; addAttributeErrors: (errors: UserError[]) => void;
editAttributeErrors: (errors: ProductErrorFragment[]) => void; editAttributeErrors: (errors: UserError[]) => void;
formErrors: (errors: ProductErrorFragment[]) => void; formErrors: (errors: UserError[]) => void;
}; };
}) => React.ReactNode; }) => React.ReactNode;
} }
@ -32,12 +32,11 @@ export class ProductTypeUpdateErrors extends React.Component<
return this.props.children({ return this.props.children({
errors: this.state, errors: this.state,
set: { set: {
addAttributeErrors: (addAttributeErrors: ProductErrorFragment[]) => addAttributeErrors: (addAttributeErrors: UserError[]) =>
this.setState({ addAttributeErrors }), this.setState({ addAttributeErrors }),
editAttributeErrors: (editAttributeErrors: ProductErrorFragment[]) => editAttributeErrors: (editAttributeErrors: UserError[]) =>
this.setState({ editAttributeErrors }), this.setState({ editAttributeErrors }),
formErrors: (formErrors: ProductErrorFragment[]) => formErrors: (formErrors: UserError[]) => this.setState({ formErrors })
this.setState({ formErrors })
} }
}); });
} }

View file

@ -319,10 +319,13 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
) )
)} )}
confirmButtonState={assignAttribute.opts.status} confirmButtonState={assignAttribute.opts.status}
errors={ errors={maybe(
assignAttribute.opts.data?.attributeAssign () =>
.errors || [] assignAttribute.opts.data.attributeAssign.errors.map(
} err => err.message
),
[]
)}
loading={result.loading} loading={result.loading}
onClose={closeModal} onClose={closeModal}
onSubmit={handleAssignAttribute} onSubmit={handleAssignAttribute}

View file

@ -10012,7 +10012,7 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
<p <p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id" class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id"
> >
This field is required Generic form error
</p> </p>
</div> </div>
<div <div
@ -10056,7 +10056,7 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
<p <p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id" class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id"
> >
This field is required Generic form error
</p> </p>
</div> </div>
<div <div
@ -10465,7 +10465,7 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
<p <p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id" class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id"
> >
API error Generic form error
</p> </p>
</div> </div>
<div <div
@ -84010,7 +84010,7 @@ exports[`Storyshots Views / Product types / Create product type form errors 1`]
<p <p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id" class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id"
> >
Unknown error Generic form error
</p> </p>
</div> </div>
</div> </div>
@ -85147,7 +85147,7 @@ exports[`Storyshots Views / Product types / Product type details form errors 1`]
<p <p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id" class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-error-id MuiFormHelperText-filled-id"
> >
Unknown error Generic form error
</p> </p>
</div> </div>
</div> </div>

View file

@ -5,10 +5,8 @@ import AttributePage, {
AttributePageProps AttributePageProps
} from "@saleor/attributes/components/AttributePage"; } from "@saleor/attributes/components/AttributePage";
import { attribute } from "@saleor/attributes/fixtures"; import { attribute } from "@saleor/attributes/fixtures";
import { import { formError } from "@saleor/storybook/misc";
AttributeInputTypeEnum, import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
ProductErrorCode
} from "@saleor/types/globalTypes";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const props: AttributePageProps = { const props: AttributePageProps = {
@ -41,23 +39,7 @@ storiesOf("Views / Attributes / Attribute details", module)
.add("form errors", () => ( .add("form errors", () => (
<AttributePage <AttributePage
{...props} {...props}
errors={[ errors={["name", "slug", "storefrontSearchPosition"].map(formError)}
{
code: ProductErrorCode.REQUIRED,
field: "name"
},
{
code: ProductErrorCode.REQUIRED,
field: "slug"
},
{
code: ProductErrorCode.GRAPHQL_ERROR,
field: "storefrontSearchPosition"
}
].map(err => ({
__typename: "ProductError",
...err
}))}
/> />
)) ))
.add("multiple select input", () => ( .add("multiple select input", () => (

View file

@ -2,10 +2,8 @@ import { storiesOf } from "@storybook/react";
import React from "react"; import React from "react";
import { attribute } from "@saleor/attributes/fixtures"; import { attribute } from "@saleor/attributes/fixtures";
import { import { formError } from "@saleor/storybook/misc";
AttributeValueType, import { AttributeValueType } from "@saleor/types/globalTypes";
ProductErrorCode
} from "@saleor/types/globalTypes";
import AttributeValueEditDialog, { import AttributeValueEditDialog, {
AttributeValueEditDialogProps AttributeValueEditDialogProps
} from "../../../attributes/components/AttributeValueEditDialog"; } from "../../../attributes/components/AttributeValueEditDialog";
@ -28,14 +26,5 @@ storiesOf("Attributes / Attribute value edit", module)
.addDecorator(Decorator) .addDecorator(Decorator)
.add("default", () => <AttributeValueEditDialog {...props} />) .add("default", () => <AttributeValueEditDialog {...props} />)
.add("form errors", () => ( .add("form errors", () => (
<AttributeValueEditDialog <AttributeValueEditDialog {...props} errors={[formError("name")]} />
{...props}
errors={[
{
__typename: "ProductError",
code: ProductErrorCode.ALREADY_EXISTS,
field: "name"
}
]}
/>
)); ));

View file

@ -6,7 +6,7 @@ import { fetchMoreProps } from "@saleor/fixtures";
import AssignAttributeDialog, { import AssignAttributeDialog, {
AssignAttributeDialogProps AssignAttributeDialogProps
} from "@saleor/productTypes/components/AssignAttributeDialog"; } from "@saleor/productTypes/components/AssignAttributeDialog";
import { ProductErrorCode } from "@saleor/types/globalTypes"; import { formError } from "@saleor/storybook/misc";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const props: AssignAttributeDialogProps = { const props: AssignAttributeDialogProps = {
@ -30,14 +30,5 @@ storiesOf("Generics / Assign attributes dialog", module)
<AssignAttributeDialog {...props} attributes={undefined} loading={true} /> <AssignAttributeDialog {...props} attributes={undefined} loading={true} />
)) ))
.add("errors", () => ( .add("errors", () => (
<AssignAttributeDialog <AssignAttributeDialog {...props} errors={[formError("").message]} />
{...props}
errors={[
{
__typename: "ProductError" as "ProductError",
code: ProductErrorCode.INVALID,
field: null
}
]}
/>
)); ));

View file

@ -0,0 +1,38 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import { formError } from "@saleor/storybook/misc";
import ProductTypeAttributeEditDialog, {
ProductTypeAttributeEditDialogProps
} from "../../../productTypes/components/ProductTypeAttributeEditDialog";
import { attributes } from "../../../productTypes/fixtures";
import Decorator from "../../Decorator";
const attribute = attributes[0];
const props: ProductTypeAttributeEditDialogProps = {
disabled: false,
errors: [],
name: attribute.name,
onClose: () => undefined,
onConfirm: () => undefined,
opened: true,
title: "Add Attribute",
values: attribute.values.map(value => ({
label: value.name,
value: value.id
}))
};
storiesOf("Product types / Edit attribute", module)
.addDecorator(Decorator)
.add("default", () => <ProductTypeAttributeEditDialog {...props} />)
.add("loading", () => (
<ProductTypeAttributeEditDialog {...props} disabled={true} />
))
.add("form errors", () => (
<ProductTypeAttributeEditDialog
{...props}
errors={["name", "values"].map(field => formError(field))}
/>
));

View file

@ -2,11 +2,12 @@ import { Omit } from "@material-ui/core";
import { storiesOf } from "@storybook/react"; import { storiesOf } from "@storybook/react";
import React from "react"; import React from "react";
import { formError } from "@saleor/storybook/misc";
import ProductTypeCreatePage, { import ProductTypeCreatePage, {
ProductTypeCreatePageProps, ProductTypeCreatePageProps,
ProductTypeForm ProductTypeForm
} from "../../../productTypes/components/ProductTypeCreatePage"; } from "../../../productTypes/components/ProductTypeCreatePage";
import { WeightUnitsEnum, ProductErrorCode } from "../../../types/globalTypes"; import { WeightUnitsEnum } from "../../../types/globalTypes";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const props: Omit<ProductTypeCreatePageProps, "classes"> = { const props: Omit<ProductTypeCreatePageProps, "classes"> = {
@ -29,10 +30,6 @@ storiesOf("Views / Product types / Create product type", module)
.add("form errors", () => ( .add("form errors", () => (
<ProductTypeCreatePage <ProductTypeCreatePage
{...props} {...props}
errors={(["name"] as Array<keyof ProductTypeForm>).map(field => ({ errors={(["name"] as Array<keyof ProductTypeForm>).map(formError)}
__typename: "ProductError",
code: ProductErrorCode.INVALID,
field
}))}
/> />
)); ));

View file

@ -3,12 +3,13 @@ import { storiesOf } from "@storybook/react";
import React from "react"; import React from "react";
import { listActionsProps } from "@saleor/fixtures"; import { listActionsProps } from "@saleor/fixtures";
import { formError } from "@saleor/storybook/misc";
import ProductTypeDetailsPage, { import ProductTypeDetailsPage, {
ProductTypeDetailsPageProps, ProductTypeDetailsPageProps,
ProductTypeForm ProductTypeForm
} from "../../../productTypes/components/ProductTypeDetailsPage"; } from "../../../productTypes/components/ProductTypeDetailsPage";
import { productType } from "../../../productTypes/fixtures"; import { productType } from "../../../productTypes/fixtures";
import { WeightUnitsEnum, ProductErrorCode } from "../../../types/globalTypes"; import { WeightUnitsEnum } from "../../../types/globalTypes";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const props: Omit<ProductTypeDetailsPageProps, "classes"> = { const props: Omit<ProductTypeDetailsPageProps, "classes"> = {
@ -54,12 +55,6 @@ storiesOf("Views / Product types / Product type details", module)
.add("form errors", () => ( .add("form errors", () => (
<ProductTypeDetailsPage <ProductTypeDetailsPage
{...props} {...props}
errors={(["name", "weight"] as Array<keyof ProductTypeForm>).map( errors={(["name"] as Array<keyof ProductTypeForm>).map(formError)}
field => ({
__typename: "ProductError",
code: ProductErrorCode.INVALID,
field
})
)}
/> />
)); ));

View file

@ -7,7 +7,7 @@ import { MultiAutocompleteChoiceType } from "./components/MultiAutocompleteSelec
export interface UserError { export interface UserError {
field: string | null; field: string | null;
message?: string; message: string;
} }
export interface DialogProps { export interface DialogProps {

11
src/utils/errors.ts Normal file
View file

@ -0,0 +1,11 @@
import { UserError } from "@saleor/types";
export function getFieldError(errors: UserError[], field: string): UserError {
return errors.find(err => err.field === field);
}
export function getErrors(errors: UserError[]): string[] {
return errors
.filter(err => ["", null].includes(err.field))
.map(err => err.message);
}

View file

@ -1,12 +0,0 @@
import { defineMessages } from "react-intl";
const commonErrorMessages = defineMessages({
graphqlError: {
defaultMessage: "API error"
},
unknownError: {
defaultMessage: "Unknown error"
}
});
export default commonErrorMessages;

View file

@ -1,26 +0,0 @@
import { UserError } from "@saleor/types";
export function getFieldError<T extends UserError>(
errors: T[],
field: string
): T {
return errors.find(err => err.field === field);
}
export function getErrors(errors: UserError[]): string[] {
return errors
.filter(err => ["", null].includes(err.field))
.map(err => err.message);
}
export function getFormErrors<TField extends string, TError extends UserError>(
fields: TField[],
errors: TError[]
): Record<TField, TError> {
return fields.reduce((errs, field) => {
errs[field] = getFieldError(errors, field);
return errs;
}, ({} as unknown) as Record<TField, TError>);
}
export { default as getProductErrorMessage } from "./product";

View file

@ -1,50 +0,0 @@
import { IntlShape, defineMessages } from "react-intl";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { ProductErrorCode } from "@saleor/types/globalTypes";
import { commonMessages } from "@saleor/intl";
import commonErrorMessages from "./common";
const messages = defineMessages({
attributeAlreadyAssigned: {
defaultMessage:
"This attribute has already been assigned to this product type"
},
attributeCannotBeAssigned: {
defaultMessage: "This attribute cannot be assigned to this product type"
},
attributeVariantsDisabled: {
defaultMessage: "Variants are disabled in this product type"
},
variantNoDigitalContent: {
defaultMessage: "This variant does not have any digital content"
}
});
function getProductErrorMessage(
err: ProductErrorFragment,
intl: IntlShape
): string {
if (err) {
switch (err.code) {
case ProductErrorCode.ATTRIBUTE_ALREADY_ASSIGNED:
return intl.formatMessage(messages.attributeAlreadyAssigned);
case ProductErrorCode.ATTRIBUTE_CANNOT_BE_ASSIGNED:
return intl.formatMessage(messages.attributeCannotBeAssigned);
case ProductErrorCode.ATTRIBUTE_VARIANTS_DISABLED:
return intl.formatMessage(messages.attributeVariantsDisabled);
case ProductErrorCode.GRAPHQL_ERROR:
return intl.formatMessage(commonErrorMessages.graphqlError);
case ProductErrorCode.REQUIRED:
return intl.formatMessage(commonMessages.requiredField);
case ProductErrorCode.VARIANT_NO_DIGITAL_CONTENT:
return intl.formatMessage(messages.variantNoDigitalContent);
default:
return intl.formatMessage(commonErrorMessages.unknownError);
}
}
return undefined;
}
export default getProductErrorMessage;