From cbeed52a3022017fc70ac0922ffb05d0e5288c6c Mon Sep 17 00:00:00 2001 From: JanChodorowski Date: Thu, 14 Oct 2021 14:15:59 +0300 Subject: [PATCH] [Fix] preorder fixes (#1477) * Fix creating variant without preorder * Fix creating simple product in preorder * Proper stock display for variant in preorder * Fix ending preorder for simple product * CR response * CR response * Fix global threshold empty input --- locale/defaultMessages.json | 8 +++ .../components/ProductCreatePage/form.tsx | 4 +- .../ProductStocks/ProductStocks.tsx | 4 +- .../ProductUpdatePage.test.tsx | 1 + .../ProductUpdatePage/ProductUpdatePage.tsx | 7 ++ .../components/ProductUpdatePage/form.tsx | 2 +- .../ProductVariantCreatePage/form.tsx | 2 +- .../components/ProductVariantPage/form.tsx | 4 +- .../ProductVariants/ProductVariants.tsx | 19 ++++++ src/products/utils/data.ts | 4 +- src/products/views/ProductCreate/handlers.ts | 8 +++ .../views/ProductUpdate/ProductUpdate.tsx | 30 +++++++++ .../views/ProductUpdate/handlers/utils.ts | 4 +- src/products/views/ProductVariant.tsx | 6 +- src/products/views/ProductVariantCreate.tsx | 12 ++-- .../__snapshots__/Stories.test.ts.snap | 64 +++++++++---------- .../stories/products/ProductUpdatePage.tsx | 1 + 17 files changed, 131 insertions(+), 49 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index a1c34d1e1..fcfbd1404 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -6534,6 +6534,10 @@ "context": "button", "string": "Create variants" }, + "src_dot_products_dot_components_dot_ProductVariants_dot_1819927358": { + "context": "product variant preorder threshold", + "string": "In preorder" + }, "src_dot_products_dot_components_dot_ProductVariants_dot_2153006789": { "context": "section header", "string": "Variants" @@ -6565,6 +6569,10 @@ "context": "product variant inventory", "string": "Not stocked" }, + "src_dot_products_dot_components_dot_ProductVariants_dot_4232843317": { + "context": "product variant preorder threshold", + "string": "{globalThreshold} Global threshold" + }, "src_dot_products_dot_components_dot_ProductVariants_dot_693960049": { "string": "SKU" }, diff --git a/src/products/components/ProductCreatePage/form.tsx b/src/products/components/ProductCreatePage/form.tsx index c2193fb17..c0e168d8c 100644 --- a/src/products/components/ProductCreatePage/form.tsx +++ b/src/products/components/ProductCreatePage/form.tsx @@ -72,7 +72,7 @@ export interface ProductCreateFormData extends MetadataFormData { taxCode: string; trackInventory: boolean; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime: string; @@ -185,7 +185,7 @@ function useProductCreateForm( trackInventory: false, weight: "", globalSoldUnits: 0, - globalThreshold: 0, + globalThreshold: "", isPreorder: false, hasPreorderEndDate: false, preorderEndDateTime: "" diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index 1197c7bd9..2b2f1b035 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -57,7 +57,7 @@ export interface ProductStockFormData { sku: string; trackInventory: boolean; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime?: string; @@ -201,7 +201,7 @@ const ProductStocks: React.FC = ({ const intl = useIntl(); const anchor = React.useRef(); const [isExpanded, setExpansionState] = React.useState(false); - const unitsLeft = data.globalThreshold - data.globalSoldUnits; + const unitsLeft = parseInt(data.globalThreshold, 10) - data.globalSoldUnits; const warehousesToAssign = warehouses?.filter( diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx index e636a113e..f6264e1f1 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx @@ -59,6 +59,7 @@ const props: ProductUpdatePageProps = { onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, + onVariantEndPreorderDialogOpen: () => undefined, onWarehouseConfigure: () => undefined, openChannelsModal: () => undefined, placeholderImage, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 529798670..7bd058d5e 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -113,6 +113,7 @@ export interface ProductUpdatePageProps extends ListActions, ChannelProps { onVariantsAdd: () => void; onVariantShow: (id: string) => () => void; onVariantReorder: ReorderAction; + onVariantEndPreorderDialogOpen: () => void; onImageDelete: (id: string) => () => void; onSubmit: (data: ProductUpdatePageSubmitData) => SubmitPromise; openChannelsModal: () => void; @@ -183,6 +184,7 @@ export const ProductUpdatePage: React.FC = ({ onSetDefaultVariant, onVariantShow, onVariantReorder, + onVariantEndPreorderDialogOpen, onWarehouseConfigure, isChecked, isMediaUrlModalVisible, @@ -385,6 +387,11 @@ export const ProductUpdatePage: React.FC = ({ handlers.changeChannelPreorder } productVariantChannelListings={data.channelListings} + onEndPreorderTrigger={ + !!variants?.[0]?.preorder + ? () => onVariantEndPreorderDialogOpen() + : null + } data={data} disabled={disabled} hasVariants={false} diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index 746cffa66..8c92d13bb 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -81,7 +81,7 @@ export interface ProductUpdateFormData extends MetadataFormData { taxCode: string; trackInventory: boolean; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime?: string; diff --git a/src/products/components/ProductVariantCreatePage/form.tsx b/src/products/components/ProductVariantCreatePage/form.tsx index 4e2d78fc5..f8551c9bd 100644 --- a/src/products/components/ProductVariantCreatePage/form.tsx +++ b/src/products/components/ProductVariantCreatePage/form.tsx @@ -34,7 +34,7 @@ export interface ProductVariantCreateFormData extends MetadataFormData { trackInventory: boolean; weight: string; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime?: string; diff --git a/src/products/components/ProductVariantPage/form.tsx b/src/products/components/ProductVariantPage/form.tsx index fec4d7ea7..9967d0f99 100644 --- a/src/products/components/ProductVariantPage/form.tsx +++ b/src/products/components/ProductVariantPage/form.tsx @@ -56,7 +56,7 @@ export interface ProductVariantUpdateFormData extends MetadataFormData { trackInventory: boolean; weight: string; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime?: string; @@ -161,7 +161,7 @@ function useProductVariantUpdateForm( sku: variant?.sku || "", trackInventory: variant?.trackInventory, isPreorder: !!variant?.preorder || false, - globalThreshold: variant?.preorder?.globalThreshold || null, + globalThreshold: variant?.preorder?.globalThreshold?.toString() || null, globalSoldUnits: variant?.preorder?.globalSoldUnits || 0, hasPreorderEndDate: !!variant?.preorder?.endDate, preorderEndDateTime: variant?.preorder?.endDate, diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 5ea104823..c9e81fbb6 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -137,6 +137,25 @@ function getAvailabilityLabel( variant: ProductDetails_product_variants, numAvailable: number ): string { + if (variant.preorder) { + if (variant.preorder.globalThreshold) { + return intl.formatMessage( + { + defaultMessage: "{globalThreshold} Global threshold", + description: "product variant preorder threshold" + }, + { + globalThreshold: variant.preorder.globalThreshold + } + ); + } + + return intl.formatMessage({ + defaultMessage: "In preorder", + description: "product variant preorder threshold" + }); + } + const variantStock = variant.stocks.find(s => s.warehouse.id === warehouse); if (!!warehouse) { diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 09cc02433..437bfc34a 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -227,7 +227,7 @@ export interface ProductUpdatePageFormData extends MetadataFormData { trackInventory: boolean; weight: string; isPreorder: boolean; - globalThreshold: number; + globalThreshold: string; globalSoldUnits: number; hasPreorderEndDate: boolean; preorderEndDateTime?: string; @@ -273,7 +273,7 @@ export function getProductUpdatePageFormData( trackInventory: !!variant?.trackInventory, weight: product?.weight?.value.toString() || "", isPreorder: !!variant?.preorder || false, - globalThreshold: variant?.preorder?.globalThreshold || 0, + globalThreshold: variant?.preorder?.globalThreshold?.toString() || "", globalSoldUnits: variant?.preorder?.globalSoldUnits || 0, hasPreorderEndDate: !!variant?.preorder?.endDate, preorderEndDateTime: variant?.preorder?.endDate diff --git a/src/products/views/ProductCreate/handlers.ts b/src/products/views/ProductCreate/handlers.ts index 587457dc4..b32e80067 100644 --- a/src/products/views/ProductCreate/handlers.ts +++ b/src/products/views/ProductCreate/handlers.ts @@ -61,6 +61,14 @@ const getSimpleProductVariables = ( quantity: parseInt(stock.value, 10), warehouse: stock.id })), + preorder: formData.isPreorder + ? { + globalThreshold: formData.globalThreshold + ? parseInt(formData.globalThreshold, 10) + : null, + endDate: formData.preorderEndDateTime || null + } + : null, trackInventory: formData.trackInventory } }); diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 54b6ee28d..eec663d4c 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -30,6 +30,7 @@ import useShop from "@saleor/hooks/useShop"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { commonMessages, errorMessages } from "@saleor/intl"; import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog"; +import ProductVariantEndPreorderDialog from "@saleor/products/components/ProductVariantEndPreorderDialog"; import { useProductChannelListingUpdate, useProductDeleteMutation, @@ -39,6 +40,7 @@ import { useProductUpdateMutation, useProductVariantBulkDeleteMutation, useProductVariantChannelListingUpdate, + useProductVariantPreorderDeactivateMutation, useProductVariantReorderMutation, useSimpleProductUpdateMutation, useVariantCreateMutation @@ -265,6 +267,11 @@ export const ProductUpdate: React.FC = ({ id, params }) => { ProductUrlQueryParams >(navigate, params => productUrl(id, params), params); + const [ + isEndPreorderModalOpened, + setIsEndPreorderModalOpened + ] = React.useState(false); + const product = data?.product; const allChannels: ChannelData[] = createChannelsDataWithPrice( @@ -406,6 +413,18 @@ export const ProductUpdate: React.FC = ({ id, params }) => { reorderProductVariants({ variables }) ); + const handleDeactivatePreorder = async () => { + await handleDeactivateVariantPreorder(product.variants[0].id); + setIsEndPreorderModalOpened(false); + }; + + const [ + deactivatePreorder, + deactivatePreoderOpts + ] = useProductVariantPreorderDeactivateMutation({}); + const handleDeactivateVariantPreorder = (id: string) => + deactivatePreorder({ variables: { id } }); + const handleAssignAttributeReferenceClick = (attribute: AttributeInput) => navigate( productUrl(id, { @@ -424,6 +443,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { updateChannelsOpts.loading || updateVariantChannelsOpts.loading || productVariantCreateOpts.loading || + deactivatePreoderOpts.loading || deleteAttributeValueOpts.loading || createProductMediaOpts.loading || loading; @@ -563,6 +583,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onVariantShow={variantId => () => navigate(productVariantEditUrl(product.id, variantId))} onVariantReorder={handleVariantReorder} + onVariantEndPreorderDialogOpen={() => setIsEndPreorderModalOpened(true)} onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} @@ -647,6 +668,15 @@ export const ProductUpdate: React.FC = ({ id, params }) => { option === "multiple" ? handleVariantsAdd() : handleVariantAdd() } /> + {isSimpleProduct && !!product?.variants?.[0].preorder && ( + setIsEndPreorderModalOpened(false)} + onConfirm={handleDeactivatePreorder} + open={isEndPreorderModalOpened} + variantGlobalSoldUnits={product.variants[0].preorder.globalSoldUnits} + /> + )} ); }; diff --git a/src/products/views/ProductUpdate/handlers/utils.ts b/src/products/views/ProductUpdate/handlers/utils.ts index a69c539f3..1096a1841 100644 --- a/src/products/views/ProductUpdate/handlers/utils.ts +++ b/src/products/views/ProductUpdate/handlers/utils.ts @@ -38,7 +38,9 @@ export const getSimpleProductVariables = ( sku: data.sku, trackInventory: data.trackInventory, preorder: { - globalThreshold: data.globalThreshold, + globalThreshold: data.globalThreshold + ? parseInt(data.globalThreshold, 10) + : null, endDate: data.preorderEndDateTime } }, diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index a06e09284..c1eb7f37f 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -205,7 +205,7 @@ export const ProductVariant: React.FC = ({ deactivatePreorder, deactivatePreoderOpts ] = useProductVariantPreorderDeactivateMutation({}); - const handleDeactivateVariantPreorder = async (id: string) => + const handleDeactivateVariantPreorder = (id: string) => deactivatePreorder({ variables: { id } }); const [ @@ -282,7 +282,9 @@ export const ProductVariant: React.FC = ({ trackInventory: data.trackInventory, preorder: data.isPreorder ? { - globalThreshold: data.globalThreshold, + globalThreshold: data.globalThreshold + ? parseInt(data.globalThreshold, 10) + : null, endDate: data?.preorderEndDateTime || null } : null, diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 701ec28f0..5bee3c4c9 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -119,10 +119,14 @@ export const ProductVariant: React.FC = ({ })), trackInventory: true, weight: weight(formData.weight), - preorder: { - globalThreshold: formData.globalThreshold, - endDate: formData.preorderEndDateTime - } + preorder: formData.isPreorder + ? { + globalThreshold: formData.globalThreshold + ? parseInt(formData.globalThreshold, 10) + : null, + endDate: formData.preorderEndDateTime || null + } + : null }, firstValues: 10 } diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index c1059bdb3..b258655ec 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -199117,7 +199117,7 @@ exports[`Storyshots Views / Products / Product edit form errors 1`] = ` class="MuiTableCell-root-id MuiTableCell-body-id ProductVariants-colInventory-id" data-test="inventory" > - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder - 3 available at 2 locations + In preorder - 11 available at 1 location + In preorder