diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index 8b52f63e7..086d657b7 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -54,6 +54,7 @@ export interface CreateMultipleVariantsData_product_productType_variantAttribute export interface CreateMultipleVariantsData_product_productType { __typename: "ProductType"; + id: string; variantAttributes: (CreateMultipleVariantsData_product_productType_variantAttributes | null)[] | null; } diff --git a/src/products/types/Product.ts b/src/products/types/Product.ts index bf6d5b287..a0420a20a 100644 --- a/src/products/types/Product.ts +++ b/src/products/types/Product.ts @@ -54,8 +54,8 @@ export interface Product_productType_variantAttributes { export interface Product_productType { __typename: "ProductType"; - variantAttributes: (Product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (Product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index 07e21aa5a..f0b898c23 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -60,8 +60,8 @@ export interface ProductCreate_productCreate_product_productType_variantAttribut export interface ProductCreate_productCreate_product_productType { __typename: "ProductType"; - variantAttributes: (ProductCreate_productCreate_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (ProductCreate_productCreate_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 19b2b3e40..ed22c7264 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -54,8 +54,8 @@ export interface ProductDetails_product_productType_variantAttributes { export interface ProductDetails_product_productType { __typename: "ProductType"; - variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 6c6d583a9..3c1ad318d 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -60,8 +60,8 @@ export interface ProductImageCreate_productImageCreate_product_productType_varia export interface ProductImageCreate_productImageCreate_product_productType { __typename: "ProductType"; - variantAttributes: (ProductImageCreate_productImageCreate_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (ProductImageCreate_productImageCreate_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 5bb8acfea..becd44e2c 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -60,8 +60,8 @@ export interface ProductImageUpdate_productImageUpdate_product_productType_varia export interface ProductImageUpdate_productImageUpdate_product_productType { __typename: "ProductType"; - variantAttributes: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index f0b15d703..f9c8c7a7e 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -60,8 +60,8 @@ export interface ProductUpdate_productUpdate_product_productType_variantAttribut export interface ProductUpdate_productUpdate_product_productType { __typename: "ProductType"; - variantAttributes: (ProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (ProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/products/types/ProductVariantAttributesFragment.ts b/src/products/types/ProductVariantAttributesFragment.ts index 462cbd1e6..02d1e7847 100644 --- a/src/products/types/ProductVariantAttributesFragment.ts +++ b/src/products/types/ProductVariantAttributesFragment.ts @@ -54,6 +54,7 @@ export interface ProductVariantAttributesFragment_productType_variantAttributes export interface ProductVariantAttributesFragment_productType { __typename: "ProductType"; + id: string; variantAttributes: (ProductVariantAttributesFragment_productType_variantAttributes | null)[] | null; } diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index adfa2075b..a6515ab73 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -60,8 +60,8 @@ export interface SimpleProductUpdate_productUpdate_product_productType_variantAt export interface SimpleProductUpdate_productUpdate_product_productType { __typename: "ProductType"; - variantAttributes: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; id: string; + variantAttributes: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; name: string; hasVariants: boolean; } diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index c8047c838..84167ace5 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -11,9 +11,10 @@ import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ShippingErrorFragment } from "@saleor/shipping/types/ShippingErrorFragment"; -import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { getStringOrPlaceholder } from "../../../misc"; import { FetchMoreProps, SearchProps } from "../../../types"; import { ShippingMethodTypeEnum } from "../../../types/globalTypes"; @@ -27,7 +28,7 @@ import ShippingZoneWarehouses from "../ShippingZoneWarehouses"; export interface FormData { name: string; - warehouse: string; + warehouses: string[]; } export interface ShippingZoneDetailsPageProps @@ -86,20 +87,26 @@ const ShippingZoneDetailsPage: React.FC = ({ const initialForm: FormData = { name: shippingZone?.name || "", - warehouse: shippingZone?.warehouses[0]?.id || null + warehouses: shippingZone?.warehouses?.map(warehouse => warehouse.id) || [] }; - const [warehouseDisplayValue, setWarehouseDisplayValue] = useStateFromProps( - shippingZone?.warehouses[0]?.name || "" + const [warehouseDisplayValues, setWarehouseDisplayValues] = useStateFromProps< + MultiAutocompleteChoiceType[] + >( + shippingZone?.warehouses?.map(warehouse => ({ + label: warehouse.name, + value: warehouse.id + })) || [] ); const warehouseChoices = warehouses.map(warehouseToChoice); return (
- {({ change, data, hasChanged, submit }) => { - const handleWarehouseChange = createSingleAutocompleteSelectHandler( - change, - setWarehouseDisplayValue, + {({ change, data, hasChanged, submit, toggleValue }) => { + const handleWarehouseChange = createMultiAutocompleteSelectHandler( + toggleValue, + setWarehouseDisplayValues, + warehouseDisplayValues, warehouseChoices ); @@ -166,7 +173,7 @@ const ShippingZoneDetailsPage: React.FC = ({
void; } @@ -24,7 +24,7 @@ interface ShippingZonewWarehousesProps extends FetchMoreProps, SearchProps { export const ShippingZoneWarehouses: React.FC = props => { const { data, - displayValue, + displayValues, hasMore, loading, warehouses, @@ -44,7 +44,7 @@ export const ShippingZoneWarehouses: React.FC = pr })} /> - = pr onClick: onWarehouseAdd }} choices={warehouses} - displayValue={displayValue} + displayValues={displayValues} fetchChoices={onSearchChange} hasMore={hasMore} helperText={intl.formatMessage({ @@ -66,14 +66,14 @@ export const ShippingZoneWarehouses: React.FC = pr id: "shippingZoneWarehouses.autocomplete.label" })} loading={loading} - name="warehouse" + name="warehouses" onChange={onChange} onFetchMore={onFetchMore} placeholder={intl.formatMessage({ defaultMessage: "Select Warehouse", description: "input placeholder" })} - value={data.warehouse} + value={data.warehouses} /> diff --git a/src/shipping/mutations.ts b/src/shipping/mutations.ts index b5430e640..6a028ab5b 100644 --- a/src/shipping/mutations.ts +++ b/src/shipping/mutations.ts @@ -44,6 +44,10 @@ import { AssignShippingZoneToWarehouse, AssignShippingZoneToWarehouseVariables } from "./types/AssignShippingZoneToWarehouse"; +import { + UnassignShippingZoneToWarehouse, + UnassignShippingZoneToWarehouseVariables +} from "./types/UnassignShippingZoneToWarehouse"; export const shippingErrorFragment = gql` fragment ShippingErrorFragment on ShippingError { @@ -240,3 +244,24 @@ export const useAssignShippingZoneToWarehouse = makeMutation< AssignShippingZoneToWarehouse, AssignShippingZoneToWarehouseVariables >(assignShippingZoneToWarehouse); + +const unassignShippingZoneToWarehouse = gql` + ${warehouseErrorFragment} + mutation UnassignShippingZoneToWarehouse( + $warehouseId: ID! + $shippingZoneId: ID! + ) { + unassignWarehouseShippingZone( + id: $warehouseId + shippingZoneIds: [$shippingZoneId] + ) { + errors: warehouseErrors { + ...WarehouseErrorFragment + } + } + } +`; +export const useUnassignShippingZoneToWarehouse = makeMutation< + UnassignShippingZoneToWarehouse, + UnassignShippingZoneToWarehouseVariables +>(unassignShippingZoneToWarehouse); diff --git a/src/shipping/types/UnassignShippingZoneToWarehouse.ts b/src/shipping/types/UnassignShippingZoneToWarehouse.ts index 3004a8356..137a51c25 100644 --- a/src/shipping/types/UnassignShippingZoneToWarehouse.ts +++ b/src/shipping/types/UnassignShippingZoneToWarehouse.ts @@ -8,7 +8,7 @@ import { WarehouseErrorCode } from "./../../types/globalTypes"; // GraphQL mutation operation: UnassignShippingZoneToWarehouse // ==================================================== -export interface UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone_warehouseErrors { +export interface UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone_errors { __typename: "WarehouseError"; code: WarehouseErrorCode; field: string | null; @@ -16,7 +16,7 @@ export interface UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone_w export interface UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone { __typename: "WarehouseShippingZoneUnassign"; - warehouseErrors: UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone_warehouseErrors[]; + errors: UnassignShippingZoneToWarehouse_unassignWarehouseShippingZone_errors[]; } export interface UnassignShippingZoneToWarehouse { diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index 0dd6a6777..9b724292c 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -1,3 +1,4 @@ +import { diff } from "fast-array-diff"; import DialogContentText from "@material-ui/core/DialogContentText"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -14,7 +15,8 @@ import { useShippingRateDelete, useShippingZoneDelete, useShippingZoneUpdate, - useAssignShippingZoneToWarehouse + useAssignShippingZoneToWarehouse, + useUnassignShippingZoneToWarehouse } from "@saleor/shipping/mutations"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import ShippingZoneRateDialog from "@saleor/shipping/components/ShippingZoneRateDialog"; @@ -64,6 +66,7 @@ const ShippingZoneDetails: React.FC = ({ ); const [assignToWarehouse] = useAssignShippingZoneToWarehouse({}); + const [unassignToWarehouse] = useUnassignShippingZoneToWarehouse({}); const { data, loading } = useShippingZone({ displayLoader: true, @@ -144,27 +147,38 @@ const ShippingZoneDetails: React.FC = ({ } }); - const handleSubmit = async (data: FormData) => { + const handleSubmit = async (submitData: FormData) => { try { const updateResult = await updateShippingZone({ variables: { id, input: { - name: data.name + name: submitData.name } } }); const updateErrors = updateResult.data.shippingZoneUpdate.errors; if (updateErrors.length === 0) { - const assignResult = await assignToWarehouse({ - variables: { - shippingZoneId: id, - warehouseId: data.warehouse - } - }); - const assignErrors = - assignResult.data.assignWarehouseShippingZone.errors; + const warehouseDiff = diff( + data.shippingZone.warehouses.map(warehouse => warehouse.id), + submitData.warehouses + ); + const assignResults = await Promise.all( + warehouseDiff.added.map(warehouseId => + assignToWarehouse({ + variables: { + shippingZoneId: id, + warehouseId + } + }) + ) + ); + const assignErrors = assignResults + .map( + assignResult => assignResult.data.assignWarehouseShippingZone.errors + ) + .reduce((acc, errors) => [...acc, ...errors], []); if (assignErrors.length === 0) { notify({ @@ -175,6 +189,33 @@ const ShippingZoneDetails: React.FC = ({ `Assigning to warehouse failed: ${assignErrors[0].code}` ); } + + const unassignResults = await Promise.all( + warehouseDiff.removed.map(warehouseId => + unassignToWarehouse({ + variables: { + shippingZoneId: id, + warehouseId + } + }) + ) + ); + const unassignErrors = unassignResults + .map( + unassignResult => + unassignResult.data.unassignWarehouseShippingZone.errors + ) + .reduce((acc, errors) => [...acc, ...errors], []); + + if (unassignErrors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + } else { + throw new Error( + `Assigning to warehouse failed: ${unassignErrors[0].code}` + ); + } } else { throw new Error(`Updating failed: ${updateErrors[0].code}`); } diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 777989892..414ab8bd1 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -127398,7 +127398,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors > ​ @@ -128207,7 +128207,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data > ​ @@ -137544,14 +137544,14 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = ` class="MuiCardContent-root-id" >
@@ -137567,7 +137567,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = ` class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id" placeholder="Select Warehouse" type="text" - value="C our wares" + value="" />

Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes.

+
+
+
+
+ C our wares +
+ +
+
+
+
+
+ Be stocked +
+ +
+
+
@@ -138267,14 +138339,14 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = ` class="MuiCardContent-root-id" >
@@ -138290,7 +138362,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = ` class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id" placeholder="Select Warehouse" type="text" - value="C our wares" + value="" />

Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes.

+
+
+
+
+ C our wares +
+ +
+
+
+
+
+ Be stocked +
+ +
+
+
@@ -138884,7 +139028,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details loading 1`] = ` class="MuiCardContent-root-id" >
+