Enable assigning multiple warehouses to zone

This commit is contained in:
dominik-zeglen 2020-04-16 16:31:44 +02:00
parent 16fc30c662
commit bcc4f96f82
15 changed files with 276 additions and 54 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -54,6 +54,7 @@ export interface ProductVariantAttributesFragment_productType_variantAttributes
export interface ProductVariantAttributesFragment_productType {
__typename: "ProductType";
id: string;
variantAttributes: (ProductVariantAttributesFragment_productType_variantAttributes | null)[] | null;
}

View file

@ -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;
}

View file

@ -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<ShippingZoneDetailsPageProps> = ({
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 (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ 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<ShippingZoneDetailsPageProps> = ({
<div>
<ShippingZoneWarehouses
data={data}
displayValue={warehouseDisplayValue}
displayValues={warehouseDisplayValues}
hasMore={hasMore}
loading={loading}
onChange={handleWarehouseChange}

View file

@ -6,17 +6,17 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { FetchMoreProps, SearchProps } from "@saleor/types";
import { FormChange } from "@saleor/hooks/useForm";
import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType
} from "@saleor/components/SingleAutocompleteSelectField";
import MultiAutocompleteSelectField, {
MultiAutocompleteChoiceType
} from "@saleor/components/MultiAutocompleteSelectField";
interface ShippingZoneWarehousesFormData {
warehouse: string;
warehouses: string[];
}
interface ShippingZonewWarehousesProps extends FetchMoreProps, SearchProps {
data: ShippingZoneWarehousesFormData;
displayValue: string;
warehouses: SingleAutocompleteChoiceType[];
displayValues: MultiAutocompleteChoiceType[];
warehouses: MultiAutocompleteChoiceType[];
onChange: FormChange;
onWarehouseAdd: () => void;
}
@ -24,7 +24,7 @@ interface ShippingZonewWarehousesProps extends FetchMoreProps, SearchProps {
export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = props => {
const {
data,
displayValue,
displayValues,
hasMore,
loading,
warehouses,
@ -44,7 +44,7 @@ export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = pr
})}
/>
<CardContent>
<SingleAutocompleteSelectField
<MultiAutocompleteSelectField
add={{
label: intl.formatMessage({
defaultMessage: "Add New Warehouse",
@ -53,7 +53,7 @@ export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = 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<ShippingZonewWarehousesProps> = 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}
/>
</CardContent>
</Card>

View file

@ -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);

View file

@ -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 {

View file

@ -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<ShippingZoneDetailsProps> = ({
);
const [assignToWarehouse] = useAssignShippingZoneToWarehouse({});
const [unassignToWarehouse] = useUnassignShippingZoneToWarehouse({});
const { data, loading } = useShippingZone({
displayLoader: true,
@ -144,27 +147,38 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
}
});
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({
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: data.warehouse
warehouseId
}
});
const assignErrors =
assignResult.data.assignWarehouseShippingZone.errors;
})
)
);
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<ShippingZoneDetailsProps> = ({
`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}`);
}

View file

@ -127398,7 +127398,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:0"
style="width:0.01px"
>
<span>
@ -128207,7 +128207,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data
>
<legend
class="PrivateNotchedOutline-legend-id"
style="width:0"
style="width:0.01px"
>
<span>
@ -137544,14 +137544,14 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
class="MuiCardContent-root-id"
>
<div
class="SingleAutocompleteSelectField-container-id"
class="MultiAutocompleteSelectField-container-id"
>
<div
class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id"
>
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-filled-id"
data-shrink="true"
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id"
data-shrink="false"
>
Warehouse
</label>
@ -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=""
/>
<div>
<svg
@ -137602,12 +137602,84 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
</fieldset>
</div>
<p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-filled-id"
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id"
>
Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes.
</p>
</div>
</div>
<div
class="MultiAutocompleteSelectField-chipContainer-id"
>
<div
class="MultiAutocompleteSelectField-chip-id"
>
<div
class="MultiAutocompleteSelectField-chipInner-id"
>
<div
class="MuiTypography-root-id MultiAutocompleteSelectField-chipLabel-id MuiTypography-body1-id"
>
C our wares
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
tabindex="0"
type="button"
>
<span
class="MuiIconButton-label-id"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSvgIcon-fontSizeSmall-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
</span>
</button>
</div>
</div>
<div
class="MultiAutocompleteSelectField-chip-id"
>
<div
class="MultiAutocompleteSelectField-chipInner-id"
>
<div
class="MuiTypography-root-id MultiAutocompleteSelectField-chipLabel-id MuiTypography-body1-id"
>
Be stocked
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
tabindex="0"
type="button"
>
<span
class="MuiIconButton-label-id"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSvgIcon-fontSizeSmall-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@ -138267,14 +138339,14 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
class="MuiCardContent-root-id"
>
<div
class="SingleAutocompleteSelectField-container-id"
class="MultiAutocompleteSelectField-container-id"
>
<div
class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id"
>
<label
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-filled-id"
data-shrink="true"
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id"
data-shrink="false"
>
Warehouse
</label>
@ -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=""
/>
<div>
<svg
@ -138325,12 +138397,84 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
</fieldset>
</div>
<p
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-filled-id"
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id"
>
Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes.
</p>
</div>
</div>
<div
class="MultiAutocompleteSelectField-chipContainer-id"
>
<div
class="MultiAutocompleteSelectField-chip-id"
>
<div
class="MultiAutocompleteSelectField-chipInner-id"
>
<div
class="MuiTypography-root-id MultiAutocompleteSelectField-chipLabel-id MuiTypography-body1-id"
>
C our wares
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
tabindex="0"
type="button"
>
<span
class="MuiIconButton-label-id"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSvgIcon-fontSizeSmall-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
</span>
</button>
</div>
</div>
<div
class="MultiAutocompleteSelectField-chip-id"
>
<div
class="MultiAutocompleteSelectField-chipInner-id"
>
<div
class="MuiTypography-root-id MultiAutocompleteSelectField-chipLabel-id MuiTypography-body1-id"
>
Be stocked
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
tabindex="0"
type="button"
>
<span
class="MuiIconButton-label-id"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSvgIcon-fontSizeSmall-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@ -138884,7 +139028,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details loading 1`] = `
class="MuiCardContent-root-id"
>
<div
class="SingleAutocompleteSelectField-container-id"
class="MultiAutocompleteSelectField-container-id"
>
<div
class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id"
@ -138948,6 +139092,9 @@ exports[`Storyshots Views / Shipping / Shipping zone details loading 1`] = `
</p>
</div>
</div>
<div
class="MultiAutocompleteSelectField-chipContainer-id"
/>
</div>
</div>
</div>