From ff74c566ab567c436646e598b1f980bada4be824 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Fri, 6 Mar 2020 15:25:23 +0100 Subject: [PATCH] Use error formatting in product section --- .../ProductCreatePage/ProductCreatePage.tsx | 6 +- .../ProductDetailsForm/ProductDetailsForm.tsx | 16 ++- .../ProductOrganization.tsx | 34 +++-- .../ProductPricing/ProductPricing.tsx | 16 ++- .../components/ProductStock/ProductStock.tsx | 38 +++--- .../ProductUpdatePage/ProductUpdatePage.tsx | 6 +- .../ProductVariantCreateSummary.tsx | 51 ++++---- .../ProductVariantCreatePage.tsx | 4 +- .../ProductVariantPrice.tsx | 18 +-- .../ProductVariantStock.tsx | 40 +++--- src/products/mutations.ts | 122 +++++++++--------- .../types/BulkProductErrorFragment.ts | 16 +++ src/products/types/ProductCreate.ts | 6 +- src/products/types/ProductDelete.ts | 6 +- src/products/types/ProductImageCreate.ts | 6 +- src/products/types/ProductImageDelete.ts | 9 ++ src/products/types/ProductImageReorder.ts | 6 +- src/products/types/ProductImageUpdate.ts | 6 +- src/products/types/ProductUpdate.ts | 6 +- .../types/ProductVariantBulkCreate.ts | 10 +- .../types/ProductVariantBulkDelete.ts | 6 +- src/products/types/SimpleProductUpdate.ts | 10 +- src/products/types/VariantCreate.ts | 5 +- src/products/types/VariantDelete.ts | 6 +- src/products/types/VariantImageAssign.ts | 6 +- src/products/types/VariantImageUnassign.ts | 6 +- src/products/types/VariantUpdate.ts | 5 +- src/products/types/productBulkDelete.ts | 6 +- src/products/types/productBulkPublish.ts | 6 +- src/products/views/ProductCreate.tsx | 12 +- .../views/ProductUpdate/ProductUpdate.tsx | 20 +-- src/products/views/ProductVariantCreate.tsx | 17 +-- src/utils/errors/product.ts | 65 ++++++++++ 33 files changed, 348 insertions(+), 244 deletions(-) create mode 100644 src/products/types/BulkProductErrorFragment.ts create mode 100644 src/utils/errors/product.ts diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 10c543699..8386cfb8c 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -27,7 +27,8 @@ import { SearchCollections_search_edges_node } from "@saleor/searches/types/Sear import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/searches/types/SearchProductTypes"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; -import { FetchMoreProps, UserError } from "../../../types"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { FetchMoreProps } from "../../../types"; import { createAttributeChangeHandler, createAttributeMultiChangeHandler, @@ -62,7 +63,7 @@ export interface ProductCreatePageSubmitData extends FormData { } interface ProductCreatePageProps { - errors: UserError[]; + errors: ProductErrorFragment[]; collections: SearchCollections_search_edges_node[]; categories: SearchCategories_search_edges_node[]; currency: string; @@ -227,6 +228,7 @@ export const ProductCreatePage: React.FC = ({ currency={currency} data={data} disabled={disabled} + errors={errors} onChange={change} /> diff --git a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx index 3ade54592..04d3321f4 100644 --- a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx +++ b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx @@ -9,8 +9,8 @@ import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor from "@saleor/components/RichTextEditor"; import { commonMessages } from "@saleor/intl"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; interface ProductDetailsFormProps { data: { @@ -18,7 +18,7 @@ interface ProductDetailsFormProps { name: string; }; disabled?: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; // Draftail isn't controlled - it needs only initial input // because it's autosaving on its own. // Ref https://github.com/mirumee/saleor/issues/4470 @@ -35,6 +35,8 @@ export const ProductDetailsForm: React.FC = ({ }) => { const intl = useIntl(); + const formErrors = getFormErrors(["name", "descriptionJson"], errors); + return ( = ({ /> = ({ = props => { const classes = useStyles(props); const intl = useIntl(); + const formErrors = getFormErrors( + ["productType", "category", "collections"], + errors + ); + return ( = props => { {canChangeType ? ( = props => { = props => { ({ @@ -26,15 +28,18 @@ interface ProductPricingProps { basePrice: number; }; disabled: boolean; + errors: ProductErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } const ProductPricing: React.FC = props => { - const { currency, data, disabled, onChange } = props; - const classes = useStyles(props); + const { currency, data, disabled, errors, onChange } = props; + const classes = useStyles(props); const intl = useIntl(); + const formErrors = getFormErrors(["basePrice"], errors); + return ( = props => { defaultMessage: "Price", description: "product price" })} + error={!!formErrors.basePrice} + hint={getProductErrorMessage(formErrors.basePrice, intl)} name="basePrice" value={data.basePrice} currencySymbol={currency} onChange={onChange} + InputProps={{ + inputProps: { + min: 0 + } + }} /> diff --git a/src/products/components/ProductStock/ProductStock.tsx b/src/products/components/ProductStock/ProductStock.tsx index 17a61cf59..c76609af4 100644 --- a/src/products/components/ProductStock/ProductStock.tsx +++ b/src/products/components/ProductStock/ProductStock.tsx @@ -6,8 +6,8 @@ import React from "react"; import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; import { maybe } from "../../../misc"; import { ProductDetails_product } from "../../types/ProductDetails"; @@ -28,17 +28,19 @@ interface ProductStockProps { stockQuantity: number; }; disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; product: ProductDetails_product; onChange: (event: React.ChangeEvent) => void; } const ProductStock: React.FC = props => { const { data, disabled, product, onChange, errors } = props; - const classes = useStyles(props); + const classes = useStyles(props); const intl = useIntl(); + const formErrors = getFormErrors(["sku", "stockQuantity"], errors); + return ( = props => { })} value={data.sku} onChange={onChange} - error={!!getFieldError(errors, "sku")} - helperText={getFieldError(errors, "sku")?.message} + error={!!formErrors.sku} + helperText={getProductErrorMessage(formErrors.sku, intl)} /> = props => { type="number" onChange={onChange} helperText={ - product - ? intl.formatMessage( - { - defaultMessage: "Allocated: {quantity}", - description: "allocated product stock" - }, - { - quantity: maybe( - () => product.variants[0].quantityAllocated - ) - } - ) - : undefined + getProductErrorMessage(formErrors.stockQuantity, intl) || + (product && + intl.formatMessage( + { + defaultMessage: "Allocated: {quantity}", + description: "allocated product stock" + }, + { + quantity: product?.variants[0].quantityAllocated + } + )) } /> diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 92adbea8e..0e7362403 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -19,9 +19,10 @@ import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; -import { FetchMoreProps, ListActions, UserError } from "@saleor/types"; +import { FetchMoreProps, ListActions } from "@saleor/types"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { ProductDetails_product, ProductDetails_product_images, @@ -48,7 +49,7 @@ import ProductStock from "../ProductStock"; import ProductVariants from "../ProductVariants"; export interface ProductUpdatePageProps extends ListActions { - errors: UserError[]; + errors: ProductErrorFragment[]; placeholderImage: string; collections: SearchCollections_search_edges_node[]; categories: SearchCategories_search_edges_node[]; @@ -219,6 +220,7 @@ export const ProductUpdatePage: React.FC = ({ currency={currency} data={data} disabled={disabled} + errors={errors} onChange={change} /> diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx index 86cf5cc90..01e370e9e 100644 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx +++ b/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx @@ -10,12 +10,13 @@ import Typography from "@material-ui/core/Typography"; import DeleteIcon from "@material-ui/icons/Delete"; import classNames from "classnames"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import Hr from "@saleor/components/Hr"; -import { maybe } from "@saleor/misc"; -import { ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors } from "@saleor/products/types/ProductVariantBulkCreate"; +import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate"; import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes"; +import { getFormErrors } from "@saleor/utils/errors"; +import { getBulkProductErrorMessage } from "@saleor/utils/errors/product"; import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails"; import { ProductVariantCreateFormData } from "./form"; import { VariantField } from "./reducer"; @@ -24,7 +25,7 @@ export interface ProductVariantCreateSummaryProps { attributes: ProductDetails_product_productType_variantAttributes[]; currencySymbol: string; data: ProductVariantCreateFormData; - errors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[]; + errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[]; onVariantDataChange: ( variantIndex: number, field: VariantField, @@ -107,9 +108,7 @@ function getVariantName( ); } -const ProductVariantCreateSummary: React.FC< - ProductVariantCreateSummaryProps -> = props => { +const ProductVariantCreateSummary: React.FC = props => { const { attributes, currencySymbol, @@ -119,6 +118,7 @@ const ProductVariantCreateSummary: React.FC< onVariantDelete } = props; const classes = useStyles(props); + const intl = useIntl(); return ( <> @@ -181,6 +181,10 @@ const ProductVariantCreateSummary: React.FC< const variantErrors = errors.filter( error => error.index === variantIndex ); + const variantFormErrors = getFormErrors( + ["priceOverride", "quantity", "sku"], + variantErrors + ); return (
error.field === "priceOverride" - ) - } - helperText={maybe( - () => - variantErrors.find( - error => error.field === "priceOverride" - ).message + error={!!variantFormErrors.priceOverride} + helperText={getBulkProductErrorMessage( + variantFormErrors.priceOverride, + intl )} inputProps={{ min: 0, @@ -241,13 +239,10 @@ const ProductVariantCreateSummary: React.FC<
error.field === "quantity") - } - helperText={maybe( - () => - variantErrors.find(error => error.field === "quantity") - .message + error={!!variantFormErrors.quantity} + helperText={getBulkProductErrorMessage( + variantFormErrors.quantity, + intl )} inputProps={{ min: 0, @@ -267,10 +262,10 @@ const ProductVariantCreateSummary: React.FC<
error.field === "sku")} - helperText={maybe( - () => - variantErrors.find(error => error.field === "sku").message + error={!!variantFormErrors.sku} + helperText={getBulkProductErrorMessage( + variantFormErrors.sku, + intl )} fullWidth value={variant.sku} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index eb6449f09..47b3c4d13 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -13,8 +13,8 @@ import useFormset, { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; -import { VariantCreate_productVariantCreate_productErrors } from "@saleor/products/types/VariantCreate"; import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { maybe } from "../../../misc"; import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData"; import ProductVariantAttributes, { @@ -39,7 +39,7 @@ export interface ProductVariantCreatePageSubmitData interface ProductVariantCreatePageProps { currencySymbol: string; - errors: VariantCreate_productVariantCreate_productErrors[]; + errors: ProductErrorFragment[]; header: string; loading: boolean; product: ProductVariantCreateData_product; diff --git a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx index 057985ddd..738c3ea04 100644 --- a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx +++ b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx @@ -6,8 +6,8 @@ import { useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import PriceField from "@saleor/components/PriceField"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; const useStyles = makeStyles( theme => ({ @@ -24,7 +24,7 @@ interface ProductVariantPriceProps { currencySymbol?: string; priceOverride?: string; costPrice?: string; - errors: UserError[]; + errors: ProductErrorFragment[]; loading?: boolean; onChange(event: any); } @@ -38,10 +38,12 @@ const ProductVariantPrice: React.FC = props => { loading, onChange } = props; - const classes = useStyles(props); + const classes = useStyles(props); const intl = useIntl(); + const formErrors = getFormErrors(["price_override", "cost_price"], errors); + return ( = props => {
= props => {
({ @@ -21,7 +21,7 @@ const useStyles = makeStyles( ); interface ProductVariantStockProps { - errors: UserError[]; + errors: ProductErrorFragment[]; sku: string; quantity: string; stockAllocated?: number; @@ -31,10 +31,12 @@ interface ProductVariantStockProps { const ProductVariantStock: React.FC = props => { const { errors, sku, quantity, stockAllocated, loading, onChange } = props; - const classes = useStyles(props); + const classes = useStyles(props); const intl = useIntl(); + const formErrors = getFormErrors(["quantity", "sku"], errors); + return ( = props => {
= props => { description: "product variant stock" })} helperText={ - getFieldError(errors, "quantity") - ? getFieldError(errors, "quantity") - : !!stockAllocated - ? intl.formatMessage( - { - defaultMessage: "Allocated: {quantity}", - description: "variant allocated stock" - }, - { - quantity: stockAllocated - } - ) - : undefined + getProductErrorMessage(formErrors.quantity, intl) || + (!!stockAllocated && + intl.formatMessage( + { + defaultMessage: "Allocated: {quantity}", + description: "variant allocated stock" + }, + { + quantity: stockAllocated + } + )) } onChange={onChange} disabled={loading} @@ -76,8 +76,8 @@ const ProductVariantStock: React.FC = props => {
(productImageCreateMutation); export const productDeleteMutation = gql` + ${productErrorFragment} mutation ProductDelete($id: ID!) { productDelete(id: $id) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { id @@ -92,11 +101,11 @@ export const TypedProductDeleteMutation = TypedMutation< >(productDeleteMutation); export const productImagesReorder = gql` + ${productErrorFragment} mutation ProductImageReorder($productId: ID!, $imagesIds: [ID]!) { productImageReorder(productId: $productId, imagesIds: $imagesIds) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { id @@ -116,6 +125,7 @@ export const TypedProductImagesReorder = TypedMutation< >(productImagesReorder); export const productUpdateMutation = gql` + ${productErrorFragment} ${productFragmentDetails} mutation ProductUpdate( $id: ID! @@ -145,9 +155,8 @@ export const productUpdateMutation = gql` seo: $seo } ) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { ...Product @@ -161,6 +170,7 @@ export const TypedProductUpdateMutation = TypedMutation< >(productUpdateMutation); export const simpleProductUpdateMutation = gql` + ${productErrorFragment} ${productFragmentDetails} ${fragmentVariant} mutation SimpleProductUpdate( @@ -193,18 +203,16 @@ export const simpleProductUpdateMutation = gql` seo: $seo } ) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { ...Product } } productVariantUpdate(id: $productVariantId, input: $productVariantInput) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { ...ProductVariant @@ -218,6 +226,7 @@ export const TypedSimpleProductUpdateMutation = TypedMutation< >(simpleProductUpdateMutation); export const productCreateMutation = gql` + ${productErrorFragment} ${productFragmentDetails} mutation ProductCreate( $attributes: [AttributeValueInput] @@ -251,9 +260,8 @@ export const productCreateMutation = gql` seo: $seo } ) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { ...Product @@ -267,11 +275,11 @@ export const TypedProductCreateMutation = TypedMutation< >(productCreateMutation); export const variantDeleteMutation = gql` + ${productErrorFragment} mutation VariantDelete($id: ID!) { productVariantDelete(id: $id) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { id @@ -286,6 +294,7 @@ export const TypedVariantDeleteMutation = TypedMutation< export const variantUpdateMutation = gql` ${fragmentVariant} + ${productErrorFragment} mutation VariantUpdate( $id: ID! $attributes: [AttributeValueInput] @@ -306,10 +315,8 @@ export const variantUpdateMutation = gql` trackInventory: $trackInventory } ) { - productErrors { - code - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { ...ProductVariant @@ -324,12 +331,11 @@ export const TypedVariantUpdateMutation = TypedMutation< export const variantCreateMutation = gql` ${fragmentVariant} + ${productErrorFragment} mutation VariantCreate($input: ProductVariantCreateInput!) { productVariantCreate(input: $input) { - productErrors { - code - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { ...ProductVariant @@ -343,8 +349,12 @@ export const TypedVariantCreateMutation = TypedMutation< >(variantCreateMutation); export const productImageDeleteMutation = gql` + ${productErrorFragment} mutation ProductImageDelete($id: ID!) { productImageDelete(id: $id) { + errors: productErrors { + ...ProductErrorFragment + } product { id images { @@ -360,12 +370,12 @@ export const TypedProductImageDeleteMutation = TypedMutation< >(productImageDeleteMutation); export const productImageUpdateMutation = gql` + ${productErrorFragment} ${productFragmentDetails} mutation ProductImageUpdate($id: ID!, $alt: String!) { productImageUpdate(id: $id, input: { alt: $alt }) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } product { ...Product @@ -380,11 +390,11 @@ export const TypedProductImageUpdateMutation = TypedMutation< export const variantImageAssignMutation = gql` ${fragmentVariant} + ${productErrorFragment} mutation VariantImageAssign($variantId: ID!, $imageId: ID!) { variantImageAssign(variantId: $variantId, imageId: $imageId) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { ...ProductVariant @@ -399,11 +409,11 @@ export const TypedVariantImageAssignMutation = TypedMutation< export const variantImageUnassignMutation = gql` ${fragmentVariant} + ${productErrorFragment} mutation VariantImageUnassign($variantId: ID!, $imageId: ID!) { variantImageUnassign(variantId: $variantId, imageId: $imageId) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } productVariant { ...ProductVariant @@ -417,11 +427,11 @@ export const TypedVariantImageUnassignMutation = TypedMutation< >(variantImageUnassignMutation); export const productBulkDeleteMutation = gql` + ${productErrorFragment} mutation productBulkDelete($ids: [ID!]!) { productBulkDelete(ids: $ids) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -432,11 +442,11 @@ export const TypedProductBulkDeleteMutation = TypedMutation< >(productBulkDeleteMutation); export const productBulkPublishMutation = gql` + ${productErrorFragment} mutation productBulkPublish($ids: [ID!]!, $isPublished: Boolean!) { productBulkPublish(ids: $ids, isPublished: $isPublished) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -447,20 +457,14 @@ export const TypedProductBulkPublishMutation = TypedMutation< >(productBulkPublishMutation); export const ProductVariantBulkCreateMutation = gql` + ${bulkProductErrorFragment} mutation ProductVariantBulkCreate( $id: ID! $inputs: [ProductVariantBulkCreateInput]! ) { productVariantBulkCreate(product: $id, variants: $inputs) { - bulkProductErrors { - field - message - code - index - } - errors { - field - message + errors: bulkProductErrors { + ...BulkProductErrorFragment } } } @@ -471,11 +475,11 @@ export const TypedProductVariantBulkCreateMutation = TypedMutation< >(ProductVariantBulkCreateMutation); export const ProductVariantBulkDeleteMutation = gql` + ${productErrorFragment} mutation ProductVariantBulkDelete($ids: [ID!]!) { productVariantBulkDelete(ids: $ids) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } diff --git a/src/products/types/BulkProductErrorFragment.ts b/src/products/types/BulkProductErrorFragment.ts new file mode 100644 index 000000000..940f13b10 --- /dev/null +++ b/src/products/types/BulkProductErrorFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: BulkProductErrorFragment +// ==================================================== + +export interface BulkProductErrorFragment { + __typename: "BulkProductError"; + field: string | null; + code: ProductErrorCode; + index: number | null; +} diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index e5a191096..c265fd69f 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductCreate // ==================================================== export interface ProductCreate_productCreate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductCreate_productCreate_product_category { diff --git a/src/products/types/ProductDelete.ts b/src/products/types/ProductDelete.ts index c6c12dacf..3d64102f3 100644 --- a/src/products/types/ProductDelete.ts +++ b/src/products/types/ProductDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductDelete // ==================================================== export interface ProductDelete_productDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductDelete_productDelete_product { diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 12e5fefc0..c481ea341 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductImageCreate // ==================================================== export interface ProductImageCreate_productImageCreate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductImageCreate_productImageCreate_product_category { diff --git a/src/products/types/ProductImageDelete.ts b/src/products/types/ProductImageDelete.ts index 4d55c57a8..a598b34a2 100644 --- a/src/products/types/ProductImageDelete.ts +++ b/src/products/types/ProductImageDelete.ts @@ -2,10 +2,18 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductImageDelete // ==================================================== +export interface ProductImageDelete_productImageDelete_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface ProductImageDelete_productImageDelete_product_images { __typename: "ProductImage"; id: string; @@ -19,6 +27,7 @@ export interface ProductImageDelete_productImageDelete_product { export interface ProductImageDelete_productImageDelete { __typename: "ProductImageDelete"; + errors: ProductImageDelete_productImageDelete_errors[]; product: ProductImageDelete_productImageDelete_product | null; } diff --git a/src/products/types/ProductImageReorder.ts b/src/products/types/ProductImageReorder.ts index 5fc5534f0..cddec7610 100644 --- a/src/products/types/ProductImageReorder.ts +++ b/src/products/types/ProductImageReorder.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductImageReorder // ==================================================== export interface ProductImageReorder_productImageReorder_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductImageReorder_productImageReorder_product_images { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 3e1b5b5f7..23687fe07 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductImageUpdate // ==================================================== export interface ProductImageUpdate_productImageUpdate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductImageUpdate_productImageUpdate_product_category { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 7bdf6af0d..20c745df8 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductUpdate // ==================================================== export interface ProductUpdate_productUpdate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductUpdate_productUpdate_product_category { diff --git a/src/products/types/ProductVariantBulkCreate.ts b/src/products/types/ProductVariantBulkCreate.ts index 8c79b0e15..2dc8867e6 100644 --- a/src/products/types/ProductVariantBulkCreate.ts +++ b/src/products/types/ProductVariantBulkCreate.ts @@ -8,23 +8,15 @@ import { ProductVariantBulkCreateInput, ProductErrorCode } from "./../../types/g // GraphQL mutation operation: ProductVariantBulkCreate // ==================================================== -export interface ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors { +export interface ProductVariantBulkCreate_productVariantBulkCreate_errors { __typename: "BulkProductError"; field: string | null; - message: string | null; code: ProductErrorCode; index: number | null; } -export interface ProductVariantBulkCreate_productVariantBulkCreate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface ProductVariantBulkCreate_productVariantBulkCreate { __typename: "ProductVariantBulkCreate"; - bulkProductErrors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[]; errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[]; } diff --git a/src/products/types/ProductVariantBulkDelete.ts b/src/products/types/ProductVariantBulkDelete.ts index 903c21fe1..4463eca72 100644 --- a/src/products/types/ProductVariantBulkDelete.ts +++ b/src/products/types/ProductVariantBulkDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductVariantBulkDelete // ==================================================== export interface ProductVariantBulkDelete_productVariantBulkDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface ProductVariantBulkDelete_productVariantBulkDelete { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 861391ead..51c33a7e3 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, ProductVariantInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeValueInput, ProductVariantInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SimpleProductUpdate // ==================================================== export interface SimpleProductUpdate_productUpdate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface SimpleProductUpdate_productUpdate_product_category { @@ -183,9 +183,9 @@ export interface SimpleProductUpdate_productUpdate { } export interface SimpleProductUpdate_productVariantUpdate_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute_values { diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index 7523a6121..da6dd3939 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -8,11 +8,10 @@ import { ProductVariantCreateInput, ProductErrorCode } from "./../../types/globa // GraphQL mutation operation: VariantCreate // ==================================================== -export interface VariantCreate_productVariantCreate_productErrors { +export interface VariantCreate_productVariantCreate_errors { __typename: "ProductError"; code: ProductErrorCode; field: string | null; - message: string | null; } export interface VariantCreate_productVariantCreate_productVariant_attributes_attribute_values { @@ -114,7 +113,7 @@ export interface VariantCreate_productVariantCreate_productVariant { export interface VariantCreate_productVariantCreate { __typename: "ProductVariantCreate"; - productErrors: VariantCreate_productVariantCreate_productErrors[]; + errors: VariantCreate_productVariantCreate_errors[]; productVariant: VariantCreate_productVariantCreate_productVariant | null; } diff --git a/src/products/types/VariantDelete.ts b/src/products/types/VariantDelete.ts index 38f2f0600..48c85f2c0 100644 --- a/src/products/types/VariantDelete.ts +++ b/src/products/types/VariantDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: VariantDelete // ==================================================== export interface VariantDelete_productVariantDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface VariantDelete_productVariantDelete_productVariant { diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index 1894ecbde..5a5c8ad0a 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: VariantImageAssign // ==================================================== export interface VariantImageAssign_variantImageAssign_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface VariantImageAssign_variantImageAssign_productVariant_attributes_attribute_values { diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index e90b5a43b..4978ee18b 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: VariantImageUnassign // ==================================================== export interface VariantImageUnassign_variantImageUnassign_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute_values { diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 6a4e8bcd6..0028a0887 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -8,11 +8,10 @@ import { AttributeValueInput, ProductErrorCode } from "./../../types/globalTypes // GraphQL mutation operation: VariantUpdate // ==================================================== -export interface VariantUpdate_productVariantUpdate_productErrors { +export interface VariantUpdate_productVariantUpdate_errors { __typename: "ProductError"; code: ProductErrorCode; field: string | null; - message: string | null; } export interface VariantUpdate_productVariantUpdate_productVariant_attributes_attribute_values { @@ -114,7 +113,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant { export interface VariantUpdate_productVariantUpdate { __typename: "ProductVariantUpdate"; - productErrors: VariantUpdate_productVariantUpdate_productErrors[]; + errors: VariantUpdate_productVariantUpdate_errors[]; productVariant: VariantUpdate_productVariantUpdate_productVariant | null; } diff --git a/src/products/types/productBulkDelete.ts b/src/products/types/productBulkDelete.ts index 6334e46f7..3164f7b6c 100644 --- a/src/products/types/productBulkDelete.ts +++ b/src/products/types/productBulkDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: productBulkDelete // ==================================================== export interface productBulkDelete_productBulkDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface productBulkDelete_productBulkDelete { diff --git a/src/products/types/productBulkPublish.ts b/src/products/types/productBulkPublish.ts index da03ab934..f429764a4 100644 --- a/src/products/types/productBulkPublish.ts +++ b/src/products/types/productBulkPublish.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: productBulkPublish // ==================================================== export interface productBulkPublish_productBulkPublish_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface productBulkPublish_productBulkPublish { diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 8bfc63a54..ae7b565e6 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -58,13 +58,6 @@ export const ProductUpdate: React.FC = () => { }) }); navigate(productUrl(data.productCreate.product.id)); - } else { - const attributeError = data.productCreate.errors.find( - err => err.field === "attributes" - ); - if (!!attributeError) { - notify({ text: attributeError.message }); - } } }; @@ -120,10 +113,7 @@ export const ProductUpdate: React.FC = () => { [] ).map(edge => edge.node)} disabled={productCreateOpts.loading} - errors={maybe( - () => productCreateOpts.data.productCreate.errors, - [] - )} + errors={productCreateOpts.data?.productCreate.errors || []} fetchCategories={searchCategory} fetchCollections={searchCollection} fetchProductTypes={searchProductTypes} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index aee72a004..b478e2caa 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -19,6 +19,7 @@ import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import NotFoundPage from "@saleor/components/NotFoundPage"; +import { ProductErrorCode } from "@saleor/types/globalTypes"; import { getMutationState, maybe } from "../../../misc"; import ProductUpdatePage from "../../components/ProductUpdatePage"; import ProductUpdateOperations from "../../containers/ProductUpdateOperations"; @@ -101,13 +102,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { notify({ text: intl.formatMessage(commonMessages.savedChanges) }); - } else { - const attributeError = data.productUpdate.errors.find( - err => err.field === "attributes" - ); - if (!!attributeError) { - notify({ text: attributeError.message }); - } } }; @@ -118,7 +112,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { ); if (imageError) { notify({ - text: imageError.message + text: intl.formatMessage(commonMessages.somethingWentWrong) }); } }; @@ -339,12 +333,10 @@ export const ProductUpdate: React.FC = ({ id, params }) => { defaultPrice={maybe(() => data.product.basePrice.amount.toFixed(2) )} - errors={maybe( - () => - bulkProductVariantCreate.opts.data - .productVariantBulkCreate.bulkProductErrors, - [] - )} + errors={ + bulkProductVariantCreate.opts.data + ?.productVariantBulkCreate.errors || [] + } open={params.action === "create-variants"} attributes={maybe( () => data.product.productType.variantAttributes, diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 888ad7d79..c2e446ab1 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -6,6 +6,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import NotFoundPage from "@saleor/components/NotFoundPage"; +import { commonMessages } from "@saleor/intl"; import { decimal, maybe } from "../../misc"; import ProductVariantCreatePage, { ProductVariantCreatePageSubmitData @@ -35,11 +36,9 @@ export const ProductVariant: React.FC = ({ productId }) => { } const handleCreateSuccess = (data: VariantCreate) => { - if (data.productVariantCreate.productErrors.length === 0) { + if (data.productVariantCreate.errors.length === 0) { notify({ - text: intl.formatMessage({ - defaultMessage: "Product created" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); navigate( productVariantEditUrl( @@ -90,18 +89,16 @@ export const ProductVariant: React.FC = ({ productId }) => { /> shop.defaultCurrency)} - errors={maybe( - () => - variantCreateResult.data.productVariantCreate - .productErrors, + errors={ + variantCreateResult.data?.productVariantCreate.errors || [] - )} + } header={intl.formatMessage({ defaultMessage: "Create Variant", description: "header" })} loading={disableForm} - product={maybe(() => data.product)} + product={data?.product} onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} diff --git a/src/utils/errors/product.ts b/src/utils/errors/product.ts new file mode 100644 index 000000000..3e5e1d9e3 --- /dev/null +++ b/src/utils/errors/product.ts @@ -0,0 +1,65 @@ +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 { BulkProductErrorFragment } from "@saleor/products/types/BulkProductErrorFragment"; +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" + }, + skuUnique: { + defaultMessage: "SKUs must be unique", + description: "bulk variant create error" + }, + variantNoDigitalContent: { + defaultMessage: "This variant does not have any digital content" + } +}); + +function getProductErrorMessage( + err: Omit | undefined, + 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 function getBulkProductErrorMessage( + err: BulkProductErrorFragment | undefined, + intl: IntlShape +): string { + if (err?.code === ProductErrorCode.UNIQUE && err.field === "sku") { + return intl.formatMessage(messages.skuUnique); + } + return getProductErrorMessage(err, intl); +} + +export default getProductErrorMessage;