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 () =>
?.attributeUpdate.errors || [] attributeUpdateOpts.data
} .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 () =>
?.attributeValueCreate.errors || [] attributeValueCreateOpts.data
} .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 () =>
?.attributeValueUpdate.errors || [] attributeValueUpdateOpts.data
} .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) || defaultMessage:
intl.formatMessage({ "Used to calculate rates for shipping for products of this product type, when specific weight is not given"
defaultMessage: })}
"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;