diff --git a/CHANGELOG.md b/CHANGELOG.md index d6387d43d..84b498f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ All notable, unreleased changes to this project will be documented in this file. - Use MacawUI - #1229 by @dominik-zeglen - Add Metadata for Sale & Voucher - #7653 by @piotrgrundas - Add variant create options dialog - #1238 by @orzechdev +- Fix for errors on changing channel availability - #1264 by @krzysztofwolski # 2.11.1 diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index dd6a0d839..d031077ec 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -20,6 +20,7 @@ import usePaginator, { } from "@saleor/hooks/usePaginator"; import { commonMessages, errorMessages } from "@saleor/intl"; import useProductSearch from "@saleor/searches/useProductSearch"; +import { arrayDiff } from "@saleor/utils/arrays"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { mapEdgesToItems } from "@saleor/utils/maps"; @@ -28,7 +29,6 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { getParsedDataForJsonStringField } from "@saleor/utils/richText/misc"; -import { diff } from "fast-array-diff"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -215,11 +215,14 @@ export const CollectionDetails: React.FC = ({ input } }); - const diffChannels = diff( - collectionChannelsChoices, - formData.channelListings, - (a, b) => a.id === b.id + const initialIds = collectionChannelsChoices.map( + channel => channel.id ); + const modifiedIds = formData.channelListings.map( + channel => channel.id + ); + + const idsDiff = arrayDiff(initialIds, modifiedIds); updateChannels({ variables: { @@ -230,10 +233,7 @@ export const CollectionDetails: React.FC = ({ isPublished: channel.isPublished, publicationDate: channel.publicationDate })), - removeChannels: - diffChannels.removed?.map( - removedChannel => removedChannel.id - ) || [] + removeChannels: idsDiff.removed } } }); diff --git a/src/discounts/handlers.ts b/src/discounts/handlers.ts index b65562c03..adabf9910 100644 --- a/src/discounts/handlers.ts +++ b/src/discounts/handlers.ts @@ -5,7 +5,8 @@ import { DiscountTypeEnum, RequirementsPicker } from "@saleor/discounts/types"; import { ChangeEvent, FormChange } from "@saleor/hooks/useForm"; import { RequireOnlyOne } from "@saleor/misc"; import { VoucherTypeEnum } from "@saleor/types/globalTypes"; -import { diff } from "fast-array-diff"; +import { arrayDiff } from "@saleor/utils/arrays"; + export interface ChannelArgs { discountValue: string; minSpent: string; @@ -96,13 +97,10 @@ export const getChannelsVariables = ( formData: VoucherDetailsPageFormData, prevChannels?: ChannelVoucherData[] ) => { - const removeChannels = prevChannels - ? diff( - prevChannels, - formData.channelListings, - (a, b) => a.id === b.id - ).removed?.map(removedChannel => removedChannel.id) - : []; + const initialIds = prevChannels.map(channel => channel.id); + const modifiedIds = formData.channelListings.map(channel => channel.id); + + const idsDiff = arrayDiff(initialIds, modifiedIds); return { id, @@ -121,7 +119,7 @@ export const getChannelsVariables = ( ? 0 : channel.minSpent })) || [], - removeChannels + removeChannels: idsDiff.removed } }; }; @@ -131,13 +129,10 @@ export const getSaleChannelsVariables = ( formData: SaleDetailsPageFormData, prevChannels?: ChannelSaleData[] ) => { - const removeChannels = prevChannels - ? diff( - prevChannels, - formData.channelListings, - (a, b) => a.id === b.id - ).removed?.map(removedChannel => removedChannel.id) - : []; + const initialIds = prevChannels.map(channel => channel.id); + const modifiedIds = formData.channelListings.map(channel => channel.id); + + const idsDiff = arrayDiff(initialIds, modifiedIds); return { id, @@ -147,7 +142,7 @@ export const getSaleChannelsVariables = ( channelId: channel.id, discountValue: channel.discountValue })) || [], - removeChannels + removeChannels: idsDiff.removed } }; }; diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index bab40571c..f47469581 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -40,13 +40,13 @@ import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPage import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { FetchMoreProps, ReorderEvent } from "@saleor/types"; +import { arrayDiff } from "@saleor/utils/arrays"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import getMetadata from "@saleor/utils/metadata/getMetadata"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import useRichText from "@saleor/utils/richText/useRichText"; -import { diff } from "fast-array-diff"; import React from "react"; import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks"; @@ -180,7 +180,7 @@ const getStocksData = ( const dataStocks = stocks.map(stock => stock.id); const variantStocks = product?.variants[0]?.stocks.map(stock => stock.warehouse.id) || []; - const stockDiff = diff(variantStocks, dataStocks); + const stockDiff = arrayDiff(variantStocks, dataStocks); return { addStocks: stocks.filter(stock => diff --git a/src/products/components/ProductVariantPage/form.tsx b/src/products/components/ProductVariantPage/form.tsx index 69346a6c3..285cbf05e 100644 --- a/src/products/components/ProductVariantPage/form.tsx +++ b/src/products/components/ProductVariantPage/form.tsx @@ -30,10 +30,10 @@ import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPage import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { FetchMoreProps, ReorderEvent } from "@saleor/types"; +import { arrayDiff } from "@saleor/utils/arrays"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import getMetadata from "@saleor/utils/metadata/getMetadata"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; -import { diff } from "fast-array-diff"; import React from "react"; import handleFormSubmit from "../../../utils/handlers/handleFormSubmit"; @@ -206,7 +206,7 @@ function useProductVariantUpdateForm( const dataStocks = stocks.data.map(stock => stock.id); const variantStocks = variant?.stocks.map(stock => stock.warehouse.id) || []; - const stockDiff = diff(variantStocks, dataStocks); + const stockDiff = arrayDiff(variantStocks, dataStocks); const addStocks = stocks.data.filter(stock => stockDiff.added.some(addedStock => addedStock === stock.id) diff --git a/src/products/views/ProductUpdate/handlers/utils.ts b/src/products/views/ProductUpdate/handlers/utils.ts index d4e6d9ee5..f0e603920 100644 --- a/src/products/views/ProductUpdate/handlers/utils.ts +++ b/src/products/views/ProductUpdate/handlers/utils.ts @@ -15,7 +15,7 @@ import { SimpleProductUpdate } from "@saleor/products/types/SimpleProductUpdate" import { mapFormsetStockToStockInput } from "@saleor/products/utils/data"; import { getAvailabilityVariables } from "@saleor/products/utils/handlers"; import { ProductChannelListingAddInput } from "@saleor/types/globalTypes"; -import { diff } from "fast-array-diff"; +import { arrayDiff } from "@saleor/utils/arrays"; import isEqual from "lodash/isEqual"; import { ChannelsWithVariantsData, ChannelWithVariantData } from "../types"; @@ -70,7 +70,7 @@ export const getChannelListingUpdateInputFromData = ( basicChannelData: ChannelData ) => ({ ...getChannelListingBaseInputData(basicChannelData), - addVariants: diff(initialSelectedVariantsIds, variantsIdsToAdd).added, + addVariants: arrayDiff(initialSelectedVariantsIds, variantsIdsToAdd).added, removeVariants: variantsIdsToRemove }); @@ -164,10 +164,11 @@ export const getSimpleChannelsVariables = ( product: ProductDetails_product ) => { const productChannels = createSortedChannelsDataFromProduct(product); - const diffChannels = diff( - productChannels, - data.channelListings, - (a, b) => a.id === b.id + const existingChannelIDs = productChannels.map(channel => channel.id); + const modifiedChannelIDs = data.channelListings.map(channel => channel.id); + + const removedChannelIDs = existingChannelIDs.filter( + x => !modifiedChannelIDs.includes(x) ); return { @@ -175,9 +176,7 @@ export const getSimpleChannelsVariables = ( id: product.id, input: { updateChannels: getAvailabilityVariables(data.channelListings), - removeChannels: diffChannels.removed?.map( - removedChannel => removedChannel.id - ) + removeChannels: removedChannelIDs } } }; diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index a3f23da0d..3712d236c 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -19,6 +19,7 @@ import { useShippingZoneDelete, useShippingZoneUpdate } from "@saleor/shipping/mutations"; +import { arrayDiff } from "@saleor/utils/arrays"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { mapEdgesToItems } from "@saleor/utils/maps"; @@ -27,7 +28,6 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseCreate } from "@saleor/warehouses/mutations"; -import { diff } from "fast-array-diff"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -133,12 +133,12 @@ const ShippingZoneDetails: React.FC = ({ const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const updateData = async (submitData: FormData) => { - const warehouseDiff = diff( + const warehouseDiff = arrayDiff( data.shippingZone.warehouses.map(warehouse => warehouse.id), submitData.warehouses ); - const channelsDiff = diff( + const channelsDiff = arrayDiff( data.shippingZone.channels.map(channel => channel.id), submitData.channels ); diff --git a/src/utils/arrays/arrays.test.ts b/src/utils/arrays/arrays.test.ts new file mode 100644 index 000000000..84fddaccd --- /dev/null +++ b/src/utils/arrays/arrays.test.ts @@ -0,0 +1,55 @@ +import { arrayDiff } from "./arrays"; + +const fruits = ["apple", "orange", "strawberry"]; + +const vegetables = ["potato", "onion"]; + +describe("Validate diff results", () => { + it("Empty arrays", () => { + const diff = arrayDiff([], []); + expect(diff).toStrictEqual({ added: [], removed: [], common: [] }); + }); + + it("Compare array with itself", () => { + const diff = arrayDiff(fruits, fruits); + expect(diff).toStrictEqual({ added: [], removed: [], common: fruits }); + }); + + it("Added elements to empty", () => { + const diff = arrayDiff([], vegetables); + expect(diff).toStrictEqual({ + added: vegetables, + removed: [], + common: [] + }); + }); + + it("Added elements to populated array", () => { + const diff = arrayDiff(fruits, [...fruits, ...vegetables]); + expect(diff).toStrictEqual({ + added: vegetables, + removed: [], + common: fruits + }); + }); + + it("Removed elements", () => { + const diff = arrayDiff([...fruits, ...vegetables], fruits); + expect(diff).toStrictEqual({ + added: [], + removed: vegetables, + common: fruits + }); + }); + + it("Added, removed, and common elements", () => { + const before = ["a", "b", "c", "d"]; + const after = ["b", "e", "a", "t"]; + const diff = arrayDiff(before, after); + expect(diff).toStrictEqual({ + added: ["e", "t"], + removed: ["c", "d"], + common: ["a", "b"] + }); + }); +}); diff --git a/src/utils/arrays/arrays.ts b/src/utils/arrays/arrays.ts new file mode 100644 index 000000000..3278f7bc1 --- /dev/null +++ b/src/utils/arrays/arrays.ts @@ -0,0 +1,8 @@ +import difference from "lodash/difference"; +import intersection from "lodash/intersection"; + +export const arrayDiff = (before: string[], after: string[]) => ({ + added: difference(after, before), + removed: difference(before, after), + common: intersection(before, after) +}); diff --git a/src/utils/arrays/index.ts b/src/utils/arrays/index.ts new file mode 100644 index 000000000..ca7100766 --- /dev/null +++ b/src/utils/arrays/index.ts @@ -0,0 +1 @@ +export * from "./arrays"; diff --git a/src/utils/handlers/metadataUpdateHandler.ts b/src/utils/handlers/metadataUpdateHandler.ts index b92144c78..2fe5b15e7 100644 --- a/src/utils/handlers/metadataUpdateHandler.ts +++ b/src/utils/handlers/metadataUpdateHandler.ts @@ -1,7 +1,7 @@ import { MetadataFormData } from "@saleor/components/Metadata/types"; import { MetadataErrorFragment } from "@saleor/fragments/types/MetadataErrorFragment"; import { MetadataInput } from "@saleor/types/globalTypes"; -import { diff } from "fast-array-diff"; +import { arrayDiff } from "@saleor/utils/arrays"; import { MutationFetchResult } from "react-apollo"; import { @@ -40,16 +40,15 @@ function createMetadataUpdateHandler( if (errors.length === 0) { if (data.metadata) { - const metaDiff = diff( - initial.metadata, - data.metadata, - (a, b) => a.key === b.key - ); + const initialKeys = initial.metadata.map(m => m.key); + const modifiedKeys = data.metadata.map(m => m.key); + + const keyDiff = arrayDiff(initialKeys, modifiedKeys); const updateMetaResult = await updateMetadata({ id: initial.id, input: data.metadata, - keysToDelete: metaDiff.removed.map(meta => meta.key) + keysToDelete: keyDiff.removed }); const updateMetaErrors = [ ...(updateMetaResult.data.deleteMetadata.errors || []), @@ -62,16 +61,15 @@ function createMetadataUpdateHandler( } if (data.privateMetadata) { - const privateMetaDiff = diff( - initial.privateMetadata, - data.privateMetadata, - (a, b) => a.key === b.key - ); + const initialKeys = initial.privateMetadata.map(m => m.key); + const modifiedKeys = data.privateMetadata.map(m => m.key); + + const keyDiff = arrayDiff(initialKeys, modifiedKeys); const updatePrivateMetaResult = await updatePrivateMetadata({ id: initial.id, input: data.privateMetadata, - keysToDelete: privateMetaDiff.removed.map(meta => meta.key) + keysToDelete: keyDiff.removed }); const updatePrivateMetaErrors = [