Fix product creation flow for simple products (#4081)
* Fix simple product creation * Fix simple product creation
This commit is contained in:
parent
3c1d84f775
commit
498feff0a2
4 changed files with 91 additions and 11 deletions
|
@ -233,7 +233,6 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
assignReferencesAttributeId,
|
||||
data.attributes,
|
||||
);
|
||||
|
||||
return (
|
||||
<DetailPageLayout>
|
||||
<TopNav href={productListUrl()} title={header} />
|
||||
|
@ -275,7 +274,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
/>
|
||||
<ProductVariantPrice
|
||||
ProductVariantChannelListings={data.channelListings}
|
||||
errors={channelsErrors}
|
||||
errors={[...errors, ...channelsErrors]}
|
||||
loading={loading}
|
||||
onChange={handlers.changeChannelPrice}
|
||||
/>
|
||||
|
|
|
@ -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<ProductErrorFragment | ProductChannelListingErrorFragment>;
|
||||
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 =>
|
||||
|
|
63
src/products/utils/validation.test.ts
Normal file
63
src/products/utils/validation.test.ts
Normal file
|
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue