diff --git a/CHANGELOG.md b/CHANGELOG.md index e19c7f11f..ac2284e6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ All notable, unreleased changes to this project will be documented in this file. - Update schema with PositiveDecimal type - #695 by @AlicjaSzu - Add error info when fetching taxes - #701 by @dominik-zeglen - Fix return to previous page on screen size change - #710 by @orzechdev +- Fix updating order details on address change #711 - by @orzechdev +- Add no warehouses info when working with stock quantities #713 - by @orzechdev - Add variants reordering possibility - #716 by @orzechdev - Fix avatar change button - #719 by @orzechdev - Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik @@ -104,7 +106,6 @@ All notable, unreleased changes to this project will be documented in this file. - Update product stock management to newest design - #515 by @dominik-zeglen - Handle untracked products - #523 by @dominik-zeglen - Display correct error if there were no graphql errors - #525 by @dominik-zeglen -- Fix updating order details on address change #711 - by @orzechdev ## 2.0.0 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 546019980..435914cf8 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -302,6 +302,10 @@ "context": "variant stock, header", "string": "Stock" }, + "productVariantCreatorWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. You can configure variants without providing stock quantities." + }, "productVariantCreatorWarehouseSectionHeader": { "context": "header", "string": "Warehouses" @@ -310,6 +314,14 @@ "context": "optional field", "string": "Optional" }, + "productVariantWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. To add stock quantity to the variant please configure a warehouse" + }, + "productWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. To add stock quantity to the product please configure a warehouse" + }, "saleDetailsPageCategoriesQuantity": { "context": "number of categories", "string": "Categories ({quantity})" diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 031abec20..21464797b 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -101,6 +101,7 @@ interface ProductCreatePageProps { fetchCategories: (data: string) => void; fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; + onWarehouseConfigure: () => void; onBack?(); onSubmit?(data: ProductCreatePageSubmitData); } @@ -124,7 +125,8 @@ export const ProductCreatePage: React.FC = ({ onBack, fetchProductTypes, weightUnit, - onSubmit + onSubmit, + onWarehouseConfigure }: ProductCreatePageProps) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -297,6 +299,7 @@ export const ProductCreatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index 1f36f6601..9a8d430db 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -21,6 +21,7 @@ import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import Hr from "@saleor/components/Hr"; +import Link from "@saleor/components/Link"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { FormChange } from "@saleor/hooks/useForm"; @@ -41,12 +42,14 @@ export interface ProductStocksProps { data: ProductStockFormData; disabled: boolean; errors: ProductErrorFragment[]; + hasVariants: boolean; stocks: ProductStockInput[]; warehouses: WarehouseFragment[]; onChange: FormsetChange; onFormDataChange: FormChange; onWarehouseStockAdd: (warehouseId: string) => void; onWarehouseStockDelete: (warehouseId: string) => void; + onWarehouseConfigure: () => void; } const useStyles = makeStyles( @@ -75,6 +78,9 @@ const useStyles = makeStyles( marginBottom: theme.spacing(2) } }, + noWarehouseInfo: { + marginTop: theme.spacing() + }, paper: { padding: theme.spacing(2) }, @@ -105,13 +111,15 @@ const useStyles = makeStyles( const ProductStocks: React.FC = ({ data, disabled, + hasVariants, errors, stocks, warehouses, onChange, onFormDataChange, onWarehouseStockAdd, - onWarehouseStockDelete + onWarehouseStockDelete, + onWarehouseConfigure }) => { const classes = useStyles({}); const intl = useIntl(); @@ -178,108 +186,143 @@ const ProductStocks: React.FC = ({ - - - - - - - - - - - - - - - {renderCollection(stocks, stock => ( - - {stock.label} - - + {hasVariants ? ( + <> + ( + {chunks} + ) }} - onChange={event => onChange(stock.id, event.target.value)} - value={stock.value} + /> + + ) : ( + <> + ( + {chunks} + ) + }} + /> + + )} + + )} + + {warehouses.length > 0 && ( +
+ + + + - - onWarehouseStockDelete(stock.id)} - > - - + + + - ))} - {warehousesToAssign.length > 0 && ( - - - - + + {renderCollection(stocks, stock => ( + + {stock.label} + + onChange(stock.id, event.target.value)} + value={stock.value} /> - - - - setExpansionState(false)}> -
- setExpansionState(!isExpanded)} - > - - - - {({ TransitionProps }) => ( - - - {warehousesToAssign.map(warehouse => ( - - onWarehouseStockAdd(warehouse.id) - } - > - {warehouse.name} - - ))} - - - )} - -
-
-
-
- )} - -
+ + + onWarehouseStockDelete(stock.id)} + > + + + + + ))} + {warehousesToAssign.length > 0 && ( + + + + + + + + setExpansionState(false)} + > +
+ setExpansionState(!isExpanded)} + > + + + + {({ TransitionProps }) => ( + + + {warehousesToAssign.map(warehouse => ( + + onWarehouseStockAdd(warehouse.id) + } + > + {warehouse.name} + + ))} + + + )} + +
+
+
+
+ )} + + + )} ); }; diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 79252b195..13e56879d 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -87,6 +87,7 @@ export interface ProductUpdatePageProps extends ListActions { onSubmit?(data: ProductUpdatePageSubmitData); onVariantAdd?(); onSetDefaultVariant(); + onWarehouseConfigure(); } export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { @@ -128,6 +129,7 @@ export const ProductUpdatePage: React.FC = ({ onSetDefaultVariant, onVariantShow, onVariantReorder, + onWarehouseConfigure, isChecked, selected, toggle, @@ -346,6 +348,7 @@ export const ProductUpdatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> )} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 1237d8b98..8c345e4f2 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -58,6 +58,7 @@ interface ProductVariantCreatePageProps { onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; + onWarehouseConfigure: () => void; } const ProductVariantCreatePage: React.FC = ({ @@ -72,7 +73,8 @@ const ProductVariantCreatePage: React.FC = ({ onBack, onSubmit, onVariantClick, - onVariantReorder + onVariantReorder, + onWarehouseConfigure }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -165,6 +167,7 @@ const ProductVariantCreatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx index fa936506a..806d3bb75 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx @@ -140,6 +140,17 @@ storiesOf( step={ProductVariantCreatorStep.prices} warehouses={[props.warehouses[0]]} /> + )) + .add("ship when no warehouses", () => ( + )); storiesOf("Views / Products / Create multiple variants / summary", module) diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx index 51770851a..c07bfa598 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx @@ -123,192 +123,206 @@ const ProductVariantCreatorStock: React.FC = pr })} /> - {warehouses.length > 1 && ( + {!warehouses.length ? ( + + + + ) : ( <> - - - - - acc + attr.values.length, - 0 - ) - }} - /> - -
- {warehouses.map(warehouse => ( - a === b - )} - name={`warehouse:${warehouse.id}`} - label={warehouse.name} - onChange={() => onWarehouseToggle(warehouse.id)} - key={warehouse.id} - /> - ))} -
- -
- - - )} - - - - - } - label={intl.formatMessage({ - defaultMessage: "Apply single stock to all SKUs" - })} - onChange={() => onApplyToAllChange("all")} - /> - {data.stock.mode === "all" && ( -
- {data.warehouses.map((warehouseId, warehouseIndex) => ( -
- - { - warehouses.find(warehouse => warehouse.id === warehouseId) - .name - } - - - onApplyToAllStockChange( - parseInt(event.target.value, 10), - warehouseIndex - ) - } + {warehouses.length > 1 && ( + <> + + + + + acc + attr.values.length, + 0 + ) + }} + /> + +
+ {warehouses.map(warehouse => ( + a === b + )} + name={`warehouse:${warehouse.id}`} + label={warehouse.name} + onChange={() => onWarehouseToggle(warehouse.id)} + key={warehouse.id} + /> + ))}
- ))} -
- )} - - } - label={intl.formatMessage({ - defaultMessage: "Apply unique stock by attribute to each SKU" - })} - onChange={() => onApplyToAllChange("attribute")} - /> - {data.stock.mode === "attribute" && ( - <> - - onAttributeSelect(event.target.value)} + +
+ + + )} + + - {stockAttributeValues && ( - <> -
- -
-
-
- {data.stock.attribute && - data.warehouses.map(warehouseId => ( - - { - warehouses.find( - warehouse => warehouse.id === warehouseId - ).name - } - - ))} - {stockAttributeValues.map(attributeValue => ( - - {attributeValue.name} - {data.warehouses.map( - (warehouseId, warehouseIndex) => ( - value.slug === attributeValue.slug - ).value[warehouseIndex] - } - onChange={event => - onAttributeValueChange( - attributeValue.slug, - parseInt(event.target.value, 10), - warehouseIndex - ) - } - key={warehouseId} - /> - ) - )} - - ))} + + + } + label={intl.formatMessage({ + defaultMessage: "Apply single stock to all SKUs" + })} + onChange={() => onApplyToAllChange("all")} + /> + {data.stock.mode === "all" && ( +
+ {data.warehouses.map((warehouseId, warehouseIndex) => ( +
+ + { + warehouses.find( + warehouse => warehouse.id === warehouseId + ).name + } + + + onApplyToAllStockChange( + parseInt(event.target.value, 10), + warehouseIndex + ) + } + />
-
+ ))} +
+ )} + + } + label={intl.formatMessage({ + defaultMessage: "Apply unique stock by attribute to each SKU" + })} + onChange={() => onApplyToAllChange("attribute")} + /> + {data.stock.mode === "attribute" && ( + <> + + onAttributeSelect(event.target.value)} + /> + {stockAttributeValues && ( + <> +
+ +
+
+
+ {data.stock.attribute && + data.warehouses.map(warehouseId => ( + + { + warehouses.find( + warehouse => warehouse.id === warehouseId + ).name + } + + ))} + {stockAttributeValues.map(attributeValue => ( + + {attributeValue.name} + {data.warehouses.map( + (warehouseId, warehouseIndex) => ( + + value.slug === attributeValue.slug + ).value[warehouseIndex] + } + onChange={event => + onAttributeValueChange( + attributeValue.slug, + parseInt(event.target.value, 10), + warehouseIndex + ) + } + key={warehouseId} + /> + ) + )} + + ))} +
+
+ + )} + + )} + {data.stock.mode === "attribute" && !!data.stock.attribute && ( + <> + +
)} - - )} - {data.stock.mode === "attribute" && !!data.stock.attribute && ( - <> -
- - )} - - } - label={intl.formatMessage({ - defaultMessage: "Skip stock for now" - })} - onChange={() => onApplyToAllChange("skip")} - /> - + } + label={intl.formatMessage({ + defaultMessage: "Skip stock for now" + })} + onChange={() => onApplyToAllChange("skip")} + /> + + + )} ); diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 4d7643521..d875acf0d 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -70,6 +70,7 @@ interface ProductVariantPageProps { onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); + onWarehouseConfigure(); } const ProductVariantPage: React.FC = ({ @@ -88,7 +89,8 @@ const ProductVariantPage: React.FC = ({ onSubmit, onVariantClick, onVariantReorder, - onSetDefaultVariant + onSetDefaultVariant, + onWarehouseConfigure }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), @@ -240,6 +242,7 @@ const ProductVariantPage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 138e24e38..b5df23fcd 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -14,6 +14,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -180,6 +181,7 @@ export const ProductCreateView: React.FC = () => { )} onBack={handleBack} onSubmit={handleSubmit} + onWarehouseConfigure={() => navigate(warehouseAddPath)} saveButtonBarState={productCreateOpts.status} fetchMoreCategories={{ hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 4b03c68d6..294db7989 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -33,6 +33,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -304,6 +305,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} onSubmit={handleSubmit} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index ebd7e8b93..2de88694b 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -13,6 +13,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React, { useEffect, useState } from "react"; import { useIntl } from "react-intl"; @@ -215,6 +216,7 @@ export const ProductVariant: React.FC = ({ onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} onSubmit={handleSubmit} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 6d4df390d..80516e2b2 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -10,6 +10,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -142,6 +143,7 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantReorder={handleVariantReorder} saveButtonBarState={variantCreateResult.status} warehouses={ diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 71cac422c..a4478f4a0 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -133929,6 +133929,407 @@ exports[`Storyshots Views / Products / Create multiple variants / prices and SKU
`; +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs ship when no warehouses 1`] = ` +
+
+
+
+ + Price + +
+
+
+
+
+
+ +
+
+ +
+ + USD + +
+
+
+ +
+
+
+
+
+ Choose attribute +
+
+
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+
+ 100g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ 500g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ There are no warehouses set up for your store. You can configure variants without providing stock quantities. +
+
+
+
+
+`; + exports[`Storyshots Views / Products / Create multiple variants / summary default 1`] = `
`; +exports[`Storyshots Views / Products / Create product variant no warehouses 1`] = ` +
+
+
+
+
+ Add variant +
+
+
+
+
+
+
+
+
+ + Variants + +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ New Variant +
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+

+ Optional +

+
+
+
+
+
+
+
+
+ + Shipping + +
+
+
+
+
+
+
+ +
+ +
+
+ kg +
+
+ +
+
+
+
+
+
+
+
+ + Inventory + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + Quantity + +
+
+
+ There are no warehouses set up for your store. To add stock quantity to the variant please + + configure a warehouse + +
+
+
+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+ +
+
+
+ +
+`; + exports[`Storyshots Views / Products / Create product variant when loading data 1`] = `
`; +exports[`Storyshots Views / Products / Product edit no stock, no variants and no warehouses 1`] = ` +
+
+
+
+
+ Ergonomic Plastic Bacon +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ Description +
+
+ +
+
+
+
+
+
+ + + bold + + +
+
+
+
+ + + italic + + +
+
+
+
+ + + strikethrough + + +
+
+

+
+ + + h1 + + +
+

+

+
+ + + h2 + + +
+

+

+
+ + + h3 + + +
+

+
+
+ + + blockquote + + +
+
+
    +
  • +
    + + + ul + + +
    +
  • +
+
    +
  1. +
    + + + ol + + +
    +
  2. +
+
+
+ + + link + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Images + +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 2 Attributes +
+
+ +
+
+
+
+
+ Borders +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Legacy +
+
+
+
+
+ + +
+
+
+
+
+
+ Auto Loan Account +
+ +
+
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+ + Shipping + +
+
+
+
+
+
+
+ +
+ +
+
+ KG +
+
+ +
+
+
+
+
+
+
+
+ + Inventory + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + Quantity + +
+
+
+ There are no warehouses set up for your store. To add stock quantity to the product please + + configure a warehouse + +
+
+
+
+
+
+ + Search Engine Preview + +
+ +
+
+
+
+
+
+ Add search engine title and description to make this product easier to find +
+
+
+
+
+
+ + Metadata + +
+
+
+
+ + + + + + + + + + + + +