Fix undefined and null objects handling
This commit is contained in:
parent
16d9d28033
commit
a34d22de8f
5 changed files with 167 additions and 163 deletions
|
@ -128,151 +128,156 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
taxTypes={taxTypeChoices}
|
taxTypes={taxTypeChoices}
|
||||||
warehouses={warehouses}
|
warehouses={warehouses}
|
||||||
>
|
>
|
||||||
{({ change, data, handlers, hasChanged, submit }) => (
|
{({ change, data, handlers, hasChanged, submit }) => {
|
||||||
<Container>
|
// Comparing explicitly to false because `hasVariants` can be undefined
|
||||||
<AppHeader onBack={onBack}>
|
const isSimpleProduct = data.productType?.hasVariants === false;
|
||||||
{intl.formatMessage(sectionNames.products)}
|
|
||||||
</AppHeader>
|
return (
|
||||||
<PageHeader title={header} />
|
<Container>
|
||||||
<Grid>
|
<AppHeader onBack={onBack}>
|
||||||
<div>
|
{intl.formatMessage(sectionNames.products)}
|
||||||
<ProductDetailsForm
|
</AppHeader>
|
||||||
data={data}
|
<PageHeader title={header} />
|
||||||
disabled={disabled}
|
<Grid>
|
||||||
errors={errors}
|
<div>
|
||||||
initialDescription={initialDescription.current}
|
<ProductDetailsForm
|
||||||
onChange={change}
|
data={data}
|
||||||
/>
|
|
||||||
<CardSpacer />
|
|
||||||
{data.attributes.length > 0 && (
|
|
||||||
<ProductAttributes
|
|
||||||
attributes={data.attributes}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={handlers.selectAttribute}
|
initialDescription={initialDescription.current}
|
||||||
onMultiChange={handlers.selectAttributeMultiple}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
)}
|
<CardSpacer />
|
||||||
<CardSpacer />
|
{data.attributes.length > 0 && (
|
||||||
{!data.productType?.hasVariants && (
|
<ProductAttributes
|
||||||
<>
|
attributes={data.attributes}
|
||||||
<ProductShipping
|
|
||||||
data={data}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
weightUnit={weightUnit}
|
onChange={handlers.selectAttribute}
|
||||||
onChange={change}
|
onMultiChange={handlers.selectAttributeMultiple}
|
||||||
/>
|
/>
|
||||||
<ProductPricing
|
)}
|
||||||
currency={currency}
|
<CardSpacer />
|
||||||
data={data}
|
{isSimpleProduct && (
|
||||||
disabled={disabled}
|
<>
|
||||||
errors={errors}
|
<ProductShipping
|
||||||
onChange={change}
|
data={data}
|
||||||
/>
|
disabled={disabled}
|
||||||
<CardSpacer />
|
errors={errors}
|
||||||
<ProductStocks
|
weightUnit={weightUnit}
|
||||||
data={data}
|
onChange={change}
|
||||||
disabled={disabled}
|
/>
|
||||||
hasVariants={false}
|
<ProductPricing
|
||||||
onFormDataChange={change}
|
currency={currency}
|
||||||
errors={errors}
|
data={data}
|
||||||
stocks={data.stocks}
|
disabled={disabled}
|
||||||
warehouses={warehouses}
|
errors={errors}
|
||||||
onChange={handlers.changeStock}
|
onChange={change}
|
||||||
onWarehouseStockAdd={handlers.addStock}
|
/>
|
||||||
onWarehouseStockDelete={handlers.deleteStock}
|
<CardSpacer />
|
||||||
onWarehouseConfigure={onWarehouseConfigure}
|
<ProductStocks
|
||||||
/>
|
data={data}
|
||||||
<CardSpacer />
|
disabled={disabled}
|
||||||
</>
|
hasVariants={false}
|
||||||
)}
|
onFormDataChange={change}
|
||||||
<SeoForm
|
errors={errors}
|
||||||
allowEmptySlug={true}
|
stocks={data.stocks}
|
||||||
helperText={intl.formatMessage({
|
warehouses={warehouses}
|
||||||
defaultMessage:
|
onChange={handlers.changeStock}
|
||||||
"Add search engine title and description to make this product easier to find"
|
onWarehouseStockAdd={handlers.addStock}
|
||||||
})}
|
onWarehouseStockDelete={handlers.deleteStock}
|
||||||
title={data.seoTitle}
|
onWarehouseConfigure={onWarehouseConfigure}
|
||||||
slug={data.slug}
|
/>
|
||||||
slugPlaceholder={data.name}
|
<CardSpacer />
|
||||||
titlePlaceholder={data.name}
|
</>
|
||||||
description={data.seoDescription}
|
)}
|
||||||
descriptionPlaceholder={data.seoTitle}
|
<SeoForm
|
||||||
loading={disabled}
|
allowEmptySlug={true}
|
||||||
onChange={change}
|
helperText={intl.formatMessage({
|
||||||
/>
|
defaultMessage:
|
||||||
<CardSpacer />
|
"Add search engine title and description to make this product easier to find"
|
||||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
})}
|
||||||
</div>
|
title={data.seoTitle}
|
||||||
<div>
|
slug={data.slug}
|
||||||
<ProductOrganization
|
slugPlaceholder={data.name}
|
||||||
canChangeType={true}
|
titlePlaceholder={data.name}
|
||||||
categories={categories}
|
description={data.seoDescription}
|
||||||
categoryInputDisplayValue={selectedCategory}
|
descriptionPlaceholder={data.seoTitle}
|
||||||
collections={collections}
|
loading={disabled}
|
||||||
data={data}
|
onChange={change}
|
||||||
disabled={disabled}
|
/>
|
||||||
errors={errors}
|
<CardSpacer />
|
||||||
fetchCategories={fetchCategories}
|
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||||
fetchCollections={fetchCollections}
|
</div>
|
||||||
fetchMoreCategories={fetchMoreCategories}
|
<div>
|
||||||
fetchMoreCollections={fetchMoreCollections}
|
<ProductOrganization
|
||||||
fetchMoreProductTypes={fetchMoreProductTypes}
|
canChangeType={true}
|
||||||
fetchProductTypes={fetchProductTypes}
|
categories={categories}
|
||||||
productType={data.productType}
|
categoryInputDisplayValue={selectedCategory}
|
||||||
productTypeInputDisplayValue={data.productType?.name || ""}
|
collections={collections}
|
||||||
productTypes={productTypes}
|
data={data}
|
||||||
onCategoryChange={handlers.selectCategory}
|
disabled={disabled}
|
||||||
onCollectionChange={handlers.selectCollection}
|
errors={errors}
|
||||||
onProductTypeChange={handlers.selectProductType}
|
fetchCategories={fetchCategories}
|
||||||
collectionsInputDisplayValue={selectedCollections}
|
fetchCollections={fetchCollections}
|
||||||
/>
|
fetchMoreCategories={fetchMoreCategories}
|
||||||
<CardSpacer />
|
fetchMoreCollections={fetchMoreCollections}
|
||||||
<AvailabilityCard
|
fetchMoreProductTypes={fetchMoreProductTypes}
|
||||||
data={data}
|
fetchProductTypes={fetchProductTypes}
|
||||||
errors={errors}
|
productType={data.productType}
|
||||||
disabled={disabled}
|
productTypeInputDisplayValue={data.productType?.name || ""}
|
||||||
messages={{
|
productTypes={productTypes}
|
||||||
hiddenLabel: intl.formatMessage({
|
onCategoryChange={handlers.selectCategory}
|
||||||
defaultMessage: "Not published",
|
onCollectionChange={handlers.selectCollection}
|
||||||
description: "product label"
|
onProductTypeChange={handlers.selectProductType}
|
||||||
}),
|
collectionsInputDisplayValue={selectedCollections}
|
||||||
hiddenSecondLabel: intl.formatMessage(
|
/>
|
||||||
{
|
<CardSpacer />
|
||||||
defaultMessage: "will become published on {date}",
|
<AvailabilityCard
|
||||||
description: "product publication date label"
|
data={data}
|
||||||
},
|
errors={errors}
|
||||||
{
|
disabled={disabled}
|
||||||
date: localizeDate(data.publicationDate, "L")
|
messages={{
|
||||||
}
|
hiddenLabel: intl.formatMessage({
|
||||||
),
|
defaultMessage: "Not published",
|
||||||
visibleLabel: intl.formatMessage({
|
description: "product label"
|
||||||
defaultMessage: "Published",
|
}),
|
||||||
description: "product label"
|
hiddenSecondLabel: intl.formatMessage(
|
||||||
})
|
{
|
||||||
}}
|
defaultMessage: "will become published on {date}",
|
||||||
onChange={change}
|
description: "product publication date label"
|
||||||
/>
|
},
|
||||||
<CardSpacer />
|
{
|
||||||
<ProductTaxes
|
date: localizeDate(data.publicationDate, "L")
|
||||||
data={data}
|
}
|
||||||
disabled={disabled}
|
),
|
||||||
onChange={change}
|
visibleLabel: intl.formatMessage({
|
||||||
onTaxTypeChange={handlers.selectTaxRate}
|
defaultMessage: "Published",
|
||||||
selectedTaxTypeDisplayName={selectedTaxType}
|
description: "product label"
|
||||||
taxTypes={taxTypes}
|
})
|
||||||
/>
|
}}
|
||||||
</div>
|
onChange={change}
|
||||||
</Grid>
|
/>
|
||||||
<SaveButtonBar
|
<CardSpacer />
|
||||||
onCancel={onBack}
|
<ProductTaxes
|
||||||
onSave={submit}
|
data={data}
|
||||||
state={saveButtonBarState}
|
disabled={disabled}
|
||||||
disabled={disabled || !onSubmit || !hasChanged}
|
onChange={change}
|
||||||
/>
|
onTaxTypeChange={handlers.selectTaxRate}
|
||||||
</Container>
|
selectedTaxTypeDisplayName={selectedTaxType}
|
||||||
)}
|
taxTypes={taxTypes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<SaveButtonBar
|
||||||
|
onCancel={onBack}
|
||||||
|
onSave={submit}
|
||||||
|
state={saveButtonBarState}
|
||||||
|
disabled={disabled || !onSubmit || !hasChanged}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}}
|
||||||
</ProductCreateForm>
|
</ProductCreateForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export { default } from "./ProductCreatePage";
|
export { default } from "./ProductCreatePage";
|
||||||
export * from "./ProductCreatePage";
|
export * from "./ProductCreatePage";
|
||||||
|
export * from "./form";
|
||||||
|
|
|
@ -127,9 +127,10 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
|
||||||
const anchor = React.useRef<HTMLDivElement>();
|
const anchor = React.useRef<HTMLDivElement>();
|
||||||
const [isExpanded, setExpansionState] = React.useState(false);
|
const [isExpanded, setExpansionState] = React.useState(false);
|
||||||
|
|
||||||
const warehousesToAssign = warehouses.filter(
|
const warehousesToAssign =
|
||||||
warehouse => !stocks.some(stock => stock.id === warehouse.id)
|
warehouses?.filter(
|
||||||
);
|
warehouse => !stocks.some(stock => stock.id === warehouse.id)
|
||||||
|
) || [];
|
||||||
const formErrors = getFormErrors(["sku"], errors);
|
const formErrors = getFormErrors(["sku"], errors);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -187,7 +188,7 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Typography>
|
</Typography>
|
||||||
{!warehouses.length && (
|
{!warehouses?.length && (
|
||||||
<Typography color="textSecondary" className={classes.noWarehouseInfo}>
|
<Typography color="textSecondary" className={classes.noWarehouseInfo}>
|
||||||
{hasVariants ? (
|
{hasVariants ? (
|
||||||
<>
|
<>
|
||||||
|
@ -219,7 +220,7 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
{warehouses.length > 0 && (
|
{warehouses?.length > 0 && (
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -90,13 +90,12 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
||||||
const [isModalOpened, setModalStatus] = React.useState(false);
|
const [isModalOpened, setModalStatus] = React.useState(false);
|
||||||
const toggleModal = () => setModalStatus(!isModalOpened);
|
const toggleModal = () => setModalStatus(!isModalOpened);
|
||||||
|
|
||||||
const variantImages = variant?.images?.map(image => image.id) || [];
|
const variantImages = variant?.images?.map(image => image.id);
|
||||||
const productImages =
|
const productImages = variant?.product?.images?.sort((prev, next) =>
|
||||||
variant?.product?.images?.sort((prev, next) =>
|
prev.sortOrder > next.sortOrder ? 1 : -1
|
||||||
prev.sortOrder > next.sortOrder ? 1 : -1
|
);
|
||||||
) || [];
|
|
||||||
const images = productImages
|
const images = productImages
|
||||||
.filter(image => variantImages.indexOf(image.id) !== -1)
|
?.filter(image => variantImages.indexOf(image.id) !== -1)
|
||||||
.sort((prev, next) => (prev.sortOrder > next.sortOrder ? 1 : -1));
|
.sort((prev, next) => (prev.sortOrder > next.sortOrder ? 1 : -1));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import ProductCreatePage, {
|
import ProductCreatePage, {
|
||||||
ProductCreatePageSubmitData
|
ProductCreateFormData
|
||||||
} from "../../../products/components/ProductCreatePage";
|
} from "../../../products/components/ProductCreatePage";
|
||||||
import { product as productFixture } from "../../../products/fixtures";
|
import { product as productFixture } from "../../../products/fixtures";
|
||||||
import { productTypes } from "../../../productTypes/fixtures";
|
import { productTypes } from "../../../productTypes/fixtures";
|
||||||
|
@ -74,17 +74,15 @@ storiesOf("Views / Products / Create product", module)
|
||||||
"productType",
|
"productType",
|
||||||
"category",
|
"category",
|
||||||
"sku"
|
"sku"
|
||||||
] as Array<keyof ProductCreatePageSubmitData | "attributes">).map(
|
] as Array<keyof ProductCreateFormData | "attributes">).map(field => ({
|
||||||
field => ({
|
__typename: "ProductError",
|
||||||
__typename: "ProductError",
|
attributes:
|
||||||
attributes:
|
field === "attributes"
|
||||||
field === "attributes"
|
? [productTypes[0].productAttributes[0].id]
|
||||||
? [productTypes[0].productAttributes[0].id]
|
: null,
|
||||||
: null,
|
code: ProductErrorCode.INVALID,
|
||||||
code: ProductErrorCode.INVALID,
|
field
|
||||||
field
|
}))}
|
||||||
})
|
|
||||||
)}
|
|
||||||
header="Add product"
|
header="Add product"
|
||||||
collections={product.collections}
|
collections={product.collections}
|
||||||
fetchCategories={() => undefined}
|
fetchCategories={() => undefined}
|
||||||
|
@ -94,7 +92,7 @@ storiesOf("Views / Products / Create product", module)
|
||||||
fetchMoreCollections={fetchMoreProps}
|
fetchMoreCollections={fetchMoreProps}
|
||||||
fetchMoreProductTypes={fetchMoreProps}
|
fetchMoreProductTypes={fetchMoreProps}
|
||||||
initial={{
|
initial={{
|
||||||
productType: productTypes[0].id
|
productType: productTypes[0]
|
||||||
}}
|
}}
|
||||||
productTypes={productTypes}
|
productTypes={productTypes}
|
||||||
categories={[product.category]}
|
categories={[product.category]}
|
||||||
|
|
Loading…
Reference in a new issue