diff --git a/CHANGELOG.md b/CHANGELOG.md index f187259f5d5..84b498f2f6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ All notable, unreleased changes to this project will be documented in this file. - Fix forbidden null sending as attribute value - #1201 by @orzechdev - Fix missing call for update metadata mutation - #1207 by @orzechdev - Disable next step when no value selected in variant selector - #1218 by @orzechdev +- Add boolean attributes - #1157 by @piotrgrundas +- Add date & date time attributes - #1180 by @piotrgrundas - Fix order links on home page - #1219 by @jwm0 - Fix incorrectly handled image upload errors - #1223 by @kamilpastuszka - Fix huge payload issue for plugins view - #1203 by @kamilpastuszka @@ -68,7 +70,9 @@ All notable, unreleased changes to this project will be documented in this file. - Fix list pagination crash on search - #1230 by @orzechdev - Fix positive float number input validation - #1233 by @orzechdev - 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 @@ -185,7 +189,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 -- Add boolean attributes - #1157 by @piotrgrundas ## 2.0.0 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 30f0892e531..e2ce4250528 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -957,6 +957,14 @@ "context": "boolean attribute type", "string": "Boolean" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_date": { + "context": "date attribute type", + "string": "Date" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_dateTime": { + "context": "date time attribute type", + "string": "Date Time" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_distance": { "context": "distance units type", "string": "Distance" @@ -2757,6 +2765,9 @@ "src_dot_dashboard": { "string": "Dashboard" }, + "src_dot_date": { + "string": "Date" + }, "src_dot_delete": { "context": "button", "string": "Delete" @@ -6656,6 +6667,10 @@ "src_dot_translationAttributes": { "string": "Attributes" }, + "src_dot_time": { + "context": "independent of any particular day, eg. 11:35", + "string": "Time" + }, "src_dot_translations": { "context": "translations section name", "string": "Translations" @@ -7245,6 +7260,10 @@ "context": "event", "string": "Order created" }, + "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_36047288": { + "context": "event", + "string": "Translation created" + }, "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3617444329": { "context": "event", "string": "Order cancelled" @@ -7285,6 +7304,10 @@ "context": "event", "string": "Process payment" }, + "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_482477254": { + "context": "event", + "string": "Translation updated" + }, "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_679080833": { "context": "event", "string": "Page created" diff --git a/package.json b/package.json index 05965e3e6db..d2590fb143c 100644 --- a/package.json +++ b/package.json @@ -254,7 +254,7 @@ "test:e2e:run": "start-server-and-test start http://localhost:9000 cy:run", "test:e2e:run:record": "start-server-and-test start http://localhost:9000 cy:run:record", "test:e2e:dev": "start-server-and-test start http://localhost:9000 cy:open", - "test": "jest src/", + "test": "TZ=UTC jest src/", "transpile-messages": "node scripts/transpile-tx.js", "lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write", "postbuild": "rimraf ./build/**/*.js.map", diff --git a/schema.graphql b/schema.graphql index 23f632d04c4..ef2c6c3ad59 100644 --- a/schema.graphql +++ b/schema.graphql @@ -513,12 +513,15 @@ input AttributeFilterInput { type: AttributeTypeEnum inCollection: ID inCategory: ID + channel: String } input AttributeInput { slug: String! values: [String] valuesRange: IntRangeInput + dateTime: DateTimeRangeInput + date: DateRangeInput boolean: Boolean } @@ -530,6 +533,8 @@ enum AttributeInputTypeEnum { NUMERIC RICH_TEXT BOOLEAN + DATE + DATE_TIME } type AttributeReorderValues { @@ -611,6 +616,8 @@ type AttributeValue implements Node { file: File richText: JSONString boolean: Boolean + date: Date + dateTime: DateTime } type AttributeValueBulkDelete { @@ -662,6 +669,8 @@ input AttributeValueInput { references: [ID!] richText: JSONString boolean: Boolean + date: Date + dateTime: DateTime } type AttributeValueTranslatableContent implements Node { @@ -699,7 +708,8 @@ type AttributeValueUpdate { input BulkAttributeValueInput { id: ID - values: [String!]! + values: [String!] + boolean: Boolean } type BulkProductError { @@ -799,6 +809,7 @@ enum CategorySortField { input CategorySortingInput { direction: OrderDirection! + channel: String field: CategorySortField! } @@ -1232,6 +1243,7 @@ input CollectionFilterInput { search: String metadata: [MetadataFilter] ids: [ID] + channel: String } input CollectionInput { @@ -1271,6 +1283,7 @@ enum CollectionSortField { input CollectionSortingInput { direction: OrderDirection! + channel: String field: CollectionSortField! } @@ -1641,6 +1654,7 @@ type CustomerEvent implements Node { date: DateTime type: CustomerEventsEnum user: User + app: App message: String count: Int order: Order @@ -3186,6 +3200,7 @@ type OrderEvent implements Node { date: DateTime type: OrderEventsEnum user: User + app: App message: String email: String emailType: OrderEventsEmailsEnum @@ -3903,6 +3918,7 @@ type PaymentRefund { type PaymentSource { gateway: String! + paymentMethodId: String creditCardInfo: CreditCard } @@ -4293,6 +4309,7 @@ input ProductFilterInput { minimalPrice: PriceRangeInput productTypes: [ID] ids: [ID] + channel: String } type ProductImage { @@ -4377,6 +4394,7 @@ input ProductMediaUpdateInput { input ProductOrder { direction: OrderDirection! + channel: String attributeId: ID field: ProductOrderField } @@ -4781,7 +4799,6 @@ type Query { taxTypes: [TaxType] checkout(token: UUID): Checkout checkouts(channel: String, before: String, after: String, first: Int, last: Int): CheckoutCountableConnection - checkoutLine(id: ID): CheckoutLine checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection channel(id: ID): Channel channels: [Channel!] @@ -4835,12 +4852,14 @@ type RequestPasswordReset { errors: [AccountError!]! } -type Sale implements Node { +type Sale implements Node & ObjectWithMetadata { id: ID! name: String! type: SaleType! startDate: DateTime! endDate: DateTime + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! categories(before: String, after: String, first: Int, last: Int): CategoryCountableConnection collections(before: String, after: String, first: Int, last: Int): CollectionCountableConnection products(before: String, after: String, first: Int, last: Int): ProductCountableConnection @@ -4913,6 +4932,7 @@ input SaleFilterInput { saleType: DiscountValueTypeEnum started: DateTimeRangeInput search: String + metadata: [MetadataFilter] } input SaleInput { @@ -4942,6 +4962,7 @@ enum SaleSortField { input SaleSortingInput { direction: OrderDirection! + channel: String field: SaleSortField! } @@ -5753,7 +5774,7 @@ enum VolumeUnitsEnum { ACRE_FT } -type Voucher implements Node { +type Voucher implements Node & ObjectWithMetadata { id: ID! name: String type: VoucherTypeEnum! @@ -5767,6 +5788,8 @@ type Voucher implements Node { onlyForStaff: Boolean! discountValueType: DiscountValueTypeEnum! minCheckoutItemsQuantity: Int + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! categories(before: String, after: String, first: Int, last: Int): CategoryCountableConnection collections(before: String, after: String, first: Int, last: Int): CollectionCountableConnection products(before: String, after: String, first: Int, last: Int): ProductCountableConnection @@ -5850,6 +5873,7 @@ input VoucherFilterInput { discountType: [VoucherDiscountType] started: DateTimeRangeInput search: String + metadata: [MetadataFilter] } input VoucherInput { @@ -5888,6 +5912,7 @@ enum VoucherSortField { input VoucherSortingInput { direction: OrderDirection! + channel: String field: VoucherSortField! } @@ -6102,6 +6127,8 @@ enum WebhookEventTypeEnum { PAYMENT_PROCESS PAYMENT_REFUND PAYMENT_VOID + TRANSLATION_CREATED + TRANSLATION_UPDATED } enum WebhookSampleEventTypeEnum { @@ -6136,6 +6163,8 @@ enum WebhookSampleEventTypeEnum { PAYMENT_PROCESS PAYMENT_REFUND PAYMENT_VOID + TRANSLATION_CREATED + TRANSLATION_UPDATED } type WebhookUpdate { @@ -6170,7 +6199,7 @@ enum WeightUnitsEnum { scalar _Any -union _Entity = Address | User | Group | App | ProductVariant | Product | ProductType | Collection | Category | ProductMedia | ProductImage | PageType +union _Entity = App | Address | User | Group | ProductVariant | Product | ProductType | Collection | Category | ProductMedia | ProductImage | PageType type _Service { sdl: String diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index d6653121bd4..85da11af37c 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -100,6 +100,14 @@ const AttributeDetails: React.FC = props => { { label: intl.formatMessage(inputTypeMessages.boolean), value: AttributeInputTypeEnum.BOOLEAN + }, + { + label: intl.formatMessage(inputTypeMessages.date), + value: AttributeInputTypeEnum.DATE + }, + { + label: intl.formatMessage(inputTypeMessages.dateTime), + value: AttributeInputTypeEnum.DATE_TIME } ]; const entityTypeChoices = [ diff --git a/src/attributes/components/AttributeDetails/messages.tsx b/src/attributes/components/AttributeDetails/messages.tsx index 02aa217d402..454ad896ff7 100644 --- a/src/attributes/components/AttributeDetails/messages.tsx +++ b/src/attributes/components/AttributeDetails/messages.tsx @@ -73,6 +73,14 @@ export const inputTypeMessages = defineMessages({ boolean: { defaultMessage: "Boolean", description: "boolean attribute type" + }, + date: { + defaultMessage: "Date", + description: "date attribute type" + }, + dateTime: { + defaultMessage: "Date Time", + description: "date time attribute type" } }); diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts index 981d7d3c5dc..6745c6d0425 100644 --- a/src/attributes/fixtures.ts +++ b/src/attributes/fixtures.ts @@ -50,7 +50,9 @@ export const attribute: AttributeDetails_attribute = { reference: null, slug: "john-doe", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -64,7 +66,9 @@ export const attribute: AttributeDetails_attribute = { reference: null, slug: "milionare-pirate", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -108,7 +112,9 @@ export const attributes: Array value.slug); } diff --git a/src/attributes/utils/handlers.test.ts b/src/attributes/utils/handlers.test.ts index 99bd3c60526..fc4eff1e4b2 100644 --- a/src/attributes/utils/handlers.test.ts +++ b/src/attributes/utils/handlers.test.ts @@ -17,7 +17,9 @@ const attributes: FormsetData = [ reference: null, slug: "attr-1-v-1", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -38,7 +40,9 @@ const attributes: FormsetData = [ reference: null, slug: "attr-2-v-1", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -48,7 +52,9 @@ const attributes: FormsetData = [ reference: null, slug: "attr-2-v-2", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -58,7 +64,9 @@ const attributes: FormsetData = [ reference: null, slug: "attr-2-v-3", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -83,7 +91,9 @@ const attributes: FormsetData = [ reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, diff --git a/src/attributes/utils/handlers.ts b/src/attributes/utils/handlers.ts index ff16730a680..c2e9f5de85a 100644 --- a/src/attributes/utils/handlers.ts +++ b/src/attributes/utils/handlers.ts @@ -218,6 +218,20 @@ function getBooleanInput(attribute: AttributeInput) { }; } +function getDateInput(attribute: AttributeInput) { + return { + id: attribute.id, + date: attribute.value[0] + }; +} + +function getDateTimeInput(attribute: AttributeInput) { + return { + id: attribute.id, + dateTime: attribute.value[0] + }; +} + function getDefaultInput(attribute: AttributeInput) { return { id: attribute.id, @@ -245,6 +259,12 @@ export const prepareAttributesInput = ({ case AttributeInputTypeEnum.BOOLEAN: return getBooleanInput(attribute); + case AttributeInputTypeEnum.DATE: + return getDateInput(attribute); + + case AttributeInputTypeEnum.DATE_TIME: + return getDateTimeInput(attribute); + default: return getDefaultInput(attribute); } diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx index e3560232d29..6d1c638ef3b 100644 --- a/src/attributes/views/AttributeCreate/AttributeCreate.tsx +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -207,6 +207,8 @@ const AttributeDetails: React.FC = ({ params }) => { value: null, richText: null, boolean: null, + date: null, + dateTime: null, ...value } })) diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index dd6a0d83912..d031077ecf6 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/components/Attributes/AttributeRow.tsx b/src/components/Attributes/AttributeRow.tsx index dfd47fdac36..4f22bd4a4c5 100644 --- a/src/components/Attributes/AttributeRow.tsx +++ b/src/components/Attributes/AttributeRow.tsx @@ -15,6 +15,7 @@ import { getSingleDisplayValue } from "@saleor/components/Attributes/utils"; import Checkbox from "@saleor/components/Checkbox"; +import { DateTimeField } from "@saleor/components/DateTimeField"; import FileUploadField from "@saleor/components/FileUploadField"; import MultiAutocompleteSelectField from "@saleor/components/MultiAutocompleteSelectField"; import RichTextEditor from "@saleor/components/RichTextEditor"; @@ -24,6 +25,7 @@ import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFr import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetChange } from "@saleor/hooks/useFormset"; +import { commonMessages } from "@saleor/intl"; import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import React from "react"; @@ -219,6 +221,37 @@ const AttributeRow: React.FC = ({ ); + case AttributeInputTypeEnum.DATE: + return ( + + onChange(attribute.id, event.target.value)} + type="date" + value={attribute.value[0]} + InputLabelProps={{ shrink: true }} + /> + + ); + case AttributeInputTypeEnum.DATE_TIME: + return ( + + onChange(attribute.id, value)} + /> + + ); default: return ( diff --git a/src/components/Attributes/BasicAttributeRow.tsx b/src/components/Attributes/BasicAttributeRow.tsx index 6bc2a291747..e73d0761f7c 100644 --- a/src/components/Attributes/BasicAttributeRow.tsx +++ b/src/components/Attributes/BasicAttributeRow.tsx @@ -1,6 +1,7 @@ import { Typography } from "@material-ui/core"; import Grid from "@saleor/components/Grid"; import { makeStyles } from "@saleor/macaw-ui"; +import classNames from "classnames"; import React from "react"; const useStyles = makeStyles( @@ -15,6 +16,15 @@ const useStyles = makeStyles( attributeSectionLabel: { alignItems: "center", display: "flex" + }, + flex: { + columnGap: theme.spacing(2) + "px", + display: "flex", + flexDirection: "row", + [theme.breakpoints.down("md")]: { + flexDirection: "column", + rowGap: theme.spacing(2) + "px" + } } }), { name: "BasicAttributeRow" } @@ -22,11 +32,15 @@ const useStyles = makeStyles( interface BasicAttributeRowProps { label: string | React.ReactNode; + flexValueContainer?: boolean; } -const BasicAttributeRow: React.FC = props => { - const { label, children } = props; - const classes = useStyles(props); +const BasicAttributeRow: React.FC = ({ + label, + children, + flexValueContainer +}) => { + const classes = useStyles(); return ( @@ -36,7 +50,14 @@ const BasicAttributeRow: React.FC = props => { > {label} -
{children}
+
+ {children} +
); }; diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index 966f56dd179..a77050a7832 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -19,7 +19,9 @@ const DROPDOWN_ATTRIBUTE: AttributeInput = { reference: null, slug: "dropdown-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -29,7 +31,9 @@ const DROPDOWN_ATTRIBUTE: AttributeInput = { reference: null, slug: "dropdown-second-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -51,7 +55,9 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { reference: null, slug: "multiselect-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -61,7 +67,9 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { reference: null, slug: "multiselect-second-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -71,7 +79,9 @@ const MULTISELECT_ATTRIBUTE: AttributeInput = { reference: null, slug: "multiselect-third-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -97,7 +107,9 @@ const FILE_ATTRIBUTE: AttributeInput = { reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -134,7 +146,9 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { reference: null, slug: "references-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -144,7 +158,9 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { reference: null, slug: "references-second-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null }, { __typename: "AttributeValue", @@ -154,7 +170,9 @@ const REFERENCE_ATTRIBUTE: AttributeInput = { reference: null, slug: "references-third-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -180,7 +198,9 @@ const RICH_TEXT_ATTRIBUTE: AttributeInput = { blocks: [{ data: { text: "Some cool text" }, type: "paragraph" }], version: "2.19.3" }), - boolean: null + boolean: null, + date: null, + dateTime: null } ], selectedValues: [] @@ -204,6 +224,8 @@ const NUMERIC_ATTRIBUTE: AttributeInput = { reference: null, richText: null, boolean: null, + date: null, + dateTime: null, slug: "319_35" } ] @@ -226,7 +248,9 @@ const BOOLEAN_ATTRIBUTE: AttributeInput = { reference: null, richText: null, boolean: true, - slug: "319_True" + slug: "319_True", + date: null, + dateTime: null } ] }, @@ -235,6 +259,53 @@ const BOOLEAN_ATTRIBUTE: AttributeInput = { value: [] }; +const DATE_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.DATE, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "asdfasdfasdfasdf", + name: "Date Attribute: '2021-06-03 00:15:00+00:00'", + reference: null, + richText: null, + boolean: true, + slug: "319_True", + date: "2021-06-03", + dateTime: "2021-06-03 00:15:00+00:00" + } + ] + }, + id: "QXR0cmfsdfasfdjMasdfasdf1", + label: "Date Attribute", + value: [] +}; + +const DATE_TIME_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.DATE_TIME, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "asdfasdfasdfasdf", + name: "Date Time Attribute: '2021-06-03 00:15:00+00:00'", + reference: null, + richText: null, + boolean: true, + slug: "319_True", + date: "2021-06-03", + dateTime: "2021-06-03 00:15:00+00:00" + } + ] + }, + id: "QXR0cmlidXasdfasdfasdf1", + label: "Date Time Attribute", + value: [] +}; export const ATTRIBUTES: AttributeInput[] = [ DROPDOWN_ATTRIBUTE, MULTISELECT_ATTRIBUTE, @@ -242,7 +313,9 @@ export const ATTRIBUTES: AttributeInput[] = [ REFERENCE_ATTRIBUTE, RICH_TEXT_ATTRIBUTE, NUMERIC_ATTRIBUTE, - BOOLEAN_ATTRIBUTE + BOOLEAN_ATTRIBUTE, + DATE_ATTRIBUTE, + DATE_TIME_ATTRIBUTE ]; export const ATTRIBUTES_SELECTED: AttributeInput[] = [ @@ -284,5 +357,13 @@ export const ATTRIBUTES_SELECTED: AttributeInput[] = [ { ...BOOLEAN_ATTRIBUTE, value: [JSON.stringify(BOOLEAN_ATTRIBUTE.data.values[0].boolean)] + }, + { + ...DATE_ATTRIBUTE, + value: [DATE_TIME_ATTRIBUTE.data.values[0].date] + }, + { + ...DATE_TIME_ATTRIBUTE, + value: [DATE_TIME_ATTRIBUTE.data.values[0].dateTime] } ]; diff --git a/src/components/DateTimeField.tsx b/src/components/DateTimeField.tsx new file mode 100644 index 00000000000..6aa0930b36f --- /dev/null +++ b/src/components/DateTimeField.tsx @@ -0,0 +1,65 @@ +import { TextField } from "@material-ui/core"; +import { TextFieldProps } from "@material-ui/core/TextField"; +import { getErrorMessage } from "@saleor/components/Attributes/utils"; +import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; +import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; +import { commonMessages } from "@saleor/intl"; +import { DateTime, joinDateTime, splitDateTime } from "@saleor/misc"; +import React, { useEffect, useState } from "react"; +import { useIntl } from "react-intl"; + +type DateTimeFieldProps = Omit & { + onChange: (value: string) => void; + error: ProductErrorWithAttributesFragment | PageErrorWithAttributesFragment; + value: string; +}; + +export const DateTimeField: React.FC = ({ + disabled, + error, + name, + onChange, + value: initialValue +}) => { + const intl = useIntl(); + const [value, setValue] = useState( + initialValue ? splitDateTime(initialValue) : { date: "", time: "" } + ); + + useEffect(() => onChange(joinDateTime(value.date, value.time)), [value]); + + return ( + <> + { + const date = event.target.value; + setValue(value => ({ ...value, date })); + }} + type="date" + value={value.date} + InputLabelProps={{ shrink: true }} + /> + { + const time = event.target.value; + setValue(value => ({ ...value, time })); + }} + type="time" + value={value.time} + InputLabelProps={{ shrink: true }} + /> + + ); +}; diff --git a/src/components/Filter/FilterContent/FilterContent.tsx b/src/components/Filter/FilterContent/FilterContent.tsx index 6d63feff06d..7398d4c3f3b 100644 --- a/src/components/Filter/FilterContent/FilterContent.tsx +++ b/src/components/Filter/FilterContent/FilterContent.tsx @@ -21,9 +21,9 @@ import { InvalidFilters } from "../types"; import FilterContentBody, { FilterContentBodyProps } from "./FilterContentBody"; +import FilterContentBodyNameField from "./FilterContentBodyNameField"; import FilterContentHeader from "./FilterContentHeader"; import FilterErrorsList from "./FilterErrorsList"; -import FilterGroupLabel from "./FilterGroupLabel"; const useExpanderStyles = makeStyles( () => ({ @@ -219,7 +219,7 @@ const FilterContent: React.FC = ({ classes={summaryClasses} onClick={() => handleFilterOpen(filter)} > - handleFilterPropertyGroupChange(action, filter) diff --git a/src/components/Filter/FilterContent/FilterContentBody.tsx b/src/components/Filter/FilterContent/FilterContentBody.tsx index 9ccb2af0187..e4e03e3163e 100644 --- a/src/components/Filter/FilterContent/FilterContentBody.tsx +++ b/src/components/Filter/FilterContent/FilterContentBody.tsx @@ -1,26 +1,45 @@ -import { FormControlLabel, Radio } from "@material-ui/core"; -import FormSpacer from "@saleor/components/FormSpacer"; +import { FormControlLabel, Radio, TextField } from "@material-ui/core"; +import { fade } from "@material-ui/core/styles/colorManipulator"; +import { FilterDateTimeField } from "@saleor/components/Filter/FilterContent/FilterDateTimeField"; +import { FilterNumericField } from "@saleor/components/Filter/FilterContent/FilterNumericField"; +import { FilterSingleSelectField } from "@saleor/components/Filter/FilterContent/FilterSingleSelectField"; +import { useCommonStyles } from "@saleor/components/Filter/FilterContent/utils"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; -import SingleSelectField from "@saleor/components/SingleSelectField"; +import { makeStyles } from "@saleor/macaw-ui"; import classNames from "classnames"; import React from "react"; -import { useIntl } from "react-intl"; -import Arrow from "../Arrow"; import FilterAutocompleteField, { FilterAutocompleteDisplayValues } from "../FilterAutocompleteField"; import FilterOptionField from "../FilterOptionField"; import { FilterReducerAction } from "../reducer"; -import { FieldType, FilterType, IFilterElement } from "../types"; -import FilterRangeField from "./FilterRangeField"; -import FilterTextField from "./FilterTextField"; -import useStyles from "./styles"; -import { filterTestingContext, getIsFilterMultipleChoices } from "./utils"; +import { FieldType, IFilterElement } from "../types"; + +const useStyles = makeStyles( + theme => ({ + filterSettings: { + background: fade(theme.palette.primary.main, 0.1), + padding: theme.spacing(2, 3) + }, + + option: { + left: -theme.spacing(0.5), + position: "relative" + }, + optionRadio: { + left: -theme.spacing(0.25) + } + }), + { name: "FilterContentBody" } +); + +const filterTestingContext = "filter-field"; export interface FilterContentBodyProps { + children?: React.ReactNode; filter: IFilterElement; - currencySymbol: string; + currencySymbol?: string; initialAutocompleteDisplayValues: FilterAutocompleteDisplayValues; onFilterPropertyChange: React.Dispatch>; autocompleteDisplayValues: FilterAutocompleteDisplayValues; @@ -38,65 +57,66 @@ const FilterContentBody: React.FC = ({ setAutocompleteDisplayValues, initialAutocompleteDisplayValues }) => { - const intl = useIntl(); const classes = useStyles({}); + const commonClasses = useCommonStyles({}); + + const isDateField = [FieldType.date, FieldType.dateTime].includes( + filter.type + ); + const isNumericField = [FieldType.price, FieldType.number].includes( + filter.type + ); return (
{children} {filter.type === FieldType.text && ( - + onFilterPropertyChange({ + payload: { + name: filter.name, + update: { + value: [event.target.value, filter.value[1]] + } + }, + type: "set-property" + }) + } /> )} - {[FieldType.date, FieldType.price, FieldType.number].includes( - filter.type - ) && ( + {isDateField && ( <> - - onFilterPropertyChange({ - payload: { - name: filter.name, - update: { - multiple: event.target.value === FilterType.MULTIPLE - } - }, - type: "set-property" - }) - } + + - -
-
- -
- {filter.multiple ? ( - - ) : ( - - )} -
)} + {isNumericField && ( + <> + + + + )} + {filter.type === FieldType.options && ( ({ + container: { + "&:not(:last-of-type)": { + borderBottom: `1px solid ${theme.palette.divider}` + }, + padding: theme.spacing(1, 2.5) + } + }), + { name: "FilterContentBodyNameField" } +); + +export interface FilterContentBodyNameFieldProps { + filter: IFilterElement; + onFilterPropertyChange: React.Dispatch>; +} + +const FilterContentBodyNameField: React.FC = ({ + filter, + onFilterPropertyChange +}) => { + const classes = useStyles({}); + + if (!filter) { + return null; + } + + return ( +
+ + } + label={filter.label} + onClick={event => event.stopPropagation()} + onChange={() => + onFilterPropertyChange({ + payload: { + name: filter.name, + update: { + active: !filter.active + } + }, + type: "set-property" + }) + } + /> +
+ ); +}; + +export default FilterContentBodyNameField; diff --git a/src/components/Filter/FilterContent/FilterDateTimeField.tsx b/src/components/Filter/FilterContent/FilterDateTimeField.tsx new file mode 100644 index 00000000000..3fdd78eac86 --- /dev/null +++ b/src/components/Filter/FilterContent/FilterDateTimeField.tsx @@ -0,0 +1,149 @@ +import { TextField } from "@material-ui/core"; +import { FieldType } from "@saleor/components/Filter"; +import Arrow from "@saleor/components/Filter/Arrow"; +import { splitDateTime } from "@saleor/misc"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { + FilterFieldBaseProps, + filterTestingContext, + getDateFilterValue, + getDateTimeFilterValue, + useCommonStyles +} from "./utils"; + +type FilterDateTimeFieldProps = FilterFieldBaseProps; + +export const FilterDateTimeField: React.FC = ({ + filter, + onFilterPropertyChange +}) => { + const classes = useCommonStyles({}); + const isDateTime = filter.type === FieldType.dateTime; + const isMultiple = filter.multiple; + + const handleChange = (value: string[]) => + onFilterPropertyChange({ + payload: { + name: filter.name, + update: { + value + } + }, + type: "set-property" + }); + + return ( + <> +
+
+ +
+ { + const value = getDateFilterValue( + event.target.value, + filter.value[0], + isDateTime + ); + handleChange(isMultiple ? [value, filter.value[1]] : [value]); + }} + /> + {isDateTime && ( + { + const value = getDateTimeFilterValue( + filter.value[0], + event.target.value + ); + handleChange(isMultiple ? [value, filter.value[1]] : [value]); + }} + /> + )} +
+ {filter.multiple && ( + <> +
+
+ + + +
+
+
+ + handleChange([ + filter.value[0], + getDateFilterValue( + event.target.value, + filter.value[1], + isDateTime + ) + ]) + } + /> + {isDateTime && ( + + handleChange([ + filter.value[0], + getDateTimeFilterValue(filter.value[1], event.target.value) + ]) + } + /> + )} +
+ + )} + + ); +}; diff --git a/src/components/Filter/FilterContent/FilterNumericField.tsx b/src/components/Filter/FilterContent/FilterNumericField.tsx new file mode 100644 index 00000000000..17013f87a3f --- /dev/null +++ b/src/components/Filter/FilterContent/FilterNumericField.tsx @@ -0,0 +1,97 @@ +import { TextField } from "@material-ui/core"; +import { FieldType } from "@saleor/components/Filter"; +import Arrow from "@saleor/components/Filter/Arrow"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { + FilterFieldBaseProps, + filterTestingContext, + useCommonStyles +} from "./utils"; + +type FilterNumericFieldProps = FilterFieldBaseProps & { + currencySymbol: string | undefined; +}; + +export const FilterNumericField: React.FC = ({ + filter, + onFilterPropertyChange, + currencySymbol +}) => { + const classes = useCommonStyles({}); + const isMultiple = filter.multiple; + + const handleChange = (value: string[]) => + onFilterPropertyChange({ + payload: { + name: filter.name, + update: { + value + } + }, + type: "set-property" + }); + + return ( + <> +
+
+ +
+ + handleChange(isMultiple ? [value, filter.value[1]] : [value]) + } + /> +
+ {filter.multiple && ( + <> +
+
+ + + +
+
+
+ + handleChange([filter.value[0], event.target.value]) + } + /> +
+ + )} + + ); +}; diff --git a/src/components/Filter/FilterContent/FilterSingleSelectField.tsx b/src/components/Filter/FilterContent/FilterSingleSelectField.tsx new file mode 100644 index 00000000000..6545832f6c6 --- /dev/null +++ b/src/components/Filter/FilterContent/FilterSingleSelectField.tsx @@ -0,0 +1,48 @@ +import { FilterType } from "@saleor/components/Filter"; +import { + FilterFieldBaseProps, + getIsFilterMultipleChoices, + useCommonStyles +} from "@saleor/components/Filter/FilterContent/utils"; +import FormSpacer from "@saleor/components/FormSpacer"; +import React from "react"; +import { useIntl } from "react-intl"; + +import SingleSelectField from "../../SingleSelectField/SingleSelectField"; + +type FilterSingleSelectFieldProps = FilterFieldBaseProps; + +export const FilterSingleSelectField: React.FC = ({ + filter, + onFilterPropertyChange +}) => { + const classes = useCommonStyles({}); + const intl = useIntl(); + + return ( + <> + + onFilterPropertyChange({ + payload: { + name: filter.name, + update: { + multiple: event.target.value === FilterType.MULTIPLE + } + }, + type: "set-property" + }) + } + /> + + + ); +}; diff --git a/src/components/Filter/FilterContent/utils.ts b/src/components/Filter/FilterContent/utils.ts index 1fc1a741f06..48cdcaa82c3 100644 --- a/src/components/Filter/FilterContent/utils.ts +++ b/src/components/Filter/FilterContent/utils.ts @@ -1,10 +1,45 @@ +import { FilterReducerAction } from "@saleor/components/Filter/reducer"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { makeStyles } from "@saleor/macaw-ui"; +import { joinDateTime, splitDateTime } from "@saleor/misc"; +import React from "react"; import { IntlShape } from "react-intl"; -import { FilterType } from "../types"; +import { FilterType, IFilterElement } from "../types"; export const filterTestingContext = "filter-field"; +export interface FilterFieldBaseProps { + filter: IFilterElement; + onFilterPropertyChange: React.Dispatch>; +} + +export const useCommonStyles = makeStyles( + theme => ({ + andLabel: { + margin: theme.spacing(1, 2, 1, 0) + }, + arrow: { + marginRight: theme.spacing(2) + }, + input: { + padding: "12px 0 9px 12px" + }, + inputRange: { + alignItems: "center", + display: "flex" + }, + inputTime: { + marginLeft: theme.spacing(1), + width: "150px" + }, + spacer: { + paddingRight: theme.spacing(4) + } + }), + { name: "FilterContentBodyCommon" } +); + export function getIsFilterMultipleChoices( intl: IntlShape ): SingleAutocompleteChoiceType[] { @@ -27,3 +62,24 @@ export function getIsFilterMultipleChoices( } ]; } + +export const getDateFilterValue = ( + dateTime: string, + dateTimeString: string | null, + dateTimeFormat: boolean +) => { + const { date } = splitDateTime(dateTime); + if (!dateTimeFormat) { + return date; + } + const { time } = splitDateTime(dateTimeString); + return joinDateTime(date, time); +}; + +export const getDateTimeFilterValue = ( + dateTimeString: string | null, + timeString: string +) => { + const { date } = splitDateTime(dateTimeString || new Date().toISOString()); + return joinDateTime(date, timeString); +}; diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx index a90617612e3..7180f4ccc85 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx @@ -63,6 +63,10 @@ const useStyles = makeStyles( margin: theme.spacing(1, 0), paddingLeft: theme.spacing(2), paddingRight: theme.spacing(1) + }, + adornment: { + display: "flex", + alignItems: "center" } }), { name: "MultiAutocompleteSelectField" } @@ -86,6 +90,7 @@ export interface MultiAutocompleteSelectFieldProps fetchChoices?: (value: string) => void; onChange: (event: React.ChangeEvent) => void; fetchOnFocus?: boolean; + endAdornment?: React.ReactNode; } const DebounceAutocomplete: React.ComponentType +
+ {endAdornment} toggleMenu()} />
), diff --git a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx index 6a4292b254d..2811bbfdf4a 100644 --- a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx +++ b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx @@ -5,6 +5,7 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import Savebar from "@saleor/components/Savebar"; import { createSaleChannelsChangeHandler } from "@saleor/discounts/handlers"; @@ -12,6 +13,7 @@ import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFrag import { sectionNames } from "@saleor/intl"; import { Backlink } from "@saleor/macaw-ui"; import { validatePrice } from "@saleor/products/utils/validation"; +import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; import { useIntl } from "react-intl"; @@ -24,7 +26,7 @@ import SaleInfo from "../SaleInfo"; import SaleType from "../SaleType"; import SaleValue from "../SaleValue"; -export interface FormData { +export interface FormData extends MetadataFormData { channelListings: ChannelSaleData[]; endDate: string; endTime: string; @@ -60,6 +62,9 @@ const SaleCreatePage: React.FC = ({ onBack }) => { const intl = useIntl(); + const { + makeChangeHandler: makeMetadataChangeHandler + } = useMetadataChangeTrigger(); const initialForm: FormData = { channelListings, @@ -70,7 +75,9 @@ const SaleCreatePage: React.FC = ({ startDate: "", startTime: "", type: SaleTypeEnum.FIXED, - value: "" + value: "", + metadata: [], + privateMetadata: [] }; return (
@@ -83,6 +90,8 @@ const SaleCreatePage: React.FC = ({ const formDisabled = data.channelListings?.some(channel => validatePrice(channel?.discountValue) ); + const changeMetadata = makeMetadataChangeHandler(change); + return ( @@ -132,6 +141,7 @@ const SaleCreatePage: React.FC = ({ openModal={openChannelsModal} />
+ = ({ toggleAll }) => { const intl = useIntl(); + const { + makeChangeHandler: makeMetadataChangeHandler + } = useMetadataChangeTrigger(); const initialForm: SaleDetailsPageFormData = { channelListings, - endDate: splitDateTime(maybe(() => sale.endDate, "")).date, - endTime: splitDateTime(maybe(() => sale.endDate, "")).time, - hasEndDate: maybe(() => !!sale.endDate), - name: maybe(() => sale.name, ""), - startDate: splitDateTime(maybe(() => sale.startDate, "")).date, - startTime: splitDateTime(maybe(() => sale.startDate, "")).time, - type: maybe(() => sale.type, SaleTypeEnum.FIXED) + endDate: splitDateTime(sale?.endDate ?? "").date, + endTime: splitDateTime(sale?.endDate ?? "").time, + hasEndDate: !!sale?.endDate, + name: sale?.name ?? "", + startDate: splitDateTime(sale?.startDate ?? "").date, + startTime: splitDateTime(sale?.startDate ?? "").time, + type: sale?.type ?? SaleTypeEnum.FIXED, + metadata: sale?.metadata.map(mapMetadataItemToInput), + privateMetadata: sale?.privateMetadata.map(mapMetadataItemToInput) }; return ( @@ -149,6 +157,8 @@ const SaleDetailsPage: React.FC = ({ const formDisabled = data.channelListings?.some(channel => validatePrice(channel.discountValue) ); + const changeMetadata = makeMetadataChangeHandler(change); + return ( @@ -307,6 +317,7 @@ const SaleDetailsPage: React.FC = ({ openModal={openChannelsModal} />
+ = ({ openChannelsModal }) => { const intl = useIntl(); + const { + makeChangeHandler: makeMetadataChangeHandler + } = useMetadataChangeTrigger(); const initialForm: FormData = { applyOncePerCustomer: false, @@ -91,7 +96,9 @@ const VoucherCreatePage: React.FC = ({ startTime: "", type: VoucherTypeEnum.ENTIRE_ORDER, usageLimit: "0", - value: 0 + value: 0, + metadata: [], + privateMetadata: [] }; return ( @@ -113,6 +120,8 @@ const VoucherCreatePage: React.FC = ({ (data.requirementsPicker === RequirementsPicker.ORDER && validatePrice(channel.minSpent)) ); + const changeMetadata = makeMetadataChangeHandler(change); + return ( @@ -189,6 +198,7 @@ const VoucherCreatePage: React.FC = ({ openModal={openChannelsModal} />
+ = ({ productListToolbar }) => { const intl = useIntl(); + const { + makeChangeHandler: makeMetadataChangeHandler + } = useMetadataChangeTrigger(); const channel = voucher?.channelListings?.find( listing => listing.channel.id === selectedChannelId ); @@ -175,19 +181,19 @@ const VoucherDetailsPage: React.FC = ({ channelListings, code: voucher?.code || "", discountType, - endDate: splitDateTime(maybe(() => voucher.endDate, "")).date, - endTime: splitDateTime(maybe(() => voucher.endDate, "")).time, - hasEndDate: maybe(() => !!voucher.endDate), - hasUsageLimit: maybe(() => !!voucher.usageLimit), - minCheckoutItemsQuantity: maybe( - () => voucher.minCheckoutItemsQuantity.toString(), - "0" - ), + endDate: splitDateTime(voucher?.endDate ?? "").date, + endTime: splitDateTime(voucher?.endDate ?? "").time, + hasEndDate: !!voucher?.endDate, + hasUsageLimit: !!voucher?.usageLimit, + minCheckoutItemsQuantity: + voucher?.minCheckoutItemsQuantity?.toString() ?? "0", requirementsPicker: requirementsPickerInitValue, - startDate: splitDateTime(maybe(() => voucher.startDate, "")).date, - startTime: splitDateTime(maybe(() => voucher.startDate, "")).time, - type: maybe(() => voucher.type, VoucherTypeEnum.ENTIRE_ORDER), - usageLimit: maybe(() => voucher.usageLimit.toString(), "0") + startDate: splitDateTime(voucher?.startDate ?? "").date, + startTime: splitDateTime(voucher?.startDate ?? "").time, + type: voucher?.type ?? VoucherTypeEnum.ENTIRE_ORDER, + usageLimit: voucher?.usageLimit?.toString() ?? "0", + metadata: voucher?.metadata.map(mapMetadataItemToInput), + privateMetadata: voucher?.privateMetadata.map(mapMetadataItemToInput) }; return ( @@ -209,6 +215,8 @@ const VoucherDetailsPage: React.FC = ({ (data.requirementsPicker === RequirementsPicker.ORDER && validatePrice(channel.minSpent)) ); + const changeMetadata = makeMetadataChangeHandler(change); + return ( @@ -420,6 +428,7 @@ const VoucherDetailsPage: React.FC = ({ openModal={openChannelsModal} />
+ { - 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/discounts/types/SaleCataloguesAdd.ts b/src/discounts/types/SaleCataloguesAdd.ts index f8bc9134726..59a3339752c 100644 --- a/src/discounts/types/SaleCataloguesAdd.ts +++ b/src/discounts/types/SaleCataloguesAdd.ts @@ -16,6 +16,18 @@ export interface SaleCataloguesAdd_saleCataloguesAdd_errors { channels: string[] | null; } +export interface SaleCataloguesAdd_saleCataloguesAdd_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleCataloguesAdd_saleCataloguesAdd_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleCataloguesAdd_saleCataloguesAdd_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -154,6 +166,8 @@ export interface SaleCataloguesAdd_saleCataloguesAdd_sale_collections { export interface SaleCataloguesAdd_saleCataloguesAdd_sale { __typename: "Sale"; + metadata: (SaleCataloguesAdd_saleCataloguesAdd_sale_metadata | null)[]; + privateMetadata: (SaleCataloguesAdd_saleCataloguesAdd_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleCataloguesRemove.ts b/src/discounts/types/SaleCataloguesRemove.ts index 2b65a11d1d6..c4fd89ad723 100644 --- a/src/discounts/types/SaleCataloguesRemove.ts +++ b/src/discounts/types/SaleCataloguesRemove.ts @@ -16,6 +16,18 @@ export interface SaleCataloguesRemove_saleCataloguesRemove_errors { channels: string[] | null; } +export interface SaleCataloguesRemove_saleCataloguesRemove_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleCataloguesRemove_saleCataloguesRemove_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleCataloguesRemove_saleCataloguesRemove_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -154,6 +166,8 @@ export interface SaleCataloguesRemove_saleCataloguesRemove_sale_collections { export interface SaleCataloguesRemove_saleCataloguesRemove_sale { __typename: "Sale"; + metadata: (SaleCataloguesRemove_saleCataloguesRemove_sale_metadata | null)[]; + privateMetadata: (SaleCataloguesRemove_saleCataloguesRemove_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleChannelListingUpdate.ts b/src/discounts/types/SaleChannelListingUpdate.ts index 74fc26c56b3..a79f495de18 100644 --- a/src/discounts/types/SaleChannelListingUpdate.ts +++ b/src/discounts/types/SaleChannelListingUpdate.ts @@ -16,6 +16,18 @@ export interface SaleChannelListingUpdate_saleChannelListingUpdate_errors { channels: string[] | null; } +export interface SaleChannelListingUpdate_saleChannelListingUpdate_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleChannelListingUpdate_saleChannelListingUpdate_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleChannelListingUpdate_saleChannelListingUpdate_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -33,6 +45,8 @@ export interface SaleChannelListingUpdate_saleChannelListingUpdate_sale_channelL export interface SaleChannelListingUpdate_saleChannelListingUpdate_sale { __typename: "Sale"; + metadata: (SaleChannelListingUpdate_saleChannelListingUpdate_sale_metadata | null)[]; + privateMetadata: (SaleChannelListingUpdate_saleChannelListingUpdate_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleCreate.ts b/src/discounts/types/SaleCreate.ts index 48b81303ed4..b4cf8367f32 100644 --- a/src/discounts/types/SaleCreate.ts +++ b/src/discounts/types/SaleCreate.ts @@ -16,6 +16,18 @@ export interface SaleCreate_saleCreate_errors { channels: string[] | null; } +export interface SaleCreate_saleCreate_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleCreate_saleCreate_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleCreate_saleCreate_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -33,6 +45,8 @@ export interface SaleCreate_saleCreate_sale_channelListings { export interface SaleCreate_saleCreate_sale { __typename: "Sale"; + metadata: (SaleCreate_saleCreate_sale_metadata | null)[]; + privateMetadata: (SaleCreate_saleCreate_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleDetails.ts b/src/discounts/types/SaleDetails.ts index 8290263acca..860aaebdfda 100644 --- a/src/discounts/types/SaleDetails.ts +++ b/src/discounts/types/SaleDetails.ts @@ -9,6 +9,18 @@ import { SaleType } from "./../../types/globalTypes"; // GraphQL query operation: SaleDetails // ==================================================== +export interface SaleDetails_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleDetails_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleDetails_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -147,6 +159,8 @@ export interface SaleDetails_sale_collections { export interface SaleDetails_sale { __typename: "Sale"; + metadata: (SaleDetails_sale_metadata | null)[]; + privateMetadata: (SaleDetails_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleList.ts b/src/discounts/types/SaleList.ts index 7ab2a80db6e..d682f9aa75a 100644 --- a/src/discounts/types/SaleList.ts +++ b/src/discounts/types/SaleList.ts @@ -9,6 +9,18 @@ import { SaleFilterInput, SaleSortingInput, SaleType } from "./../../types/globa // GraphQL query operation: SaleList // ==================================================== +export interface SaleList_sales_edges_node_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleList_sales_edges_node_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleList_sales_edges_node_channelListings_channel { __typename: "Channel"; id: string; @@ -26,6 +38,8 @@ export interface SaleList_sales_edges_node_channelListings { export interface SaleList_sales_edges_node { __typename: "Sale"; + metadata: (SaleList_sales_edges_node_metadata | null)[]; + privateMetadata: (SaleList_sales_edges_node_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/SaleUpdate.ts b/src/discounts/types/SaleUpdate.ts index e8420069c49..c18a03f3b54 100644 --- a/src/discounts/types/SaleUpdate.ts +++ b/src/discounts/types/SaleUpdate.ts @@ -16,6 +16,18 @@ export interface SaleUpdate_saleUpdate_errors { channels: string[] | null; } +export interface SaleUpdate_saleUpdate_sale_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleUpdate_saleUpdate_sale_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleUpdate_saleUpdate_sale_channelListings_channel { __typename: "Channel"; id: string; @@ -33,6 +45,8 @@ export interface SaleUpdate_saleUpdate_sale_channelListings { export interface SaleUpdate_saleUpdate_sale { __typename: "Sale"; + metadata: (SaleUpdate_saleUpdate_sale_metadata | null)[]; + privateMetadata: (SaleUpdate_saleUpdate_sale_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/discounts/types/VoucherCataloguesAdd.ts b/src/discounts/types/VoucherCataloguesAdd.ts index adc42177ec4..aa414d2bcda 100644 --- a/src/discounts/types/VoucherCataloguesAdd.ts +++ b/src/discounts/types/VoucherCataloguesAdd.ts @@ -16,6 +16,18 @@ export interface VoucherCataloguesAdd_voucherCataloguesAdd_errors { channels: string[] | null; } +export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -167,6 +179,8 @@ export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories { export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher { __typename: "Voucher"; + metadata: (VoucherCataloguesAdd_voucherCataloguesAdd_voucher_metadata | null)[]; + privateMetadata: (VoucherCataloguesAdd_voucherCataloguesAdd_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherCataloguesRemove.ts b/src/discounts/types/VoucherCataloguesRemove.ts index 7479dbabf99..238a3559d5a 100644 --- a/src/discounts/types/VoucherCataloguesRemove.ts +++ b/src/discounts/types/VoucherCataloguesRemove.ts @@ -16,6 +16,18 @@ export interface VoucherCataloguesRemove_voucherCataloguesRemove_errors { channels: string[] | null; } +export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -167,6 +179,8 @@ export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categor export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher { __typename: "Voucher"; + metadata: (VoucherCataloguesRemove_voucherCataloguesRemove_voucher_metadata | null)[]; + privateMetadata: (VoucherCataloguesRemove_voucherCataloguesRemove_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherChannelListingUpdate.ts b/src/discounts/types/VoucherChannelListingUpdate.ts index 98821aca5f2..4e8531ff025 100644 --- a/src/discounts/types/VoucherChannelListingUpdate.ts +++ b/src/discounts/types/VoucherChannelListingUpdate.ts @@ -16,6 +16,18 @@ export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_errors channels: string[] | null; } +export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -46,6 +58,8 @@ export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher export interface VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher { __typename: "Voucher"; + metadata: (VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher_metadata | null)[]; + privateMetadata: (VoucherChannelListingUpdate_voucherChannelListingUpdate_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherCreate.ts b/src/discounts/types/VoucherCreate.ts index 75e5dc5e142..06573c101a3 100644 --- a/src/discounts/types/VoucherCreate.ts +++ b/src/discounts/types/VoucherCreate.ts @@ -16,6 +16,18 @@ export interface VoucherCreate_voucherCreate_errors { channels: string[] | null; } +export interface VoucherCreate_voucherCreate_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherCreate_voucherCreate_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherCreate_voucherCreate_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -46,6 +58,8 @@ export interface VoucherCreate_voucherCreate_voucher_channelListings { export interface VoucherCreate_voucherCreate_voucher { __typename: "Voucher"; + metadata: (VoucherCreate_voucherCreate_voucher_metadata | null)[]; + privateMetadata: (VoucherCreate_voucherCreate_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherDetails.ts b/src/discounts/types/VoucherDetails.ts index 86275d0086a..f9206df544c 100644 --- a/src/discounts/types/VoucherDetails.ts +++ b/src/discounts/types/VoucherDetails.ts @@ -9,6 +9,18 @@ import { VoucherTypeEnum, DiscountValueTypeEnum } from "./../../types/globalType // GraphQL query operation: VoucherDetails // ==================================================== +export interface VoucherDetails_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherDetails_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherDetails_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -160,6 +172,8 @@ export interface VoucherDetails_voucher_categories { export interface VoucherDetails_voucher { __typename: "Voucher"; + metadata: (VoucherDetails_voucher_metadata | null)[]; + privateMetadata: (VoucherDetails_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherList.ts b/src/discounts/types/VoucherList.ts index b792fdec9c0..de58cf45bbf 100644 --- a/src/discounts/types/VoucherList.ts +++ b/src/discounts/types/VoucherList.ts @@ -9,6 +9,18 @@ import { VoucherFilterInput, VoucherSortingInput, VoucherTypeEnum, DiscountValue // GraphQL query operation: VoucherList // ==================================================== +export interface VoucherList_vouchers_edges_node_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherList_vouchers_edges_node_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherList_vouchers_edges_node_countries { __typename: "CountryDisplay"; code: string; @@ -39,6 +51,8 @@ export interface VoucherList_vouchers_edges_node_channelListings { export interface VoucherList_vouchers_edges_node { __typename: "Voucher"; + metadata: (VoucherList_vouchers_edges_node_metadata | null)[]; + privateMetadata: (VoucherList_vouchers_edges_node_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/types/VoucherUpdate.ts b/src/discounts/types/VoucherUpdate.ts index c3f6d9beeeb..7bb006bd66a 100644 --- a/src/discounts/types/VoucherUpdate.ts +++ b/src/discounts/types/VoucherUpdate.ts @@ -16,6 +16,18 @@ export interface VoucherUpdate_voucherUpdate_errors { channels: string[] | null; } +export interface VoucherUpdate_voucherUpdate_voucher_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherUpdate_voucherUpdate_voucher_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherUpdate_voucherUpdate_voucher_countries { __typename: "CountryDisplay"; code: string; @@ -46,6 +58,8 @@ export interface VoucherUpdate_voucherUpdate_voucher_channelListings { export interface VoucherUpdate_voucherUpdate_voucher { __typename: "Voucher"; + metadata: (VoucherUpdate_voucherUpdate_voucher_metadata | null)[]; + privateMetadata: (VoucherUpdate_voucherUpdate_voucher_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/discounts/views/SaleCreate/SaleCreate.tsx b/src/discounts/views/SaleCreate/SaleCreate.tsx index 208b48cba68..3a536994bfd 100644 --- a/src/discounts/views/SaleCreate/SaleCreate.tsx +++ b/src/discounts/views/SaleCreate/SaleCreate.tsx @@ -20,6 +20,11 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { sectionNames } from "@saleor/intl"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; +import { + useMetadataUpdate, + usePrivateMetadataUpdate +} from "@saleor/utils/metadata/updateMetadata"; import React from "react"; import { useIntl } from "react-intl"; @@ -34,6 +39,8 @@ export const SaleCreateView: React.FC = ({ params }) => { const pushMessage = useNotifier(); const intl = useIntl(); + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const [openModal, closeModal] = createDialogActionHandlers< ChannelsAction, SaleCreateUrlQueryParams @@ -92,10 +99,16 @@ export const SaleCreateView: React.FC = ({ params }) => { )} {(saleCreate, saleCreateOpts) => { - const handleSubmit = createHandler( + const handleCreate = createHandler( variables => saleCreate({ variables }), updateChannels ); + const handleSubmit = createMetadataCreateHandler( + handleCreate, + updateMetadata, + updatePrivateMetadata + ); + return ( <> diff --git a/src/discounts/views/SaleCreate/handlers.ts b/src/discounts/views/SaleCreate/handlers.ts index c173a2637f9..938f40122fb 100644 --- a/src/discounts/views/SaleCreate/handlers.ts +++ b/src/discounts/views/SaleCreate/handlers.ts @@ -39,6 +39,7 @@ export function createHandler( value: decimal(formData.value) } }); + if (!response.data.saleCreate.errors.length) { updateChannels({ variables: getSaleChannelsVariables( @@ -46,6 +47,7 @@ export function createHandler( formData ) }); + return response.data.saleCreate.sale.id; } }; } diff --git a/src/discounts/views/SaleDetails/SaleDetails.tsx b/src/discounts/views/SaleDetails/SaleDetails.tsx index 7ef9a6667f7..8ed0bdcb291 100644 --- a/src/discounts/views/SaleDetails/SaleDetails.tsx +++ b/src/discounts/views/SaleDetails/SaleDetails.tsx @@ -50,7 +50,12 @@ import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductSearch from "@saleor/searches/useProductSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { mapEdgesToItems } from "@saleor/utils/maps"; +import { + useMetadataUpdate, + usePrivateMetadataUpdate +} from "@saleor/utils/metadata/updateMetadata"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -62,6 +67,8 @@ interface SaleDetailsProps { } export const SaleDetails: React.FC = ({ id, params }) => { + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const navigate = useNavigator(); const paginate = usePaginator(); const notify = useNotifier(); @@ -253,12 +260,19 @@ export const SaleDetails: React.FC = ({ id, params }) => { pageInfo } = paginate(tabPageInfo, paginationState, params); - const handleSubmit = createUpdateHandler( + const handleUpdate = createUpdateHandler( data?.sale, saleChannelsChoices, variables => saleUpdate({ variables }), updateChannels ); + const handleSubmit = createMetadataUpdateHandler( + data?.sale, + handleUpdate, + variables => updateMetadata({ variables }), + variables => updatePrivateMetadata({ variables }) + ); + return ( <> Promise> ) { - return (formData: SaleDetailsPageFormData) => { + return async (formData: SaleDetailsPageFormData) => { const { id } = sale; - updateSale({ - id, - input: { - endDate: formData.hasEndDate - ? joinDateTime(formData.endDate, formData.endTime) - : null, - name: formData.name, - startDate: joinDateTime(formData.startDate, formData.startTime), - type: discountValueTypeEnum(formData.type) - } - }); + const errors = await Promise.all([ + updateSale({ + id, + input: { + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + name: formData.name, + startDate: joinDateTime(formData.startDate, formData.startTime), + type: discountValueTypeEnum(formData.type) + } + }).then(({ data }) => data?.saleUpdate.errors ?? []), - updateChannels({ - variables: getSaleChannelsVariables(id, formData, saleChannelsChoices) - }); + updateChannels({ + variables: getSaleChannelsVariables(id, formData, saleChannelsChoices) + }).then(({ data }) => data?.saleChannelListingUpdate.errors ?? []) + ]); + + return errors.flat(); }; } diff --git a/src/discounts/views/VoucherCreate/VoucherCreate.tsx b/src/discounts/views/VoucherCreate/VoucherCreate.tsx index d5ff715fed3..4143eb12b43 100644 --- a/src/discounts/views/VoucherCreate/VoucherCreate.tsx +++ b/src/discounts/views/VoucherCreate/VoucherCreate.tsx @@ -11,6 +11,11 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { sectionNames } from "@saleor/intl"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; +import { + useMetadataUpdate, + usePrivateMetadataUpdate +} from "@saleor/utils/metadata/updateMetadata"; import React from "react"; import { useIntl } from "react-intl"; @@ -37,6 +42,8 @@ export const VoucherCreateView: React.FC = ({ params }) => { const notify = useNotifier(); const intl = useIntl(); + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const [openModal, closeModal] = createDialogActionHandlers< ChannelsAction, VoucherCreateUrlQueryParams @@ -79,10 +86,16 @@ export const VoucherCreateView: React.FC = ({ params }) => { return ( {(voucherCreate, voucherCreateOpts) => { - const handleSubmit = createHandler( + const handleCreate = createHandler( variables => voucherCreate({ variables }), updateChannels ); + const handleSubmit = createMetadataCreateHandler( + handleCreate, + updateMetadata, + updatePrivateMetadata + ); + return ( <> {!!allChannels?.length && ( diff --git a/src/discounts/views/VoucherCreate/handlers.ts b/src/discounts/views/VoucherCreate/handlers.ts index 9e05738388e..a2fdb138f97 100644 --- a/src/discounts/views/VoucherCreate/handlers.ts +++ b/src/discounts/views/VoucherCreate/handlers.ts @@ -63,6 +63,7 @@ export function createHandler( formData.channelListings ) }); + return response.data.voucherCreate.voucher.id; } }; } diff --git a/src/discounts/views/VoucherDetails/VoucherDetails.tsx b/src/discounts/views/VoucherDetails/VoucherDetails.tsx index 5e31131c737..31f0f45e840 100644 --- a/src/discounts/views/VoucherDetails/VoucherDetails.tsx +++ b/src/discounts/views/VoucherDetails/VoucherDetails.tsx @@ -47,7 +47,12 @@ import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductSearch from "@saleor/searches/useProductSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { mapEdgesToItems } from "@saleor/utils/maps"; +import { + useMetadataUpdate, + usePrivateMetadataUpdate +} from "@saleor/utils/metadata/updateMetadata"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -95,6 +100,8 @@ export const VoucherDetails: React.FC = ({ } = useProductSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const paginationState = createPaginationState(PAGINATE_BY, params); const changeTab = (tab: VoucherDetailsPageTab) => { @@ -218,13 +225,20 @@ export const VoucherDetails: React.FC = ({ {(voucherUpdate, voucherUpdateOpts) => ( {(voucherDelete, voucherDeleteOpts) => { - const handleSubmit = createUpdateHandler( + const handleUpdate = createUpdateHandler( data?.voucher, voucherChannelsChoices, variables => voucherUpdate({ variables }), updateChannels ); + const handleSubmit = createMetadataUpdateHandler( + data?.voucher, + handleUpdate, + variables => updateMetadata({ variables }), + variables => updatePrivateMetadata({ variables }) + ); + const tabPageInfo = params.activeTab === VoucherDetailsPageTab.categories ? maybe(() => data.voucher.categories.pageInfo) diff --git a/src/discounts/views/VoucherDetails/handlers.ts b/src/discounts/views/VoucherDetails/handlers.ts index 9244cafdacb..1f808a61869 100644 --- a/src/discounts/views/VoucherDetails/handlers.ts +++ b/src/discounts/views/VoucherDetails/handlers.ts @@ -30,38 +30,43 @@ export function createUpdateHandler( ) { return async (formData: VoucherDetailsPageFormData) => { const { id } = voucher; - await updateVoucher({ - id, - input: { - applyOncePerCustomer: formData.applyOncePerCustomer, - applyOncePerOrder: formData.applyOncePerOrder, - onlyForStaff: formData.onlyForStaff, - discountValueType: - formData.discountType === DiscountTypeEnum.VALUE_PERCENTAGE - ? DiscountValueTypeEnum.PERCENTAGE - : formData.discountType === DiscountTypeEnum.VALUE_FIXED - ? DiscountValueTypeEnum.FIXED - : DiscountValueTypeEnum.PERCENTAGE, - endDate: formData.hasEndDate - ? joinDateTime(formData.endDate, formData.endTime) - : null, - minCheckoutItemsQuantity: - formData.requirementsPicker !== RequirementsPicker.ITEM - ? 0 - : parseFloat(formData.minCheckoutItemsQuantity), - startDate: joinDateTime(formData.startDate, formData.startTime), - type: - formData.discountType === DiscountTypeEnum.SHIPPING - ? VoucherTypeEnum.SHIPPING - : formData.type, - usageLimit: formData.hasUsageLimit - ? parseInt(formData.usageLimit, 10) - : null - } - }); - updateChannels({ - variables: getChannelsVariables(id, formData, voucherChannelsChoices) - }); + const errors = await Promise.all([ + updateVoucher({ + id, + input: { + applyOncePerCustomer: formData.applyOncePerCustomer, + applyOncePerOrder: formData.applyOncePerOrder, + onlyForStaff: formData.onlyForStaff, + discountValueType: + formData.discountType === DiscountTypeEnum.VALUE_PERCENTAGE + ? DiscountValueTypeEnum.PERCENTAGE + : formData.discountType === DiscountTypeEnum.VALUE_FIXED + ? DiscountValueTypeEnum.FIXED + : DiscountValueTypeEnum.PERCENTAGE, + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + minCheckoutItemsQuantity: + formData.requirementsPicker !== RequirementsPicker.ITEM + ? 0 + : parseFloat(formData.minCheckoutItemsQuantity), + startDate: joinDateTime(formData.startDate, formData.startTime), + type: + formData.discountType === DiscountTypeEnum.SHIPPING + ? VoucherTypeEnum.SHIPPING + : formData.type, + usageLimit: formData.hasUsageLimit + ? parseInt(formData.usageLimit, 10) + : null + } + }).then(({ data }) => data?.voucherUpdate.errors ?? []), + + updateChannels({ + variables: getChannelsVariables(id, formData, voucherChannelsChoices) + }).then(({ data }) => data?.voucherChannelListingUpdate.errors ?? []) + ]); + + return errors.flat(); }; } diff --git a/src/fragments/attributes.ts b/src/fragments/attributes.ts index e409a0f99d6..1a4ab276e65 100644 --- a/src/fragments/attributes.ts +++ b/src/fragments/attributes.ts @@ -16,6 +16,8 @@ export const attributeValueFragment = gql` reference richText boolean + date + dateTime } `; diff --git a/src/fragments/discounts.ts b/src/fragments/discounts.ts index 491954fd469..48972d1fd27 100644 --- a/src/fragments/discounts.ts +++ b/src/fragments/discounts.ts @@ -1,10 +1,13 @@ +import { metadataFragment } from "@saleor/fragments/metadata"; import { channelListingProductWithoutPricingFragment } from "@saleor/fragments/products"; import gql from "graphql-tag"; import { pageInfoFragment } from "./pageInfo"; export const saleFragment = gql` + ${metadataFragment} fragment SaleFragment on Sale { + ...MetadataFragment id name type @@ -85,7 +88,9 @@ export const saleDetailsFragment = gql` `; export const voucherFragment = gql` + ${metadataFragment} fragment VoucherFragment on Voucher { + ...MetadataFragment id code startDate diff --git a/src/fragments/products.ts b/src/fragments/products.ts index b8138c007a9..6ed99808067 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -150,6 +150,8 @@ export const productVariantAttributesFragment = gql` variantAttributes(variantSelection: VARIANT_SELECTION) { id name + inputType + unit choices( first: $firstValues after: $afterValues diff --git a/src/fragments/types/AttributeValueFragment.ts b/src/fragments/types/AttributeValueFragment.ts index bb0841b86cb..b16f4005083 100644 --- a/src/fragments/types/AttributeValueFragment.ts +++ b/src/fragments/types/AttributeValueFragment.ts @@ -22,4 +22,6 @@ export interface AttributeValueFragment { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } diff --git a/src/fragments/types/AttributeValueListFragment.ts b/src/fragments/types/AttributeValueListFragment.ts index 320b7960961..02f69d59f28 100644 --- a/src/fragments/types/AttributeValueListFragment.ts +++ b/src/fragments/types/AttributeValueListFragment.ts @@ -30,6 +30,8 @@ export interface AttributeValueListFragment_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface AttributeValueListFragment_edges { diff --git a/src/fragments/types/MetadataFragment.ts b/src/fragments/types/MetadataFragment.ts index dea14e3f616..f36f74ccac0 100644 --- a/src/fragments/types/MetadataFragment.ts +++ b/src/fragments/types/MetadataFragment.ts @@ -20,7 +20,7 @@ export interface MetadataFragment_privateMetadata { } export interface MetadataFragment { - __typename: "App" | "Warehouse" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; + __typename: "App" | "Warehouse" | "ShippingZone" | "ShippingMethod" | "Product" | "ProductType" | "Attribute" | "Category" | "ProductVariant" | "DigitalContent" | "Collection" | "Page" | "PageType" | "Sale" | "Voucher" | "MenuItem" | "Menu" | "User" | "Checkout" | "Order" | "Fulfillment" | "Invoice"; metadata: (MetadataFragment_metadata | null)[]; privateMetadata: (MetadataFragment_privateMetadata | null)[]; } diff --git a/src/fragments/types/PageAttributesFragment.ts b/src/fragments/types/PageAttributesFragment.ts index c4bf17bb2d1..41ef483254a 100644 --- a/src/fragments/types/PageAttributesFragment.ts +++ b/src/fragments/types/PageAttributesFragment.ts @@ -32,6 +32,8 @@ export interface PageAttributesFragment_attributes_attribute_choices_edges_node reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageAttributesFragment_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface PageAttributesFragment_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageAttributesFragment_attributes { @@ -104,6 +108,8 @@ export interface PageAttributesFragment_pageType_attributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageAttributesFragment_pageType_attributes_choices_edges { diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index 3ba8f2171a6..313a8b5bfc8 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -32,6 +32,8 @@ export interface PageDetailsFragment_attributes_attribute_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetailsFragment_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface PageDetailsFragment_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetailsFragment_attributes { @@ -104,6 +108,8 @@ export interface PageDetailsFragment_pageType_attributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetailsFragment_pageType_attributes_choices_edges { diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index 575c8dc1b78..1f0eaa127b8 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -32,6 +32,8 @@ export interface Product_attributes_attribute_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface Product_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface Product_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface Product_attributes { @@ -104,6 +108,8 @@ export interface Product_productType_variantAttributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface Product_productType_variantAttributes_choices_edges { @@ -122,6 +128,8 @@ export interface Product_productType_variantAttributes { __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: Product_productType_variantAttributes_choices | null; } diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index 70afef82689..bcd18ab3d45 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -44,6 +44,8 @@ export interface ProductVariant_selectionAttributes_attribute_choices_edges_node reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariant_selectionAttributes_attribute_choices_edges { @@ -85,6 +87,8 @@ export interface ProductVariant_selectionAttributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariant_selectionAttributes { @@ -116,6 +120,8 @@ export interface ProductVariant_nonSelectionAttributes_attribute_choices_edges_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariant_nonSelectionAttributes_attribute_choices_edges { @@ -157,6 +163,8 @@ export interface ProductVariant_nonSelectionAttributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariant_nonSelectionAttributes { diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index b09c02789e8..cc79bcd95e1 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -32,6 +32,8 @@ export interface ProductVariantAttributesFragment_attributes_attribute_choices_e reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantAttributesFragment_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface ProductVariantAttributesFragment_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantAttributesFragment_attributes { @@ -104,6 +108,8 @@ export interface ProductVariantAttributesFragment_productType_variantAttributes_ reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantAttributesFragment_productType_variantAttributes_choices_edges { @@ -122,6 +128,8 @@ export interface ProductVariantAttributesFragment_productType_variantAttributes __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: ProductVariantAttributesFragment_productType_variantAttributes_choices | null; } diff --git a/src/fragments/types/SaleDetailsFragment.ts b/src/fragments/types/SaleDetailsFragment.ts index e3196e615de..7dc3d36f377 100644 --- a/src/fragments/types/SaleDetailsFragment.ts +++ b/src/fragments/types/SaleDetailsFragment.ts @@ -9,6 +9,18 @@ import { SaleType } from "./../../types/globalTypes"; // GraphQL fragment: SaleDetailsFragment // ==================================================== +export interface SaleDetailsFragment_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleDetailsFragment_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleDetailsFragment_channelListings_channel { __typename: "Channel"; id: string; @@ -147,6 +159,8 @@ export interface SaleDetailsFragment_collections { export interface SaleDetailsFragment { __typename: "Sale"; + metadata: (SaleDetailsFragment_metadata | null)[]; + privateMetadata: (SaleDetailsFragment_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/fragments/types/SaleFragment.ts b/src/fragments/types/SaleFragment.ts index 5056957c526..474ffb0bec6 100644 --- a/src/fragments/types/SaleFragment.ts +++ b/src/fragments/types/SaleFragment.ts @@ -9,6 +9,18 @@ import { SaleType } from "./../../types/globalTypes"; // GraphQL fragment: SaleFragment // ==================================================== +export interface SaleFragment_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface SaleFragment_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface SaleFragment_channelListings_channel { __typename: "Channel"; id: string; @@ -26,6 +38,8 @@ export interface SaleFragment_channelListings { export interface SaleFragment { __typename: "Sale"; + metadata: (SaleFragment_metadata | null)[]; + privateMetadata: (SaleFragment_privateMetadata | null)[]; id: string; name: string; type: SaleType; diff --git a/src/fragments/types/SelectedVariantAttributeFragment.ts b/src/fragments/types/SelectedVariantAttributeFragment.ts index bdf0efb3078..f56c418b87d 100644 --- a/src/fragments/types/SelectedVariantAttributeFragment.ts +++ b/src/fragments/types/SelectedVariantAttributeFragment.ts @@ -32,6 +32,8 @@ export interface SelectedVariantAttributeFragment_attribute_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SelectedVariantAttributeFragment_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface SelectedVariantAttributeFragment_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SelectedVariantAttributeFragment { diff --git a/src/fragments/types/VariantAttributeFragment.ts b/src/fragments/types/VariantAttributeFragment.ts index 4b32765a6e7..bbdf5d3a05f 100644 --- a/src/fragments/types/VariantAttributeFragment.ts +++ b/src/fragments/types/VariantAttributeFragment.ts @@ -32,6 +32,8 @@ export interface VariantAttributeFragment_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantAttributeFragment_choices_edges { diff --git a/src/fragments/types/VoucherDetailsFragment.ts b/src/fragments/types/VoucherDetailsFragment.ts index 3a09ccdf0d3..5442a40e1fc 100644 --- a/src/fragments/types/VoucherDetailsFragment.ts +++ b/src/fragments/types/VoucherDetailsFragment.ts @@ -9,6 +9,18 @@ import { VoucherTypeEnum, DiscountValueTypeEnum } from "./../../types/globalType // GraphQL fragment: VoucherDetailsFragment // ==================================================== +export interface VoucherDetailsFragment_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherDetailsFragment_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherDetailsFragment_countries { __typename: "CountryDisplay"; code: string; @@ -160,6 +172,8 @@ export interface VoucherDetailsFragment_categories { export interface VoucherDetailsFragment { __typename: "Voucher"; + metadata: (VoucherDetailsFragment_metadata | null)[]; + privateMetadata: (VoucherDetailsFragment_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/fragments/types/VoucherFragment.ts b/src/fragments/types/VoucherFragment.ts index 4ef7334a272..52b680e5b0f 100644 --- a/src/fragments/types/VoucherFragment.ts +++ b/src/fragments/types/VoucherFragment.ts @@ -9,6 +9,18 @@ import { VoucherTypeEnum, DiscountValueTypeEnum } from "./../../types/globalType // GraphQL fragment: VoucherFragment // ==================================================== +export interface VoucherFragment_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface VoucherFragment_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + export interface VoucherFragment_countries { __typename: "CountryDisplay"; code: string; @@ -39,6 +51,8 @@ export interface VoucherFragment_channelListings { export interface VoucherFragment { __typename: "Voucher"; + metadata: (VoucherFragment_metadata | null)[]; + privateMetadata: (VoucherFragment_privateMetadata | null)[]; id: string; code: string; startDate: any; diff --git a/src/intl.ts b/src/intl.ts index 1554cc8ddfa..6d750a09f4a 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -111,6 +111,13 @@ export const commonMessages = defineMessages({ }, yes: { defaultMessage: "Yes" + }, + date: { + defaultMessage: "Date" + }, + time: { + defaultMessage: "Time", + description: "independent of any particular day, eg. 11:35" } }); diff --git a/src/misc.ts b/src/misc.ts index e1c9a28d7bb..08de30e3133 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -346,6 +346,11 @@ export function stopPropagation(cb: (event?: AnyEvent) => void) { }; } +export interface DateTime { + date: string; + time: string; +} + export function joinDateTime(date: string, time?: string) { if (!date) { return null; diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index 6cfb40444ef..b5e8de2766d 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -70,7 +70,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -84,7 +86,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -98,7 +102,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -114,7 +120,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ], __typename: "SelectedAttribute" @@ -149,7 +157,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -163,7 +173,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -177,7 +189,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -191,7 +205,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -207,7 +223,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ], __typename: "SelectedAttribute" @@ -255,7 +273,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -269,7 +289,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -283,7 +305,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -317,7 +341,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -331,7 +357,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -345,7 +373,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -359,7 +389,9 @@ export const page: PageDetails_page = { __typename: "AttributeValue", file: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index 5fa3190ece7..6d8f71b4926 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -32,6 +32,8 @@ export interface PageDetails_page_attributes_attribute_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetails_page_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface PageDetails_page_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetails_page_attributes { @@ -104,6 +108,8 @@ export interface PageDetails_page_pageType_attributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageDetails_page_pageType_attributes_choices_edges { diff --git a/src/pages/types/PageType.ts b/src/pages/types/PageType.ts index 3db36642051..3b322fffebe 100644 --- a/src/pages/types/PageType.ts +++ b/src/pages/types/PageType.ts @@ -32,6 +32,8 @@ export interface PageType_pageType_attributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageType_pageType_attributes_choices_edges { diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index 616217ff6ba..394e579e7dd 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -39,6 +39,8 @@ export interface PageUpdate_pageUpdate_page_attributes_attribute_choices_edges_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageUpdate_pageUpdate_page_attributes_attribute_choices_edges { @@ -80,6 +82,8 @@ export interface PageUpdate_pageUpdate_page_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageUpdate_pageUpdate_page_attributes { @@ -111,6 +115,8 @@ export interface PageUpdate_pageUpdate_page_pageType_attributes_choices_edges_no reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface PageUpdate_pageUpdate_page_pageType_attributes_choices_edges { diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index 9a9e9925247..e6e7e3338ba 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -47,7 +47,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -64,7 +66,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -105,7 +109,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -122,7 +128,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -139,7 +147,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -156,7 +166,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -197,7 +209,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -238,7 +252,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -255,7 +271,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -272,7 +290,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -313,7 +333,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -330,7 +352,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -371,7 +395,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -388,7 +414,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -405,7 +433,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -446,7 +476,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -463,7 +495,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -504,7 +538,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -521,7 +557,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -538,7 +576,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -555,7 +595,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -572,7 +614,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -589,7 +633,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -630,7 +676,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -647,7 +695,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -688,7 +738,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -705,7 +757,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -746,7 +800,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -763,7 +819,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -804,7 +862,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -821,7 +881,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -838,7 +900,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -855,7 +919,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -872,7 +938,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -889,7 +957,9 @@ export const attributes: ProductType_productType_productAttributes[] = [ type: "STRING", value: "", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] diff --git a/src/products/components/ProductListPage/filters.ts b/src/products/components/ProductListPage/filters.ts index 3b7462f7c20..03c5f4df74c 100644 --- a/src/products/components/ProductListPage/filters.ts +++ b/src/products/components/ProductListPage/filters.ts @@ -9,6 +9,8 @@ import { import { createAutocompleteField, createBooleanField, + createDateField, + createDateTimeField, createOptionsField, createPriceField } from "@saleor/utils/filters/fields"; @@ -24,15 +26,15 @@ export enum ProductFilterKeys { channel = "channel" } +export type AttributeFilterOpts = FilterOpts & { + id: string; + name: string; + slug: string; + inputType: AttributeInputTypeEnum; +}; + export interface ProductListFilterOpts { - attributes: Array< - FilterOpts & { - id: string; - name: string; - slug: string; - inputType: AttributeInputTypeEnum; - } - >; + attributes: AttributeFilterOpts[]; attributeChoices: FilterOpts & AutocompleteFilterOpts; categories: FilterOpts & AutocompleteFilterOpts; collections: FilterOpts & AutocompleteFilterOpts; @@ -76,15 +78,33 @@ const messages = defineMessages({ } }); +const filterByType = (type: AttributeInputTypeEnum) => ( + attribute: AttributeFilterOpts +) => attribute.inputType === type; + export function createFilterStructure( intl: IntlShape, opts: ProductListFilterOpts ): IFilter { - const booleanAttributes = opts.attributes.filter( - ({ inputType }) => inputType === AttributeInputTypeEnum.BOOLEAN + const attributes = opts.attributes; + + const booleanAttributes = attributes.filter( + filterByType(AttributeInputTypeEnum.BOOLEAN) + ); + const dateAttributes = attributes.filter( + filterByType(AttributeInputTypeEnum.DATE) ); + const dateTimeAttributes = attributes.filter( + filterByType(AttributeInputTypeEnum.DATE_TIME) + ); + const defaultAttributes = opts.attributes.filter( - ({ inputType }) => !inputType.includes(AttributeInputTypeEnum.BOOLEAN) + ({ inputType }) => + ![ + AttributeInputTypeEnum.BOOLEAN, + AttributeInputTypeEnum.DATE, + AttributeInputTypeEnum.DATE_TIME + ].includes(inputType) ); return [ @@ -195,9 +215,25 @@ export function createFilterStructure( active: attr.active, group: ProductFilterKeys.attributes })), + ...dateAttributes.map(attr => ({ + ...createDateField(attr.slug, attr.name, { + min: attr.value[0], + max: attr.value[1] ?? attr.value[0] + }), + active: attr.active, + group: ProductFilterKeys.attributes + })), + ...dateTimeAttributes.map(attr => ({ + ...createDateTimeField(attr.slug, attr.name, { + min: attr.value[0], + max: attr.value[1] ?? attr.value[0] + }), + active: attr.active, + group: ProductFilterKeys.attributes + })), ...defaultAttributes.map(attr => ({ ...createAutocompleteField( - attr.slug as any, + attr.slug, attr.name, attr.value, opts.attributeChoices.displayValues, diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index bab40571ce5..f4746958189 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/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx index d0ce0f50479..3d8edfce092 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx @@ -118,13 +118,15 @@ function getVariantName( return attributes.reduce( (acc, attribute) => [ ...acc, - attribute.values?.find( - value => - value?.slug === - variant.attributes.find( - variantAttribute => variantAttribute.id === attribute.id - ).values[0] - )?.value?.name + attribute.values?.find(value => { + const variantAttributeValue = variant.attributes.find( + variantAttribute => variantAttribute.id === attribute.id + ); + return ( + variantAttributeValue.values?.[0] === value?.slug || + variantAttributeValue.boolean === value.value.boolean + ); + })?.value?.name ], [] ); @@ -215,7 +217,7 @@ const ProductVariantCreatorSummary: React.FC return ( attribute.values[0]) + .map(attribute => attribute.values?.[0] ?? attribute.boolean) .join(":")} >
diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx index 48fd522e884..7aabb0b1e49 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx @@ -1,4 +1,5 @@ import { Card, CardContent } from "@material-ui/core"; +import { getMeasurementUnitMessage } from "@saleor/attributes/components/AttributeDetails/utils"; import { getMultiChoices } from "@saleor/components/Attributes/utils"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; @@ -6,12 +7,23 @@ import LimitReachedAlert from "@saleor/components/LimitReachedAlert"; import MultiAutocompleteSelectField from "@saleor/components/MultiAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFragment"; +import { commonMessages } from "@saleor/intl"; import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { + getBasicAttributeValue, + getBooleanAttributeValue +} from "@saleor/products/components/ProductVariantCreatorPage/utils"; import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; import { SearchAttributeValues_attribute_choices_edges_node } from "@saleor/searches/types/SearchAttributeValues"; import { FetchMoreProps } from "@saleor/types"; +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import React from "react"; -import { defineMessages, FormattedMessage, useIntl } from "react-intl"; +import { + defineMessages, + FormattedMessage, + IntlShape, + useIntl +} from "react-intl"; import { Attribute, @@ -52,6 +64,33 @@ export function getMultiDisplayValues( })); } +const getBooleanDisplayValues = ( + intl: IntlShape, + values: Array>> +) => { + if (!values.length) { + return []; + } + + const choices = getBooleanChoices(intl); + return values.map(({ value: { boolean } }) => + choices.find(({ value }) => value === boolean) + ); +}; + +const getBooleanChoices = ( + intl: IntlShape, + values?: Array>> +) => { + const selectedValues = values?.map(({ value }) => value.boolean) ?? []; + const choices = [ + { label: intl.formatMessage(commonMessages.yes), value: true }, + { label: intl.formatMessage(commonMessages.no), value: false } + ]; + + return choices.filter(({ value }) => !selectedValues.includes(value)); +}; + export interface ProductVariantCreatorValuesProps { attributes: ProductDetails_product_productType_variantAttributes[]; attributeValues: SearchAttributeValues_attribute_choices_edges_node[]; @@ -61,7 +100,7 @@ export interface ProductVariantCreatorValuesProps { variantsLeft: number | null; onValueClick: ( attributeId: string, - value: AttributeValue + value: AttributeValue> ) => void; } @@ -78,16 +117,22 @@ const ProductVariantCreatorValues: React.FC = const intl = useIntl(); const variantsNumber = getVariantsNumber(data); - const handleValueClick = (attributeId: string, valueSlug: string) => { - const dataAttribute = data.attributes.find(getById(attributeId)); - - onValueClick(attributeId, { - slug: valueSlug, - value: - dataAttribute?.values.find(value => value.slug === valueSlug)?.value || - attributeValues.find(value => value.slug === valueSlug) - }); - }; + const handleValueClick = ( + attributeId: string, + attributeName: string, + attributeValue: string | boolean + ) => + onValueClick( + attributeId, + typeof attributeValue === "boolean" + ? getBooleanAttributeValue(attributeName, attributeValue) + : getBasicAttributeValue( + attributeId, + attributeValue, + attributeValues, + data + ) + ); return ( <> @@ -112,25 +157,60 @@ const ProductVariantCreatorValues: React.FC = } /> - - handleValueClick(attribute.id, event.target.value) - } - allowCustomValues={true} - fetchOnFocus={true} - fetchChoices={value => - fetchAttributeValues(value, attribute.id) - } - {...fetchMoreAttributeValues} - /> + {attribute.inputType === AttributeInputTypeEnum.BOOLEAN ? ( + id === attribute.id).values + )} + name={`attribute:${attribute.name}`} + label={intl.formatMessage(messages.multipleValueLabel)} + value={getMultiValues(data.attributes, attribute)} + onChange={event => + handleValueClick( + attribute.id, + attribute.name, + event.target.value + ) + } + allowCustomValues={false} + choices={getBooleanChoices( + intl, + data.attributes.find(({ id }) => id === attribute.id).values + )} + /> + ) : ( + + handleValueClick( + attribute.id, + attribute.name, + event.target.value + ) + } + endAdornment={ + attribute.unit && + getMeasurementUnitMessage( + attribute.unit, + intl.formatMessage + ) + } + fetchOnFocus={true} + allowCustomValues={true} + fetchChoices={value => + fetchAttributeValues(value, attribute.id) + } + {...fetchMoreAttributeValues} + /> + )} diff --git a/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap index 645de0c75be..1e93d64b024 100644 --- a/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap +++ b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap @@ -11,6 +11,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -24,6 +26,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -42,6 +46,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -55,6 +61,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -73,6 +81,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -86,6 +96,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -638,6 +650,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -651,6 +665,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -669,6 +685,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -682,6 +700,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -700,6 +720,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -713,6 +735,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -1265,6 +1289,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -1278,6 +1304,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -1296,6 +1324,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -1309,6 +1339,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -1327,6 +1359,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -1340,6 +1374,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -1392,6 +1428,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -1405,6 +1443,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -1423,6 +1463,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -1436,6 +1478,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -1454,6 +1498,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -1467,6 +1513,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -1970,6 +2018,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -1983,6 +2033,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -2001,6 +2053,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -2014,6 +2068,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -2032,6 +2088,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -2045,6 +2103,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -2503,6 +2563,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -2516,6 +2578,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -2534,6 +2598,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -2547,6 +2613,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -2565,6 +2633,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -2578,6 +2648,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -3081,6 +3153,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -3094,6 +3168,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -3112,6 +3188,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -3125,6 +3203,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -3143,6 +3223,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -3156,6 +3238,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", @@ -3576,6 +3660,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-1", "name": "val-1-1", @@ -3589,6 +3675,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-1-7", "name": "val-1-7", @@ -3607,6 +3695,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-2", "name": "val-2-2", @@ -3620,6 +3710,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-2-4", "name": "val-2-4", @@ -3638,6 +3730,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-1", "name": "val-4-1", @@ -3651,6 +3745,8 @@ Object { "value": Object { "__typename": "AttributeValue", "boolean": null, + "date": null, + "dateTime": null, "file": null, "id": "val-4-5", "name": "val-4-5", diff --git a/src/products/components/ProductVariantCreatorPage/createVariants.ts b/src/products/components/ProductVariantCreatorPage/createVariants.ts index 9e0ac7cbc38..a4fbaaa3526 100644 --- a/src/products/components/ProductVariantCreatorPage/createVariants.ts +++ b/src/products/components/ProductVariantCreatorPage/createVariants.ts @@ -11,7 +11,9 @@ import { interface CreateVariantAttributeValueInput { attributeId: string; attributeValueSlug: string; + attributeBooleanValue: boolean | null; } + type CreateVariantInput = CreateVariantAttributeValueInput[]; function findAttribute( @@ -97,7 +99,9 @@ function createVariant( return { attributes: attributes.map(attribute => ({ id: attribute.attributeId, - values: [attribute.attributeValueSlug] + ...(attribute.attributeBooleanValue === null + ? { values: [attribute.attributeValueSlug] } + : { boolean: attribute.attributeBooleanValue }) })), channelListings: price, sku: "", @@ -116,7 +120,8 @@ function addAttributeToVariant( ...variant, { attributeId: attribute.id, - attributeValueSlug: attributeValue.slug + attributeValueSlug: attributeValue.slug, + attributeBooleanValue: attributeValue.value?.boolean ?? null } ]); } @@ -124,11 +129,9 @@ function addVariantAttributeInput( data: CreateVariantInput[], attribute: Attribute ): CreateVariantInput[] { - const variants = data + return data .map(variant => addAttributeToVariant(attribute, variant)) .reduce((acc, variantInput) => [...acc, ...variantInput]); - - return variants; } export function createVariantFlatMatrixDimension( diff --git a/src/products/components/ProductVariantCreatorPage/fixtures.ts b/src/products/components/ProductVariantCreatorPage/fixtures.ts index b4ffc3409b1..205f3662dc2 100644 --- a/src/products/components/ProductVariantCreatorPage/fixtures.ts +++ b/src/products/components/ProductVariantCreatorPage/fixtures.ts @@ -30,7 +30,9 @@ export const attributes = [ file: null, reference: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } })) }, @@ -48,7 +50,9 @@ export const attributes = [ file: null, reference: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } })) }, @@ -66,7 +70,9 @@ export const attributes = [ file: null, reference: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } })) }, @@ -84,7 +90,9 @@ export const attributes = [ file: null, reference: null, richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } })) } diff --git a/src/products/components/ProductVariantCreatorPage/form.ts b/src/products/components/ProductVariantCreatorPage/form.ts index 5e2974a77cd..5afe0eb9cd6 100644 --- a/src/products/components/ProductVariantCreatorPage/form.ts +++ b/src/products/components/ProductVariantCreatorPage/form.ts @@ -29,7 +29,7 @@ export interface Stock { } export interface Attribute { id: string; - values: Array>; + values: Array>>; } export interface ProductVariantCreateFormData { attributes: Attribute[]; diff --git a/src/products/components/ProductVariantCreatorPage/reducer.ts b/src/products/components/ProductVariantCreatorPage/reducer.ts index 30147cb39d4..5e5a3396868 100644 --- a/src/products/components/ProductVariantCreatorPage/reducer.ts +++ b/src/products/components/ProductVariantCreatorPage/reducer.ts @@ -76,7 +76,7 @@ export interface ProductVariantCreateReducerAction { }; selectValue?: { attributeId: string; - value: AttributeValue; + value: AttributeValue>; }; type: ProductVariantCreateReducerActionType; } @@ -84,11 +84,12 @@ export interface ProductVariantCreateReducerAction { function selectValue( prevState: ProductVariantCreateFormData, attributeId: string, - value: AttributeValue + value: AttributeValue> ): ProductVariantCreateFormData { const attribute = prevState.attributes.find( attribute => attribute.id === attributeId ); + const values = toggle(value, attribute.values, (a, b) => a.slug === b.slug); const updatedAttributes = add( { diff --git a/src/products/components/ProductVariantCreatorPage/utils.ts b/src/products/components/ProductVariantCreatorPage/utils.ts index 51998d192c7..9e3fe44a2c8 100644 --- a/src/products/components/ProductVariantCreatorPage/utils.ts +++ b/src/products/components/ProductVariantCreatorPage/utils.ts @@ -1,9 +1,12 @@ +import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFragment"; +import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; import { ProductDetails_product_productType_variantAttributes, ProductDetails_product_productType_variantAttributes_choices_edges_node } from "@saleor/products/types/ProductDetails"; +import { SearchAttributeValues_attribute_choices_edges_node } from "@saleor/searches/types/SearchAttributeValues"; -import { ProductVariantCreateFormData } from "./form"; +import { AttributeValue, ProductVariantCreateFormData } from "./form"; export function getPriceAttributeValues( data: ProductVariantCreateFormData, @@ -44,3 +47,33 @@ export function getStockAttributeValues( .map(value => value.node) : []; } + +export const getBySlug = (slugToCompare: string) => (obj: { slug: string }) => + obj.slug === slugToCompare; + +export const getBooleanAttributeValue = ( + attributeName: string, + attributeValue: boolean +): AttributeValue> => ({ + slug: attributeValue.toString(), + value: { + boolean: attributeValue, + name: `${attributeName}: ${attributeValue ? "Yes" : "No"}` + } +}); + +export const getBasicAttributeValue = ( + attributeId: string, + attributeValue: string, + attributeValues: SearchAttributeValues_attribute_choices_edges_node[], + data: ProductVariantCreateFormData +): AttributeValue> => { + const dataAttribute = data.attributes.find(getById(attributeId)); + + return { + slug: attributeValue, + value: + dataAttribute?.values.find(getBySlug(attributeValue))?.value || + attributeValues.find(getBySlug(attributeValue)) + }; +}; diff --git a/src/products/components/ProductVariantPage/form.tsx b/src/products/components/ProductVariantPage/form.tsx index 69346a6c30c..285cbf05e5b 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/fixtures.ts b/src/products/fixtures.ts index ecf82a4a512..9c3d1ebbbcb 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -51,7 +51,9 @@ export const product: ( reference: null, slug: "portals", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -65,7 +67,9 @@ export const product: ( reference: null, slug: "Baht", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -80,7 +84,9 @@ export const product: ( reference: null, slug: "portals", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -116,7 +122,9 @@ export const product: ( reference: null, slug: "payment", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -130,7 +138,9 @@ export const product: ( reference: null, slug: "Auto-Loan-Account", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -144,7 +154,9 @@ export const product: ( reference: null, slug: "Garden", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -158,7 +170,9 @@ export const product: ( reference: null, slug: "override", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -173,7 +187,9 @@ export const product: ( reference: null, slug: "Auto-Loan-Account", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -369,7 +385,9 @@ export const product: ( reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -407,7 +425,9 @@ export const product: ( reference: null, slug: "black", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -421,7 +441,9 @@ export const product: ( reference: null, slug: "white", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -438,6 +460,8 @@ export const product: ( __typename: "Attribute", id: "isdugfhud", name: "Attachment", + inputType: AttributeInputTypeEnum.DROPDOWN, + unit: null, choices: { __typename: "AttributeValueCountableConnection", pageInfo: { @@ -463,7 +487,9 @@ export const product: ( reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -473,6 +499,8 @@ export const product: ( __typename: "Attribute", id: "pta18161", name: "Color", + inputType: AttributeInputTypeEnum.DROPDOWN, + unit: null, choices: { __typename: "AttributeValueCountableConnection", pageInfo: { @@ -494,7 +522,9 @@ export const product: ( reference: null, slug: "black", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -508,7 +538,9 @@ export const product: ( reference: null, slug: "white", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -968,7 +1000,9 @@ export const products = ( reference: null, slug: "pineapple", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1078,7 +1112,9 @@ export const products = ( reference: null, slug: "coconut", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1188,7 +1224,9 @@ export const products = ( reference: null, slug: "apple", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1299,7 +1337,9 @@ export const products = ( reference: null, slug: "orange", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1409,7 +1449,9 @@ export const products = ( reference: null, slug: "banana", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1519,7 +1561,9 @@ export const products = ( reference: null, slug: "bean", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1629,7 +1673,9 @@ export const products = ( reference: null, slug: "carrot", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1739,7 +1785,9 @@ export const products = ( reference: null, slug: "sprouty", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1849,7 +1897,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -1959,7 +2009,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2069,7 +2121,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2179,7 +2233,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2289,7 +2345,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2399,7 +2457,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2509,7 +2569,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2619,7 +2681,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2729,7 +2793,9 @@ export const products = ( reference: null, slug: "cotton", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -2941,7 +3007,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -2960,7 +3028,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "file-first-value", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } @@ -3222,7 +3292,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "portals", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -3236,7 +3308,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "Baht", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -3251,7 +3325,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "portals", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] }, @@ -3287,7 +3363,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "payment", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -3301,7 +3379,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "Auto-Loan-Account", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -3315,7 +3395,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "Garden", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } }, { @@ -3329,7 +3411,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "override", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } } ] @@ -3344,7 +3428,9 @@ export const variant = (placeholderImage: string): ProductVariant => ({ reference: null, slug: "Auto-Loan-Account", richText: null, - boolean: null + boolean: null, + date: null, + dateTime: null } ] } diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index 21f3306a177..c1552804588 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -32,6 +32,8 @@ export interface CreateMultipleVariantsData_product_attributes_attribute_choices reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface CreateMultipleVariantsData_product_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface CreateMultipleVariantsData_product_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface CreateMultipleVariantsData_product_attributes { @@ -104,6 +108,8 @@ export interface CreateMultipleVariantsData_product_productType_variantAttribute reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface CreateMultipleVariantsData_product_productType_variantAttributes_choices_edges { @@ -122,6 +128,8 @@ export interface CreateMultipleVariantsData_product_productType_variantAttribute __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: CreateMultipleVariantsData_product_productType_variantAttributes_choices | null; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index de08c963afb..110ac8e1221 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -32,6 +32,8 @@ export interface ProductDetails_product_attributes_attribute_choices_edges_node reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductDetails_product_attributes_attribute_choices_edges { @@ -73,6 +75,8 @@ export interface ProductDetails_product_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductDetails_product_attributes { @@ -104,6 +108,8 @@ export interface ProductDetails_product_productType_variantAttributes_choices_ed reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductDetails_product_productType_variantAttributes_choices_edges { @@ -122,6 +128,8 @@ export interface ProductDetails_product_productType_variantAttributes { __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: ProductDetails_product_productType_variantAttributes_choices | null; } diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts index 031a9b896eb..5c60a59f6be 100644 --- a/src/products/types/ProductList.ts +++ b/src/products/types/ProductList.ts @@ -92,6 +92,8 @@ export interface ProductList_products_edges_node_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductList_products_edges_node_attributes { diff --git a/src/products/types/ProductType.ts b/src/products/types/ProductType.ts index ac006f2f978..a795d0a8778 100644 --- a/src/products/types/ProductType.ts +++ b/src/products/types/ProductType.ts @@ -32,6 +32,8 @@ export interface ProductType_productType_productAttributes_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductType_productType_productAttributes_choices_edges { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 130fef1949a..c2dd45cffeb 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -39,6 +39,8 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute_choice reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductUpdate_productUpdate_product_attributes_attribute_choices_edges { @@ -80,6 +82,8 @@ export interface ProductUpdate_productUpdate_product_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductUpdate_productUpdate_product_attributes { @@ -111,6 +115,8 @@ export interface ProductUpdate_productUpdate_product_productType_variantAttribut reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductUpdate_productUpdate_product_productType_variantAttributes_choices_edges { @@ -129,6 +135,8 @@ export interface ProductUpdate_productUpdate_product_productType_variantAttribut __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: ProductUpdate_productUpdate_product_productType_variantAttributes_choices | null; } diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index a86d7b78782..3565674e972 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -51,6 +51,8 @@ export interface ProductVariantCreateData_product_productType_selectionVariantAt reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantCreateData_product_productType_selectionVariantAttributes_choices_edges { @@ -100,6 +102,8 @@ export interface ProductVariantCreateData_product_productType_nonSelectionVarian reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_choices_edges { diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index d41e07c15ff..4b2950da7c3 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -44,6 +44,8 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_attrib reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantDetails_productVariant_selectionAttributes_attribute_choices_edges { @@ -85,6 +87,8 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_values reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantDetails_productVariant_selectionAttributes { @@ -116,6 +120,8 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_att reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -157,6 +163,8 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_val reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface ProductVariantDetails_productVariant_nonSelectionAttributes { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index ebd67067da4..c68cb9f578e 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -39,6 +39,8 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_ reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_choices_edges { @@ -80,6 +82,8 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_values { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productUpdate_product_attributes { @@ -111,6 +115,8 @@ export interface SimpleProductUpdate_productUpdate_product_productType_variantAt reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes_choices_edges { @@ -129,6 +135,8 @@ export interface SimpleProductUpdate_productUpdate_product_productType_variantAt __typename: "Attribute"; id: string; name: string | null; + inputType: AttributeInputTypeEnum | null; + unit: MeasurementUnitsEnum | null; choices: SimpleProductUpdate_productUpdate_product_productType_variantAttributes_choices | null; } @@ -379,6 +387,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_choices_edges { @@ -420,6 +430,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes { @@ -451,6 +463,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -492,6 +506,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { @@ -714,6 +730,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_choices_edges { @@ -755,6 +773,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes { @@ -786,6 +806,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -827,6 +849,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes { @@ -1048,6 +1072,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_choices_edges { @@ -1089,6 +1115,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes { @@ -1120,6 +1148,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -1161,6 +1191,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes { @@ -1383,6 +1415,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_choices_edges { @@ -1424,6 +1458,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { @@ -1455,6 +1491,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -1496,6 +1534,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index 75ed9ee8223..90cb1d9dd38 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -51,6 +51,8 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_choices_edges { @@ -92,6 +94,8 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes { @@ -123,6 +127,8 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -164,6 +170,8 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes { diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 4af9bfa8248..995423060f3 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -51,6 +51,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_choices_edges { @@ -92,6 +94,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes { @@ -123,6 +127,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -164,6 +170,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { @@ -386,6 +394,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_choices_edges { @@ -427,6 +437,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { @@ -458,6 +470,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_choices_edges { @@ -499,6 +513,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 87aead510f1..c9cf67c0118 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -47,22 +47,20 @@ export interface ProductType { export function getAttributeInputFromProduct( product: ProductDetails_product ): AttributeInput[] { - return maybe( - (): AttributeInput[] => - product.attributes.map(attribute => ({ - data: { - entityType: attribute.attribute.entityType, - inputType: attribute.attribute.inputType, - isRequired: attribute.attribute.valueRequired, - selectedValues: attribute.values, - values: mergeChoicesWithValues(attribute), - unit: attribute.attribute.unit - }, - id: attribute.attribute.id, - label: attribute.attribute.name, - value: getSelectedAttributeValues(attribute) - })), - [] + return ( + product?.attributes?.map(attribute => ({ + data: { + entityType: attribute.attribute.entityType, + inputType: attribute.attribute.inputType, + isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, + values: mergeChoicesWithValues(attribute), + unit: attribute.attribute.unit + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: getSelectedAttributeValues(attribute) + })) ?? [] ); } diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index f64ef34d9ff..c0ee3b4230e 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -31,6 +31,7 @@ import { mapSlugNodeToChoice } from "@saleor/utils/maps"; import isArray from "lodash/isArray"; +import moment from "moment-timezone"; import { IFilterElement } from "../../../components/Filter"; import { @@ -45,7 +46,8 @@ import { getMinMaxQueryParam, getMultipleValueQueryParam, getSingleEnumValueQueryParam, - getSingleValueQueryParam + getSingleValueQueryParam, + GteLte } from "../../../utils/filters"; import { ProductListUrlFilters, @@ -54,7 +56,6 @@ import { ProductListUrlFiltersWithMultipleValues, ProductListUrlQueryParams } from "../../urls"; - export const PRODUCT_FILTERS_KEY = "productFilters"; export function getFilterOpts( @@ -204,25 +205,93 @@ export function getFilterOpts( }; } +const parseFilterValue = ( + params: ProductListUrlFilters, + key: string +): { + type: "boolean" | "date" | "dateTime" | "string"; + isMulti: boolean; + value: string[]; +} => { + const value = params.attributes[key]; + const isMulti = isArray(params.attributes[key]); + + const isBooleanValue = + !isMulti && ["true", "false"].includes((value as unknown) as string); + const isDateValue = (isMulti ? value : [value]).some(val => + moment(val, moment.HTML5_FMT.DATE, true).isValid() + ); + const isDateTimeValue = (isMulti ? value : [value]).some(val => + moment(val, moment.ISO_8601, true).isValid() + ); + + const data = { isMulti, value: (isMulti ? value : [value]) as string[] }; + + if (isBooleanValue) { + return { ...data, type: "boolean" }; + } else if (isDateValue) { + return { ...data, type: "date" }; + } else if (isDateTimeValue) { + return { ...data, type: "dateTime" }; + } + return { ...data, type: "string" }; +}; + +interface BaseFilterParam { + slug: string; +} +interface BooleanFilterParam extends BaseFilterParam { + boolean: boolean; +} +interface DateFilterParam extends BaseFilterParam { + date: GteLte; +} +interface DateTimeFilterParam extends BaseFilterParam { + dateTime: GteLte; +} +interface DefaultFilterParam extends BaseFilterParam { + values: string[]; +} + function getFilteredAttributeValue( params: ProductListUrlFilters -): Array<({ boolean: boolean } | { values: string[] }) & { slug: string }> { +): Array< + | BooleanFilterParam + | BaseFilterParam + | DateTimeFilterParam + | DateFilterParam + | DefaultFilterParam +> { return !!params.attributes ? Object.keys(params.attributes).map(key => { - const value = params.attributes[key]; - const isMulti = isArray(params.attributes[key]); - const isBooleanValue = - !isMulti && ["true", "false"].includes((value as unknown) as string); + const { isMulti, type, value } = parseFilterValue(params, key); + const name = { slug: key }; - return { - slug: key, - ...(isBooleanValue - ? { boolean: JSON.parse((value as unknown) as string) } - : { - // It is possible for qs to parse values not as string[] but string - values: isMulti ? value : (([value] as unknown) as string[]) + switch (type) { + case "boolean": + return { ...name, boolean: JSON.parse(value[0]) }; + + case "date": + return { + ...name, + date: getGteLteVariables({ + gte: value[0] || null, + lte: isMulti ? value[1] || null : value[0] + }) + }; + + case "dateTime": + return { + ...name, + dateTime: getGteLteVariables({ + gte: value[0] || null, + lte: isMulti ? value[1] || null : value[0] }) - }; + }; + + default: + return { ...name, values: value }; + } }) : null; } diff --git a/src/products/views/ProductUpdate/handlers/utils.ts b/src/products/views/ProductUpdate/handlers/utils.ts index d4e6d9ee5fa..f0e60392010 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/searches/types/SearchAttributeValues.ts b/src/searches/types/SearchAttributeValues.ts index 77abb76442f..ff74b875d3d 100644 --- a/src/searches/types/SearchAttributeValues.ts +++ b/src/searches/types/SearchAttributeValues.ts @@ -22,6 +22,8 @@ export interface SearchAttributeValues_attribute_choices_edges_node { reference: string | null; richText: any | null; boolean: boolean | null; + date: any | null; + dateTime: any | null; } export interface SearchAttributeValues_attribute_choices_edges { diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index a3f23da0d58..3712d236c80 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/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index b12276a3b8b..b962199f5dd 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -61,7 +61,7 @@ exports[`Storyshots Attributes / Attributes default 1`] = `
- 7 Attributes + 9 Attributes
-
+
+
+
+
+
+ Date Attribute +
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ Date Time Attribute +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
@@ -520,7 +670,7 @@ exports[`Storyshots Attributes / Attributes disabled 1`] = `
- 7 Attributes + 9 Attributes
-
+
+
+
+
+
+ Date Attribute +
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ Date Time Attribute +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
@@ -986,7 +1289,7 @@ exports[`Storyshots Attributes / Attributes selected 1`] = `
- 7 Attributes + 9 Attributes
-
+
+
+
+
+
+ Date Attribute +
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ Date Time Attribute +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
@@ -2778,7 +3232,9 @@ exports[`Storyshots Generics / Account Permission Groups Widget default 1`] = ` type="text" value="" /> -
+
-
+