diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 848f5b4b5..391a5c526 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -233,7 +233,6 @@ export const ProductCreatePage: React.FC = ({ assignReferencesAttributeId, data.attributes, ); - return ( @@ -275,7 +274,7 @@ export const ProductCreatePage: React.FC = ({ /> diff --git a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx index c4a070d69..a3b2255e6 100644 --- a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx +++ b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx @@ -9,11 +9,15 @@ import PriceField from "@dashboard/components/PriceField"; import ResponsiveTable from "@dashboard/components/ResponsiveTable"; import Skeleton from "@dashboard/components/Skeleton"; import TableRowLink from "@dashboard/components/TableRowLink"; -import { ProductChannelListingErrorFragment } from "@dashboard/graphql"; +import { + ProductChannelListingErrorFragment, + ProductErrorFragment, +} from "@dashboard/graphql"; import { renderCollection } from "@dashboard/misc"; import { getFormChannelError, getFormChannelErrors, + getFormErrors, } from "@dashboard/utils/errors"; import getProductErrorMessage from "@dashboard/utils/errors/product"; import { TableBody, TableCell, TableHead } from "@material-ui/core"; @@ -23,7 +27,7 @@ import { FormattedMessage, MessageDescriptor, useIntl } from "react-intl"; interface ProductVariantPriceProps { ProductVariantChannelListings?: ChannelData[]; - errors?: ProductChannelListingErrorFragment[]; + errors: Array; loading?: boolean; disabled?: boolean; onChange?: ( @@ -47,7 +51,10 @@ export const ProductVariantPrice: React.FC< disabledMessage, } = props; const intl = useIntl(); - const formErrors = getFormChannelErrors(["price", "costPrice"], errors); + const channelErrors = errors.filter( + e => "channels" in e, + ) as ProductChannelListingErrorFragment[]; + const apiErrors = getFormChannelErrors(["price", "costPrice"], channelErrors); if (disabled || !ProductVariantChannelListings.length) { return ( @@ -120,12 +127,15 @@ export const ProductVariantPrice: React.FC< {renderCollection( ProductVariantChannelListings, (listing, index) => { - const priceError = getFormChannelError( - formErrors.price, - listing.id, - ); + const fieldName = `${listing.id}-channel-price`; + const formErrors = getFormErrors([fieldName], errors); + + const priceError = + getFormChannelError(apiErrors.price, listing.id) || + formErrors[fieldName]; + const costPriceError = getFormChannelError( - formErrors.costPrice, + apiErrors.costPrice, listing.id, ); @@ -142,7 +152,7 @@ export const ProductVariantPrice: React.FC< id: "b1zuN9", defaultMessage: "Price", })} - name={`${listing.id}-channel-price`} + name={fieldName} value={listing.price || ""} currencySymbol={listing.currency} onChange={e => diff --git a/src/products/utils/validation.test.ts b/src/products/utils/validation.test.ts new file mode 100644 index 000000000..c53a0d754 --- /dev/null +++ b/src/products/utils/validation.test.ts @@ -0,0 +1,63 @@ +import { ProductCreateData } from "../components/ProductCreatePage"; +import { validateProductCreateData } from "./validation"; + +describe("validateProductCreateData", () => { + it("returns errors when there is no productType or name", () => { + // Arrange + const data = { productType: "" } as unknown as ProductCreateData; + + // Act + const errors = validateProductCreateData(data); + + // Assert + expect(errors).toEqual([ + { + __typename: "ProductError", + attributes: [], + code: "REQUIRED", + field: "productType", + message: null, + }, + { + __typename: "ProductError", + attributes: [], + code: "REQUIRED", + field: "name", + message: null, + }, + ]); + }); + + it("returns errors when there is no prices for channels", () => { + // Arrange + const data = { + productType: "something", + name: "something", + channelListings: [ + { id: "chann-1", price: "" }, + { id: "chann-2", price: "" }, + ], + } as unknown as ProductCreateData; + + // Act + const errors = validateProductCreateData(data); + + // Assert + expect(errors).toEqual([ + { + __typename: "ProductError", + attributes: [], + code: "REQUIRED", + field: "chann-1-channel-price", + message: null, + }, + { + __typename: "ProductError", + attributes: [], + code: "REQUIRED", + field: "chann-2-channel-price", + message: null, + }, + ]); + }); +}); diff --git a/src/products/utils/validation.ts b/src/products/utils/validation.ts index 53420a3b1..1c67f2089 100644 --- a/src/products/utils/validation.ts +++ b/src/products/utils/validation.ts @@ -34,6 +34,14 @@ export const validateProductCreateData = (data: ProductCreateData) => { errors = [...errors, createEmptyRequiredError("name")]; } + if (data.channelListings) { + const emptyPrices = data.channelListings + .filter(channel => channel.price?.length === 0) + .map(({ id }) => createEmptyRequiredError(`${id}-channel-price`)); + + errors = [...errors, ...emptyPrices]; + } + return errors; };