Handle attribute errors in product views

This commit is contained in:
dominik-zeglen 2020-09-29 17:04:48 +02:00
parent 369fd09853
commit ee1ecff1f8
6 changed files with 51 additions and 26 deletions

View file

@ -8,10 +8,10 @@ import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType
} from "@saleor/components/SingleAutocompleteSelectField";
import Skeleton from "@saleor/components/Skeleton";
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
import { ProductVariant_attributes_attribute_values } from "@saleor/fragments/types/ProductVariant";
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
import { commonMessages } from "@saleor/intl";
import { VariantCreate_productVariantCreate_errors } from "@saleor/products/types/VariantCreate";
import { getProductVariantAttributeErrorMessage } from "@saleor/utils/errors/product";
import React from "react";
import { useIntl } from "react-intl";
@ -27,7 +27,7 @@ export type VariantAttributeInput = FormsetAtomicData<
interface ProductVariantAttributesProps {
attributes: VariantAttributeInput[];
disabled: boolean;
errors: VariantCreate_productVariantCreate_errors[];
errors: ProductErrorFragment[];
onChange: FormsetChange<VariantAttributeInputData>;
}
@ -84,31 +84,46 @@ const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
{attributes === undefined ? (
<Skeleton />
) : (
attributes.map(attribute => (
<SingleAutocompleteSelectField
key={attribute.id}
disabled={disabled}
displayValue={getAttributeDisplayValue(
attribute.id,
attribute.value,
attributes
)}
label={attribute.label}
name={`attribute:${attribute.id}`}
onChange={event => onChange(attribute.id, event.target.value)}
value={getAttributeValue(attribute.id, attributes)}
choices={getAttributeValueChoices(attribute.id, attributes)}
allowCustomValues
data-test="variant-attribute-input"
/>
))
attributes.map(attribute => {
const error = errors.find(
err => err.attributeId === attribute.id
);
return (
<SingleAutocompleteSelectField
key={attribute.id}
disabled={disabled}
displayValue={getAttributeDisplayValue(
attribute.id,
attribute.value,
attributes
)}
emptyOption
error={!!error}
helperText={getProductVariantAttributeErrorMessage(
error,
intl
)}
label={attribute.label}
name={`attribute:${attribute.id}`}
onChange={event => onChange(attribute.id, event.target.value)}
value={getAttributeValue(attribute.id, attributes)}
choices={getAttributeValueChoices(attribute.id, attributes)}
allowCustomValues
data-test="variant-attribute-input"
/>
);
})
)}
</Grid>
{errors.length > 0 && (
<>
<FormSpacer />
{errors
.filter(error => error.field === "attributes")
.filter(
error =>
error.field === "attributes" && error.attributeId === null
)
.map(error => (
<Typography color="error" key={error.code}>
{getProductVariantAttributeErrorMessage(error, intl)}

View file

@ -28,6 +28,9 @@ const useStyles = makeStyles(
colName: {
paddingLeft: 0
},
firstVariant: {
width: 88
},
link: {
cursor: "pointer"
},
@ -126,7 +129,10 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
className={classNames(
classes.colAvatar,
classes.tabActive,
classes.noHandle
classes.noHandle,
{
[classes.firstVariant]: variants.length === 0
}
)}
thumbnail={null}
colSpan={2}

View file

@ -8,13 +8,13 @@ import { MetadataFormData } from "@saleor/components/Metadata";
import Metadata from "@saleor/components/Metadata/Metadata";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
import { ProductVariant } from "@saleor/fragments/types/ProductVariant";
import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment";
import useFormset, {
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate";
import {
getAttributeInputFromVariant,
getStockInputFromVariant
@ -56,7 +56,7 @@ export interface ProductVariantPageSubmitData
interface ProductVariantPageProps {
defaultWeightUnit: string;
variant?: ProductVariant;
errors: VariantUpdate_productVariantUpdate_errors[];
errors: ProductErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState;
loading?: boolean;
placeholderImage?: string;

View file

@ -36,14 +36,17 @@ storiesOf("Views / Products / Create product variant", module)
disabled={false}
errors={[
{
attributeId: product.productType.variantAttributes[0].id,
code: ProductErrorCode.REQUIRED,
field: "attributes"
},
{
attributeId: null,
code: ProductErrorCode.UNIQUE,
field: "attributes"
},
{
attributeId: null,
code: ProductErrorCode.ALREADY_EXISTS,
field: "sku"
}

View file

@ -86,14 +86,17 @@ storiesOf("Views / Products / Product variant details", module)
saveButtonBarState="default"
errors={[
{
attributeId: variant.attributes[0].attribute.id,
code: ProductErrorCode.REQUIRED,
field: "attributes"
},
{
attributeId: null,
code: ProductErrorCode.UNIQUE,
field: "attributes"
},
{
attributeId: null,
code: ProductErrorCode.ALREADY_EXISTS,
field: "sku"
}

View file

@ -83,8 +83,6 @@ export function getProductVariantAttributeErrorMessage(
): string {
if (err) {
switch (err.code) {
case ProductErrorCode.REQUIRED:
return intl.formatMessage(messages.attributeRequired);
case ProductErrorCode.UNIQUE:
return intl.formatMessage(messages.variantUnique);
default: