From 1e140853ec0c9b11c23c996a36f00b9a8e785fc5 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 16 Dec 2020 11:53:28 +0100 Subject: [PATCH] File attributes (#884) * Update changelog with file attributes * Add file type attribute * Update attribute properties form * Update translation messages with file upload * Create generic attributes component (#832) * Create generic Attributes component * Add story for Attributes component * Remove deprecated attribute value type field from queries * Update test snapshots of attributes component * Add file upload field to atributes (#888) * Add story for Attributes component * Update test snapshots of attributes component * Create file upload field in attributes * Update upload file input data-test * Update storybook test snapshots of attributes * Add dedicated input props to file field * Run Cypress using custom API * Add missing error handling in file upload field Co-authored-by: Krzysztof Wolski * Add file attribute upload to page attributes (#894) * Support upload file attribute for pages * Update after review * Add file attribute upload to variant attributes (#892) * Support upload file attribute for variants * Update after review * Refactor attribute values errors merging * Update after review * Add file attribute upload to product attributes (#826) * Support upload file attribute for products * Update after review * Refactor attribute values errors merging * Refactor product attribute value delete handling * Fix deleting file in file upload field * Fix delete attribute values errors handling * Add link to file upload field (#898) * Update file attributes updates (#899) * Update file attributes updates * Refactor file uploads handling * Move attributes utils to attributes directory * Fix product channel listing updates * Clear file field value if file is not passed as prop * Delete attribute values before update (#908) * Delete file attributes after file update * Triggr CI * Show skeleton in file upload field during loading Co-authored-by: Krzysztof Wolski --- .github/workflows/test.yml | 13 +- CHANGELOG.md | 1 + locale/defaultMessages.json | 80 +- schema.graphql | 39 +- .../AttributeDetails/AttributeDetails.tsx | 7 + .../AttributePage/AttributePage.tsx | 22 +- .../AttributeProperties.tsx | 159 +- src/attributes/fixtures.ts | 81 +- src/attributes/types/AttributeCreate.ts | 10 +- src/attributes/types/AttributeDetails.ts | 10 +- src/attributes/types/AttributeUpdate.ts | 10 +- src/attributes/types/AttributeValueCreate.ts | 10 +- src/attributes/types/AttributeValueDelete.ts | 10 +- src/attributes/types/AttributeValueUpdate.ts | 10 +- src/attributes/utils/data.ts | 106 + src/attributes/utils/handlers.ts | 88 + .../views/AttributeCreate/AttributeCreate.tsx | 47 +- .../Attributes/Attributes.stories.tsx | 24 + .../Attributes/Attributes.tsx} | 173 +- src/components/Attributes/fixtures.ts | 109 + src/components/Attributes/index.ts | 3 + src/components/Attributes/types.ts | 5 + .../FileUploadField.stories.tsx | 68 + .../FileUploadField/FileUploadField.tsx | 147 + src/components/FileUploadField/fixtures.ts | 7 + src/components/FileUploadField/index.ts | 2 + .../MultiAutocompleteSelectField.tsx | 3 + src/files/mutations.ts | 25 + src/files/types/FileUpload.ts | 35 + src/fragments/attributes.ts | 19 +- src/fragments/errors.ts | 7 + src/fragments/file.ts | 8 + src/fragments/pages.ts | 14 +- src/fragments/products.ts | 68 +- .../types/AttributeDetailsFragment.ts | 10 +- src/fragments/types/AttributeValueFragment.ts | 21 + src/fragments/types/FileFragment.ts | 13 + src/fragments/types/PageAttributesFragment.ts | 21 + src/fragments/types/PageDetailsFragment.ts | 21 + src/fragments/types/Product.ts | 21 + src/fragments/types/ProductVariant.ts | 78 +- .../types/ProductVariantAttributesFragment.ts | 21 + ...ctedProductTypeVariantAttributeFragment.ts | 33 + ...SelectedProductVariantAttributeFragment.ts | 53 + .../types/SelectedVariantAttributeFragment.ts | 53 + src/fragments/types/UploadErrorFragment.ts | 15 + .../types/VariantAttributeFragment.ts | 33 + src/intl.ts | 4 + .../PageAttributes/PageAttributes.tsx | 247 - src/pages/components/PageAttributes/index.ts | 2 - .../PageDetailsPage/PageDetailsPage.tsx | 26 +- src/pages/components/PageDetailsPage/form.tsx | 79 +- src/pages/fixtures.ts | 48 +- src/pages/mutations.ts | 15 +- src/pages/queries.ts | 8 +- src/pages/types/PageCreate.ts | 21 + src/pages/types/PageDetails.ts | 21 + src/pages/types/PageUpdate.ts | 21 + src/pages/utils/data.ts | 29 +- src/pages/utils/handlers.test.ts | 30 +- src/pages/utils/handlers.ts | 6 +- src/pages/views/PageCreate.tsx | 32 +- src/pages/views/PageDetails.tsx | 239 +- src/productTypes/fixtures.ts | 35 + .../components/ProductAttributes/index.ts | 2 - .../ProductCreatePage/ProductCreatePage.tsx | 32 +- .../components/ProductCreatePage/form.tsx | 36 +- .../ProductUpdatePage.test.tsx | 2 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 12 +- .../components/ProductUpdatePage/form.tsx | 47 +- .../ProductVariantAttributes.tsx | 10 +- .../ProductVariantCreatePage.tsx | 62 +- .../ProductVariantCreatePage/form.tsx | 71 +- .../ProductVariantPage/ProductVariantPage.tsx | 67 +- .../components/ProductVariantPage/form.tsx | 75 +- src/products/fixtures.ts | 367 +- src/products/queries.ts | 28 +- .../types/CreateMultipleVariantsData.ts | 21 + .../types/ProductChannelListingUpdate.ts | 21 + src/products/types/ProductCreate.ts | 21 + src/products/types/ProductDetails.ts | 21 + src/products/types/ProductImageCreate.ts | 21 + src/products/types/ProductImageUpdate.ts | 21 + src/products/types/ProductList.ts | 8 + src/products/types/ProductUpdate.ts | 21 + .../ProductVariantChannelListingUpdate.ts | 78 +- .../types/ProductVariantCreateData.ts | 45 +- src/products/types/ProductVariantDetails.ts | 78 +- src/products/types/ProductVariantReorder.ts | 21 + .../types/ProductVariantSetDefault.ts | 21 + src/products/types/SimpleProductUpdate.ts | 325 +- src/products/types/VariantCreate.ts | 78 +- src/products/types/VariantImageAssign.ts | 78 +- src/products/types/VariantImageUnassign.ts | 78 +- src/products/types/VariantUpdate.ts | 154 +- src/products/utils/data.ts | 127 +- src/products/utils/handlers.test.ts | 30 +- src/products/utils/handlers.ts | 41 +- .../views/ProductCreate/ProductCreate.tsx | 7 +- src/products/views/ProductCreate/handlers.ts | 48 +- .../views/ProductUpdate/ProductUpdate.tsx | 15 +- src/products/views/ProductUpdate/handlers.ts | 88 +- src/products/views/ProductVariant.tsx | 61 +- src/products/views/ProductVariantCreate.tsx | 33 +- src/searches/types/SearchPageTypes.ts | 7 + src/searches/types/SearchProductTypes.ts | 7 + src/searches/usePageTypeSearch.ts | 6 +- src/searches/useProductTypeSearch.ts | 6 +- .../__snapshots__/Stories.test.ts.snap | 4066 ++++++++++++++--- .../attributes/AttributeValueEditDialog.tsx | 10 +- .../stories/pages/PageDetailsPage.tsx | 4 +- .../stories/products/ProductCreatePage.tsx | 6 +- .../stories/products/ProductVariantPage.tsx | 2 +- src/theme.ts | 5 + src/types/globalTypes.ts | 25 +- 115 files changed, 7335 insertions(+), 1857 deletions(-) create mode 100644 src/attributes/utils/data.ts create mode 100644 src/attributes/utils/handlers.ts create mode 100644 src/components/Attributes/Attributes.stories.tsx rename src/{products/components/ProductAttributes/ProductAttributes.tsx => components/Attributes/Attributes.tsx} (59%) create mode 100644 src/components/Attributes/fixtures.ts create mode 100644 src/components/Attributes/index.ts create mode 100644 src/components/Attributes/types.ts create mode 100644 src/components/FileUploadField/FileUploadField.stories.tsx create mode 100644 src/components/FileUploadField/FileUploadField.tsx create mode 100644 src/components/FileUploadField/fixtures.ts create mode 100644 src/components/FileUploadField/index.ts create mode 100644 src/files/mutations.ts create mode 100644 src/files/types/FileUpload.ts create mode 100644 src/fragments/file.ts create mode 100644 src/fragments/types/AttributeValueFragment.ts create mode 100644 src/fragments/types/FileFragment.ts create mode 100644 src/fragments/types/SelectedProductTypeVariantAttributeFragment.ts create mode 100644 src/fragments/types/SelectedProductVariantAttributeFragment.ts create mode 100644 src/fragments/types/SelectedVariantAttributeFragment.ts create mode 100644 src/fragments/types/UploadErrorFragment.ts create mode 100644 src/fragments/types/VariantAttributeFragment.ts delete mode 100644 src/pages/components/PageAttributes/PageAttributes.tsx delete mode 100644 src/pages/components/PageAttributes/index.ts delete mode 100644 src/products/components/ProductAttributes/index.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5bbd49dc6..f8dc415a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,10 +74,21 @@ jobs: steps: - name: Checkout uses: actions/checkout@v1 + + - name: Get custom API_URI + id: api_uri + # Search for API_URI in PR description + env: + pull_request_body: ${{ github.event.pull_request.body }} + prefix: API_URI= + pattern: (http|https)://[a-zA-Z0-9.-]+/graphql/? + run: | + echo "::set-output name=custom_api_uri::$(echo $pull_request_body | grep -Eo "$prefix$pattern" | sed s/$prefix// | head -n 1)" + - name: Cypress run uses: cypress-io/github-action@v2 env: - API_URI: ${{ secrets.API_URI }} + API_URI: ${{ steps.api_uri.outputs.custom_api_uri || secrets.API_URI }} APP_MOUNT_URI: ${{ secrets.APP_MOUNT_URI }} CYPRESS_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }} CYPRESS_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 664aead0b..41eaaebbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable, unreleased changes to this project will be documented in this file. - New Miscellaneous and Product refunds - #870 by @orzechdev - Add zip code exclusion - #877 by @dominik-zeglen - Update quantity column in Inventory part of Product Variant view - #904 by @dominik-zeglen +- Add file attributes - #884 by @orzechdev # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index babedb700..22b05e4f7 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -741,6 +741,10 @@ "context": "product attribute type", "string": "Dropdown" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_1376373679": { + "context": "file attribute type", + "string": "File" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_2592224946": { "context": "check to require attribute to have value", "string": "Value Required" @@ -1275,6 +1279,10 @@ "src_dot_channels_dot_views_dot_ChannelsList_dot_3499322424": { "string": "Channel deleted" }, + "src_dot_chooseFile": { + "context": "button", + "string": "Choose file" + }, "src_dot_clear": { "context": "button", "string": "Clear" @@ -1532,6 +1540,22 @@ "src_dot_components_dot_AttributeUnassignDialog_dot_2037985699": { "string": "Are you sure you want to unassign {attributeName} from {itemTypeName}?" }, + "src_dot_components_dot_Attributes_dot_attributesNumber": { + "context": "number of attributes", + "string": "{number} Attributes" + }, + "src_dot_components_dot_Attributes_dot_header": { + "context": "attributes, section header", + "string": "Attributes" + }, + "src_dot_components_dot_Attributes_dot_multipleValueLable": { + "context": "attribute values", + "string": "Values" + }, + "src_dot_components_dot_Attributes_dot_valueLabel": { + "context": "attribute value", + "string": "Value" + }, "src_dot_components_dot_AutocompleteSelectMenu_dot_2332404293": { "string": "No results" }, @@ -4060,22 +4084,6 @@ "context": "pages section name", "string": "Pages" }, - "src_dot_pages_dot_components_dot_PageAttributes_dot_1071548120": { - "context": "number of page attributes", - "string": "{number} Attributes" - }, - "src_dot_pages_dot_components_dot_PageAttributes_dot_1148029984": { - "context": "attribute value", - "string": "Value" - }, - "src_dot_pages_dot_components_dot_PageAttributes_dot_1207761269": { - "context": "attribute values", - "string": "Values" - }, - "src_dot_pages_dot_components_dot_PageAttributes_dot_4153345096": { - "context": "page attributes, section header", - "string": "Attributes" - }, "src_dot_pages_dot_components_dot_PageDetailsPage_dot_1068617485": { "context": "page header", "string": "Create Page" @@ -4580,22 +4588,6 @@ "context": "products section name", "string": "Products" }, - "src_dot_products_dot_components_dot_ProductAttributes_dot_1071548120": { - "context": "number of product attributes", - "string": "{number} Attributes" - }, - "src_dot_products_dot_components_dot_ProductAttributes_dot_1148029984": { - "context": "attribute value", - "string": "Value" - }, - "src_dot_products_dot_components_dot_ProductAttributes_dot_1207761269": { - "context": "attribute values", - "string": "Values" - }, - "src_dot_products_dot_components_dot_ProductAttributes_dot_4153345096": { - "context": "product attributes, section header", - "string": "Attributes" - }, "src_dot_products_dot_components_dot_ProductCategoryAndCollectionsForm_dot_1755013298": { "string": "Category" }, @@ -4901,14 +4893,22 @@ "context": "product label", "string": "Published" }, - "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_2853608829": { - "context": "button", - "string": "Save variant" + "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_attributesHeader": { + "context": "attributes, section header", + "string": "Variant Attributes" }, - "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_3726089650": { + "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_attributesSelectionHeader": { + "context": "attributes, section header", + "string": "Variant Selection Attributes" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_deleteVariant": { "context": "button", "string": "Delete Variant" }, + "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_saveVariant": { + "context": "button", + "string": "Save variant" + }, "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1134347598": { "context": "variant price, header", "string": "Price" @@ -5049,6 +5049,14 @@ "context": "variant name", "string": "New Variant" }, + "src_dot_products_dot_components_dot_ProductVariantPage_dot_nonSelectionAttributes": { + "context": "attributes, section header", + "string": "Variant Attributes" + }, + "src_dot_products_dot_components_dot_ProductVariantPage_dot_selectionAttributesHeader": { + "context": "attributes, section header", + "string": "Variant Selection Attributes" + }, "src_dot_products_dot_components_dot_ProductVariantPrice_dot_1099355007": { "context": "product pricing, section header", "string": "Pricing" diff --git a/schema.graphql b/schema.graphql index 8dc7eeaaf..a0496e583 100644 --- a/schema.graphql +++ b/schema.graphql @@ -489,6 +489,7 @@ input AttributeInput { enum AttributeInputTypeEnum { DROPDOWN MULTISELECT + FILE } type AttributeReorderValues { @@ -565,6 +566,7 @@ type AttributeValue implements Node { type: AttributeValueType @deprecated(reason: "Use the `inputType` field to determine the type of attribute's value. This field will be removed after 2020-07-31.") translation(languageCode: LanguageCodeEnum!): AttributeValueTranslation inputType: AttributeInputTypeEnum + file: File } type AttributeValueBulkDelete { @@ -593,7 +595,9 @@ type AttributeValueDelete { input AttributeValueInput { id: ID - values: [String]! + values: [String] + file: String + contentType: String } type AttributeValueTranslatableContent implements Node { @@ -658,6 +662,11 @@ enum AuthorizationKeyType { GOOGLE_OAUTH2 } +input BulkAttributeValueInput { + id: ID + values: [String]! +} + type BulkProductError { field: String message: String @@ -1959,6 +1968,11 @@ enum ExportScope { FILTER } +type File { + url: String! + contentType: String +} + enum FileTypesEnum { CSV XLSX @@ -1966,7 +1980,7 @@ enum FileTypesEnum { type FileUpload { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - uploadedFile: UploadedFile + uploadedFile: File uploadErrors: [UploadError!]! } @@ -4085,7 +4099,7 @@ type ProductType implements Node & ObjectWithMetadata { products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "Use the top-level `products` query with the `productTypes` filter.") taxRate: TaxRateType taxType: TaxType - variantAttributes: [Attribute] + variantAttributes(variantSelection: VariantAttributeScope): [Attribute] productAttributes: [Attribute] availableAttributes(filter: AttributeFilterInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection } @@ -4192,7 +4206,7 @@ type ProductVariant implements Node & ObjectWithMetadata { channelListings: [ProductVariantChannelListing!] pricing: VariantPricingInfo isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") - attributes: [SelectedAttribute!]! + attributes(variantSelection: VariantAttributeScope): [SelectedAttribute!]! costPrice: Money margin: Int quantityOrdered: Int @@ -4212,7 +4226,7 @@ type ProductVariantBulkCreate { } input ProductVariantBulkCreateInput { - attributes: [AttributeValueInput]! + attributes: [BulkAttributeValueInput]! sku: String! trackInventory: Boolean weight: WeightScalar @@ -4645,6 +4659,8 @@ type ShippingMethod implements Node & ObjectWithMetadata { name: String! minimumOrderWeight: Weight maximumOrderWeight: Weight + maximumDeliveryDays: Int + minimumDeliveryDays: Int privateMetadata: [MetadataItem]! metadata: [MetadataItem]! type: ShippingMethodTypeEnum @@ -4741,6 +4757,8 @@ input ShippingPriceInput { name: String minimumOrderWeight: WeightScalar maximumOrderWeight: WeightScalar + maximumDeliveryDays: Int + minimumDeliveryDays: Int type: ShippingMethodTypeEnum shippingZone: ID } @@ -5262,11 +5280,6 @@ enum UploadErrorCode { GRAPHQL_ERROR } -type UploadedFile { - url: String! - contentType: String! -} - type User implements Node & ObjectWithMetadata { id: ID! lastLogin: DateTime @@ -5359,6 +5372,12 @@ type VAT { reducedRates: [ReducedRate]! } +enum VariantAttributeScope { + ALL + VARIANT_SELECTION + NOT_VARIANT_SELECTION +} + type VariantImageAssign { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index 73432d70b..8ad3451d0 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -47,6 +47,13 @@ const AttributeDetails: React.FC = ({ description: "product attribute type" }), value: AttributeInputTypeEnum.MULTISELECT + }, + { + label: intl.formatMessage({ + defaultMessage: "File", + description: "file attribute type" + }), + value: AttributeInputTypeEnum.FILE } ]; diff --git a/src/attributes/components/AttributePage/AttributePage.tsx b/src/attributes/components/AttributePage/AttributePage.tsx index f6ca5f6de..5ccc2dbb2 100644 --- a/src/attributes/components/AttributePage/AttributePage.tsx +++ b/src/attributes/components/AttributePage/AttributePage.tsx @@ -172,15 +172,19 @@ const AttributePage: React.FC = ({ errors={errors} onChange={change} /> - - + {data.inputType !== AttributeInputTypeEnum.FILE && ( + <> + + + + )} diff --git a/src/attributes/components/AttributeProperties/AttributeProperties.tsx b/src/attributes/components/AttributeProperties/AttributeProperties.tsx index 21d830203..5277418ea 100644 --- a/src/attributes/components/AttributeProperties/AttributeProperties.tsx +++ b/src/attributes/components/AttributeProperties/AttributeProperties.tsx @@ -10,7 +10,10 @@ import FormSpacer from "@saleor/components/FormSpacer"; import Hr from "@saleor/components/Hr"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { commonMessages } from "@saleor/intl"; -import { AttributeTypeEnum } from "@saleor/types/globalTypes"; +import { + AttributeInputTypeEnum, + AttributeTypeEnum +} from "@saleor/types/globalTypes"; import { getFormErrors } from "@saleor/utils/errors"; import getAttributeErrorMessage from "@saleor/utils/errors/attribute"; import React from "react"; @@ -77,39 +80,43 @@ const AttributeProperties: React.FC = ({ />
- {data.type === AttributeTypeEnum.PRODUCT_TYPE && ( - <> - - - - )} - {data.filterableInStorefront && + {data.inputType !== AttributeInputTypeEnum.FILE && data.type === AttributeTypeEnum.PRODUCT_TYPE && ( - + + {data.filterableInStorefront && ( + <> + + + )} - name={"storefrontSearchPosition" as keyof AttributePageFormData} - label={intl.formatMessage({ - defaultMessage: "Position in faceted navigation", - description: "attribute position in storefront filters" - })} - value={data.storefrontSearchPosition} - onChange={onChange} - /> + )} = ({ onChange={onChange} disabled={disabled} /> - - - - -
- - + {data.inputType !== AttributeInputTypeEnum.FILE && ( + <> + + - - - - - } - checked={data.filterableInDashboard} - onChange={onChange} - disabled={disabled} - /> - - - - - - - - } - checked={data.availableInGrid} - onChange={onChange} - disabled={disabled} - /> + +
+ + + + + + + + } + checked={data.filterableInDashboard} + onChange={onChange} + disabled={disabled} + /> + + + + + + + + } + checked={data.availableInGrid} + onChange={onChange} + disabled={disabled} + /> + + )} ); diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts index 94a6218e9..160d45919 100644 --- a/src/attributes/fixtures.ts +++ b/src/attributes/fixtures.ts @@ -2,8 +2,7 @@ import { AttributeDetailsFragment } from "@saleor/fragments/types/AttributeDetai import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; import { AttributeInputTypeEnum, - AttributeTypeEnum, - AttributeValueType + AttributeTypeEnum } from "@saleor/types/globalTypes"; import { AttributeList_attributes_edges_node } from "./types/AttributeList"; @@ -31,17 +30,17 @@ export const attribute: AttributeDetailsFragment = { values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", name: "John Doe", - slug: "john-doe", - type: AttributeValueType.STRING + slug: "john-doe" }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", name: "Milionare Pirate", - slug: "milionare-pirate", - type: AttributeValueType.STRING + slug: "milionare-pirate" } ], visibleInStorefront: true @@ -61,20 +60,20 @@ export const attributes: Array, + existingAttribute: + | PageDetails_page_attributes + | SelectedVariantAttributeFragment +) => { + if (existingAttribute.attribute.inputType !== AttributeInputTypeEnum.FILE) { + return false; + } + if (existingAttribute.values.length === 0) { + return false; + } + + const modifiedAttribute = attributesWithNewFileValue.find( + dataAttribute => dataAttribute.id === existingAttribute.attribute.id + ); + + return !!modifiedAttribute; +}; + +export const mergeFileUploadErrors = ( + uploadFilesResult: Array> +): UploadErrorFragment[] => + uploadFilesResult.reduce((errors, uploadFileResult) => { + const uploadErrors = uploadFileResult?.data?.fileUpload?.uploadErrors; + if (uploadErrors) { + return [...errors, ...uploadErrors]; + } + return errors; + }, []); + +export const mergeAttributeValueDeleteErrors = ( + deleteAttributeValuesResult: Array> +): AttributeErrorFragment[] => + deleteAttributeValuesResult.reduce((errors, deleteValueResult) => { + const deleteErrors = deleteValueResult?.data?.attributeValueDelete?.errors; + if (deleteErrors) { + return [...errors, ...deleteErrors]; + } + return errors; + }, []); + +export const getFileValuesToUploadFromAttributes = ( + attributesWithNewFileValue: FormsetData +) => attributesWithNewFileValue.filter(fileAttribute => !!fileAttribute.value); + +export const getFileValuesRemovedFromAttributes = ( + attributesWithNewFileValue: FormsetData +) => attributesWithNewFileValue.filter(attribute => !attribute.value); + +export const getAttributesOfRemovedFiles = ( + fileAttributesRemoved: FormsetData +) => + fileAttributesRemoved.map(attribute => ({ + file: undefined, + id: attribute.id, + values: [] + })); + +export const getAttributesOfUploadedFiles = ( + fileValuesToUpload: FormsetData, + uploadFilesResult: Array> +) => + uploadFilesResult.map((uploadFileResult, index) => { + const attribute = fileValuesToUpload[index]; + + return { + file: uploadFileResult.data.fileUpload.uploadedFile.url, + id: attribute.id, + values: [] + }; + }); + +export const getAttributesAfterFileAttributesUpdate = ( + attributesWithNewFileValue: FormsetData, + uploadFilesResult: Array> +): AttributeValueInput[] => { + const removedFileValues = getFileValuesRemovedFromAttributes( + attributesWithNewFileValue + ); + const fileValuesToUpload = getFileValuesToUploadFromAttributes( + attributesWithNewFileValue + ); + + const removedFileAttributes = getAttributesOfRemovedFiles(removedFileValues); + const uploadedFileAttributes = getAttributesOfUploadedFiles( + fileValuesToUpload, + uploadFilesResult + ); + + return uploadedFileAttributes.concat(removedFileAttributes); +}; diff --git a/src/attributes/utils/handlers.ts b/src/attributes/utils/handlers.ts new file mode 100644 index 000000000..496fb4758 --- /dev/null +++ b/src/attributes/utils/handlers.ts @@ -0,0 +1,88 @@ +import { AttributeInput } from "@saleor/components/Attributes"; +import { + FileUpload, + FileUploadVariables +} from "@saleor/files/types/FileUpload"; +import { FormsetData } from "@saleor/hooks/useFormset"; +import { PageDetails_page_attributes } from "@saleor/pages/types/PageDetails"; +import { + AttributeInputTypeEnum, + AttributeValueInput +} from "@saleor/types/globalTypes"; +import { MutationFetchResult } from "react-apollo"; + +import { + AttributeValueDelete, + AttributeValueDeleteVariables +} from "../types/AttributeValueDelete"; +import { getFileValuesToUploadFromAttributes, isFileValueUnused } from "./data"; + +interface AttributesArgs { + attributes: AttributeInput[]; + updatedFileAttributes: AttributeValueInput[]; +} + +export const prepareAttributesInput = ({ + attributes, + updatedFileAttributes +}: AttributesArgs): AttributeValueInput[] => + attributes.map(attribute => { + if (attribute.data.inputType === AttributeInputTypeEnum.FILE) { + const updatedFileAttribute = updatedFileAttributes.find( + attributeWithNewFile => attribute.id === attributeWithNewFile.id + ); + if (updatedFileAttribute) { + return { + file: updatedFileAttribute.file, + id: updatedFileAttribute.id + }; + } + return { + file: + attribute.data.selectedValues && + attribute.data.selectedValues[0]?.file?.url, + id: attribute.id + }; + } + return { + id: attribute.id, + values: attribute.value[0] === "" ? [] : attribute.value + }; + }); + +export const handleUploadMultipleFiles = async ( + attributesWithNewFileValue: FormsetData, + uploadFile: ( + variables: FileUploadVariables + ) => Promise> +) => + Promise.all( + getFileValuesToUploadFromAttributes(attributesWithNewFileValue).map( + fileAttribute => + uploadFile({ + file: fileAttribute.value + }) + ) + ); + +export const handleDeleteMultipleAttributeValues = async ( + attributesWithNewFileValue: FormsetData, + attributes: PageDetails_page_attributes[], + deleteAttributeValue: ( + variables: AttributeValueDeleteVariables + ) => Promise> +) => + Promise.all( + attributes.map(existingAttribute => { + const fileValueUnused = isFileValueUnused( + attributesWithNewFileValue, + existingAttribute + ); + + if (fileValueUnused) { + return deleteAttributeValue({ + id: existingAttribute.values[0].id + }); + } + }) + ); diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx index 7b5634640..7fe4e48f0 100644 --- a/src/attributes/views/AttributeCreate/AttributeCreate.tsx +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -3,7 +3,10 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { getStringOrPlaceholder } from "@saleor/misc"; import { ReorderEvent } from "@saleor/types"; -import { AttributeErrorCode } from "@saleor/types/globalTypes"; +import { + AttributeErrorCode, + AttributeInputTypeEnum +} from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { @@ -54,6 +57,33 @@ function areValuesEqual( return a.name === b.name; } +function getSimpleAttributeData( + data: AttributePageFormData, + values: AttributeValueEditDialogFormData[] +) { + return { + ...data, + metadata: undefined, + privateMetadata: undefined, + storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10), + values: values.map(value => ({ + name: value.name + })) + }; +} + +function getFileAttributeData( + data: AttributePageFormData, + values: AttributeValueEditDialogFormData[] +) { + return { + ...getSimpleAttributeData(data, values), + availableInGrid: undefined, + filterableInDashboard: undefined, + filterableInStorefront: undefined + }; +} + const AttributeDetails: React.FC = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); @@ -115,15 +145,10 @@ const AttributeDetails: React.FC = ({ params }) => { setValues(move(values[oldIndex], values, areValuesEqual, newIndex)); const handleCreate = async (data: AttributePageFormData) => { - const input = { - ...data, - metadata: undefined, - privateMetadata: undefined, - storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 0), - values: values.map(value => ({ - name: value.name - })) - }; + const input = + data.inputType === AttributeInputTypeEnum.FILE + ? getFileAttributeData(data, values) + : getSimpleAttributeData(data, values); const result = await attributeCreate({ variables: { @@ -163,10 +188,10 @@ const AttributeDetails: React.FC = ({ params }) => { saveButtonBarState={attributeCreateOpts.status} values={values.map((value, valueIndex) => ({ __typename: "AttributeValue" as "AttributeValue", + file: null, id: valueIndex.toString(), slug: slugify(value.name).toLowerCase(), sortOrder: valueIndex, - type: null, value: null, ...value }))} diff --git a/src/components/Attributes/Attributes.stories.tsx b/src/components/Attributes/Attributes.stories.tsx new file mode 100644 index 000000000..b2f222856 --- /dev/null +++ b/src/components/Attributes/Attributes.stories.tsx @@ -0,0 +1,24 @@ +import Attributes, { AttributesProps } from "@saleor/components/Attributes"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import Decorator from "../../storybook/Decorator"; +import { ATTRIBUTES, ATTRIBUTES_SELECTED } from "./fixtures"; + +const props: AttributesProps = { + attributes: ATTRIBUTES, + disabled: false, + errors: [], + loading: false, + onChange: () => undefined, + onFileChange: () => undefined, + onMultiChange: () => undefined +}; + +storiesOf("Attributes / Attributes", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("selected", () => ( + + )) + .add("disabled", () => ); diff --git a/src/products/components/ProductAttributes/ProductAttributes.tsx b/src/components/Attributes/Attributes.tsx similarity index 59% rename from src/products/components/ProductAttributes/ProductAttributes.tsx rename to src/components/Attributes/Attributes.tsx index ab972a8ae..9364ec721 100644 --- a/src/products/components/ProductAttributes/ProductAttributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -13,30 +13,45 @@ import MultiAutocompleteSelectField, { import SingleAutocompleteSelectField, { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { AttributeValueFragment } from "@saleor/fragments/types/AttributeValueFragment"; +import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; -import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { getProductErrorMessage } from "@saleor/utils/errors"; +import getPageErrorMessage from "@saleor/utils/errors/page"; import classNames from "classnames"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { + defineMessages, + FormattedMessage, + IntlShape, + useIntl +} from "react-intl"; -export interface ProductAttributeInputData { +import FileUploadField, { FileChoiceType } from "../FileUploadField"; +import { VariantAttributeScope } from "./types"; + +export interface AttributeInputData { inputType: AttributeInputTypeEnum; + variantAttributeScope?: VariantAttributeScope; isRequired: boolean; - values: ProductDetails_product_attributes_attribute_values[]; + values: AttributeValueFragment[]; + selectedValues?: AttributeValueFragment[]; } -export type ProductAttributeInput = FormsetAtomicData< - ProductAttributeInputData, - string[] ->; -export interface ProductAttributesProps { - attributes: ProductAttributeInput[]; +export type AttributeInput = FormsetAtomicData; +export type AttributeFileInput = FormsetAtomicData; +export interface AttributesProps { + attributes: AttributeInput[]; disabled: boolean; - errors: ProductErrorWithAttributesFragment[]; + loading: boolean; + errors: Array< + ProductErrorWithAttributesFragment | PageErrorWithAttributesFragment + >; + title?: React.ReactNode; onChange: FormsetChange; onMultiChange: FormsetChange; + onFileChange?: FormsetChange; // TODO: temporairy optional, should be changed to required, after all pages implement it } const useStyles = makeStyles( @@ -78,15 +93,26 @@ const useStyles = makeStyles( display: "flex", flex: 1 }, + fileField: { + float: "right" + }, rotate: { transform: "rotate(180deg)" + }, + uploadFileButton: { + float: "right" + }, + uploadFileContent: { + color: theme.palette.primary.main, + float: "right", + fontSize: "1rem" } }), - { name: "ProductAttributes" } + { name: "Attributes" } ); function getMultiChoices( - values: ProductDetails_product_attributes_attribute_values[] + values: AttributeValueFragment[] ): MultiAutocompleteChoiceType[] { return values.map(value => ({ label: value.name, @@ -95,7 +121,7 @@ function getMultiChoices( } function getMultiDisplayValue( - attribute: ProductAttributeInput + attribute: AttributeInput ): MultiAutocompleteChoiceType[] { return attribute.value.map(attributeValue => { const definedAttributeValue = attribute.data.values.find( @@ -116,7 +142,7 @@ function getMultiDisplayValue( } function getSingleChoices( - values: ProductDetails_product_attributes_attribute_values[] + values: AttributeValueFragment[] ): SingleAutocompleteChoiceType[] { return values.map(value => ({ label: value.name, @@ -124,12 +150,67 @@ function getSingleChoices( })); } -const ProductAttributes: React.FC = ({ +function getFileChoice(attribute: AttributeInput): FileChoiceType { + const attributeValue = attribute.value[0]; + + const definedAttributeValue = attribute.data.values.find( + definedValue => definedValue.slug === attributeValue + ); + + if (definedAttributeValue) { + return { + file: definedAttributeValue.file, + label: definedAttributeValue.name, + value: definedAttributeValue.slug + }; + } + + return { + label: attributeValue, + value: attributeValue + }; +} + +const messages = defineMessages({ + attributesNumber: { + defaultMessage: "{number} Attributes", + description: "number of attributes" + }, + header: { + defaultMessage: "Attributes", + description: "attributes, section header" + }, + multipleValueLable: { + defaultMessage: "Values", + description: "attribute values" + }, + valueLabel: { + defaultMessage: "Value", + description: "attribute value" + } +}); + +function getErrorMessage( + err: ProductErrorWithAttributesFragment | PageErrorWithAttributesFragment, + intl: IntlShape +): string { + switch (err?.__typename) { + case "ProductError": + return getProductErrorMessage(err, intl); + case "PageError": + return getPageErrorMessage(err, intl); + } +} + +const Attributes: React.FC = ({ attributes, disabled, + loading, errors, + title, onChange, - onMultiChange + onMultiChange, + onFileChange }) => { const intl = useIntl(); const classes = useStyles({}); @@ -138,19 +219,13 @@ const ProductAttributes: React.FC = ({ return ( - +
= ({ = ({
{attribute.label}
-
+
{attribute.data.inputType === - AttributeInputTypeEnum.DROPDOWN ? ( + AttributeInputTypeEnum.FILE ? ( + + onFileChange(attribute.id, file) + } + onFileDelete={() => + onFileChange(attribute.id, undefined) + } + error={!!error} + helperText={getErrorMessage(error, intl)} + inputProps={{ + name: `attribute:${attribute.label}` + }} + /> + ) : attribute.data.inputType === + AttributeInputTypeEnum.DROPDOWN ? ( = ({ } emptyOption={!attribute.data.isRequired} error={!!error} - helperText={getProductErrorMessage(error, intl)} + helperText={getErrorMessage(error, intl)} name={`attribute:${attribute.label}`} - label={intl.formatMessage({ - defaultMessage: "Value", - description: "attribute value" - })} + label={intl.formatMessage(messages.valueLabel)} value={attribute.value[0]} onChange={event => onChange(attribute.id, event.target.value) @@ -218,12 +309,12 @@ const ProductAttributes: React.FC = ({ @@ -243,5 +334,5 @@ const ProductAttributes: React.FC = ({ ); }; -ProductAttributes.displayName = "ProductAttributes"; -export default ProductAttributes; +Attributes.displayName = "Attributes"; +export default Attributes; diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts new file mode 100644 index 000000000..6a62b561c --- /dev/null +++ b/src/components/Attributes/fixtures.ts @@ -0,0 +1,109 @@ +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; + +import { AttributeInput } from "./Attributes"; + +const DROPDOWN_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.DROPDOWN, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "fdinugiffgffd", + name: "Dropdown First Value", + slug: "dropdown-first-value" + }, + { + __typename: "AttributeValue", + file: null, + id: "fdhfdhdihidff", + name: "Dropdown Second Value", + slug: "dropdown-second-value" + } + ] + }, + id: "ifudbgidfsb", + label: "Dropdown Attribute", + value: [] +}; + +const MULTISELECT_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.MULTISELECT, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "terteretregtt", + name: "Multiselect First Value", + slug: "multiselect-first-value" + }, + { + __typename: "AttributeValue", + file: null, + id: "tyueyryetopwr", + name: "Multiselect Second Value", + slug: "multiselect-second-value" + }, + { + __typename: "AttributeValue", + file: null, + id: "truiwrtweirqd", + name: "Multiselect Third Value", + slug: "multiselect-third-value" + } + ] + }, + id: "idffuidhffl", + label: "Multiselect Attribute", + value: [] +}; + +const FILE_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.FILE, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + }, + id: "ifudbgidfsb", + label: "File Attribute", + value: [] +}; + +export const ATTRIBUTES: AttributeInput[] = [ + DROPDOWN_ATTRIBUTE, + MULTISELECT_ATTRIBUTE, + FILE_ATTRIBUTE +]; + +export const ATTRIBUTES_SELECTED: AttributeInput[] = [ + { + ...DROPDOWN_ATTRIBUTE, + value: [DROPDOWN_ATTRIBUTE.data.values[0].slug] + }, + { + ...MULTISELECT_ATTRIBUTE, + value: [ + MULTISELECT_ATTRIBUTE.data.values[0].slug, + MULTISELECT_ATTRIBUTE.data.values[1].slug + ] + }, + { + ...FILE_ATTRIBUTE, + value: [FILE_ATTRIBUTE.data.values[0].slug] + } +]; diff --git a/src/components/Attributes/index.ts b/src/components/Attributes/index.ts new file mode 100644 index 000000000..baed03372 --- /dev/null +++ b/src/components/Attributes/index.ts @@ -0,0 +1,3 @@ +export { default } from "./Attributes"; +export * from "./Attributes"; +export * from "./types"; diff --git a/src/components/Attributes/types.ts b/src/components/Attributes/types.ts new file mode 100644 index 000000000..48d1ef720 --- /dev/null +++ b/src/components/Attributes/types.ts @@ -0,0 +1,5 @@ +export enum VariantAttributeScope { + ALL = "ALL", + VARIANT_SELECTION = "VARIANT_SELECTION", + NOT_VARIANT_SELECTION = "NOT_VARIANT_SELECTION" +} diff --git a/src/components/FileUploadField/FileUploadField.stories.tsx b/src/components/FileUploadField/FileUploadField.stories.tsx new file mode 100644 index 000000000..1bc53f01f --- /dev/null +++ b/src/components/FileUploadField/FileUploadField.stories.tsx @@ -0,0 +1,68 @@ +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React, { useState } from "react"; + +import FileUploadField, { FileUploadFieldProps } from "./FileUploadField"; +import * as fixtures from "./fixtures"; + +const props: FileUploadFieldProps = { + disabled: false, + file: { label: undefined, value: undefined }, + inputProps: { + name: "country", + placeholder: "Select country" + }, + loading: false, + onFileDelete: () => undefined, + onFileUpload: () => undefined +}; + +const InteractiveStory: React.FC = () => { + const [file, setFile] = useState(); + + return ( + setFile(file)} + onFileDelete={() => setFile(null)} + /> + ); +}; + +storiesOf("Generics / File upload field", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ) + .add("with ready to upload file", () => ( + + )) + .add("with uploaded file", () => ( + + )) + .add("with error", () => ( + + )) + .add("interactive", () => ); diff --git a/src/components/FileUploadField/FileUploadField.tsx b/src/components/FileUploadField/FileUploadField.tsx new file mode 100644 index 000000000..b807cf400 --- /dev/null +++ b/src/components/FileUploadField/FileUploadField.tsx @@ -0,0 +1,147 @@ +import { makeStyles } from "@material-ui/core"; +import Button from "@material-ui/core/Button"; +import IconButton from "@material-ui/core/IconButton"; +import Typography from "@material-ui/core/Typography"; +import DeleteIcon from "@material-ui/icons/Delete"; +import { FileFragment } from "@saleor/fragments/types/FileFragment"; +import { commonMessages } from "@saleor/intl"; +import React from "react"; +import { useIntl } from "react-intl"; + +import Skeleton from "../Skeleton"; + +export interface FileChoiceType { + label: string; + value: string; + file?: FileFragment; +} + +export interface FileUploadFieldProps { + inputProps?: React.DetailedHTMLProps< + React.InputHTMLAttributes, + HTMLInputElement + >; + className?: string; + disabled: boolean; + loading: boolean; + file: FileChoiceType; + error?: boolean; + helperText?: string; + onFileUpload: (file: File) => void; + onFileDelete: () => void; +} + +const useStyles = makeStyles( + theme => ({ + errorText: { + color: theme.palette.error.light + }, + fileField: { + display: "none" + }, + fileUrl: { + color: theme.palette.primary.main, + textDecoration: "none" + }, + uploadFileContent: { + alignItems: "center", + color: theme.palette.primary.main, + display: "flex", + fontSize: "1rem" + }, + uploadFileName: { + minWidth: "6rem" + } + }), + { name: "FileUploadField" } +); + +const FileUploadField: React.FC = props => { + const { + loading, + disabled, + file, + className, + error, + helperText, + onFileUpload, + onFileDelete, + inputProps + } = props; + const classes = useStyles({}); + const intl = useIntl(); + + const fileInputAnchor = React.createRef(); + const clickFileInput = () => fileInputAnchor.current.click(); + + const handleFileDelete = () => { + fileInputAnchor.current.value = ""; + onFileDelete(); + }; + + React.useEffect(() => { + if (!file.value) { + fileInputAnchor.current.value = ""; + } + }, [file]); + + return ( + <> +
+ {file.label ? ( +
+
+ {loading ? ( + + ) : ( + + {file.label} + + )} +
+ + + +
+ ) : ( +
+ +
+ )} + {error && ( + + {helperText} + + )} +
+ onFileUpload(event.target.files[0])} + type="file" + data-test="upload-file-input" + ref={fileInputAnchor} + {...inputProps} + /> + + ); +}; +FileUploadField.displayName = "FileUploadField"; +export default FileUploadField; diff --git a/src/components/FileUploadField/fixtures.ts b/src/components/FileUploadField/fixtures.ts new file mode 100644 index 000000000..893f6a673 --- /dev/null +++ b/src/components/FileUploadField/fixtures.ts @@ -0,0 +1,7 @@ +import { FileFragment } from "@saleor/fragments/types/FileFragment"; + +export const UPLOADED_FILE: FileFragment = { + __typename: "File", + contentType: "image/png", + url: "some_url_to_image.png" +}; diff --git a/src/components/FileUploadField/index.ts b/src/components/FileUploadField/index.ts new file mode 100644 index 000000000..09c046f00 --- /dev/null +++ b/src/components/FileUploadField/index.ts @@ -0,0 +1,2 @@ +export { default } from "./FileUploadField"; +export * from "./FileUploadField"; diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx index e0f20ff1a..2cee26776 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx @@ -83,6 +83,7 @@ export interface MultiAutocompleteSelectFieldProps placeholder?: string; helperText?: string; label?: string; + disabled?: boolean; fetchChoices?: (value: string) => void; onChange: (event: React.ChangeEvent) => void; } @@ -105,6 +106,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC {isOpen && (!!inputValue || !!choices.length) && ( (fileUploadMutation); diff --git a/src/files/types/FileUpload.ts b/src/files/types/FileUpload.ts new file mode 100644 index 000000000..5662ee64c --- /dev/null +++ b/src/files/types/FileUpload.ts @@ -0,0 +1,35 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { UploadErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: FileUpload +// ==================================================== + +export interface FileUpload_fileUpload_uploadedFile { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface FileUpload_fileUpload_uploadErrors { + __typename: "UploadError"; + code: UploadErrorCode; + field: string | null; +} + +export interface FileUpload_fileUpload { + __typename: "FileUpload"; + uploadedFile: FileUpload_fileUpload_uploadedFile | null; + uploadErrors: FileUpload_fileUpload_uploadErrors[]; +} + +export interface FileUpload { + fileUpload: FileUpload_fileUpload | null; +} + +export interface FileUploadVariables { + file: any; +} diff --git a/src/fragments/attributes.ts b/src/fragments/attributes.ts index ae509736f..76e6c2504 100644 --- a/src/fragments/attributes.ts +++ b/src/fragments/attributes.ts @@ -1,7 +1,20 @@ import gql from "graphql-tag"; +import { fileFragment } from "./file"; import { metadataFragment } from "./metadata"; +export const attributeValueFragment = gql` + ${fileFragment} + fragment AttributeValueFragment on AttributeValue { + id + name + slug + file { + ...FileFragment + } + } +`; + export const attributeFragment = gql` fragment AttributeFragment on Attribute { id @@ -17,6 +30,7 @@ export const attributeFragment = gql` export const attributeDetailsFragment = gql` ${attributeFragment} ${metadataFragment} + ${attributeValueFragment} fragment AttributeDetailsFragment on Attribute { ...AttributeFragment ...MetadataFragment @@ -25,10 +39,7 @@ export const attributeDetailsFragment = gql` storefrontSearchPosition valueRequired values { - id - name - slug - type + ...AttributeValueFragment } } `; diff --git a/src/fragments/errors.ts b/src/fragments/errors.ts index 15989c329..b979a768b 100644 --- a/src/fragments/errors.ts +++ b/src/fragments/errors.ts @@ -206,3 +206,10 @@ export const collectionsErrorFragment = gql` field } `; + +export const uploadErrorFragment = gql` + fragment UploadErrorFragment on UploadError { + code + field + } +`; diff --git a/src/fragments/file.ts b/src/fragments/file.ts new file mode 100644 index 000000000..ed3382f5e --- /dev/null +++ b/src/fragments/file.ts @@ -0,0 +1,8 @@ +import gql from "graphql-tag"; + +export const fileFragment = gql` + fragment FileFragment on File { + url + contentType + } +`; diff --git a/src/fragments/pages.ts b/src/fragments/pages.ts index 8ee33939a..f757f0459 100644 --- a/src/fragments/pages.ts +++ b/src/fragments/pages.ts @@ -1,5 +1,6 @@ import gql from "graphql-tag"; +import { attributeValueFragment } from "./attributes"; import { metadataFragment } from "./metadata"; export const pageFragment = gql` @@ -12,6 +13,7 @@ export const pageFragment = gql` `; export const pageAttributesFragment = gql` + ${attributeValueFragment} fragment PageAttributesFragment on Page { attributes { attribute { @@ -21,15 +23,11 @@ export const pageAttributesFragment = gql` inputType valueRequired values { - id - name - slug + ...AttributeValueFragment } } values { - id - name - slug + ...AttributeValueFragment } } pageType { @@ -41,9 +39,7 @@ export const pageAttributesFragment = gql` inputType valueRequired values { - id - name - slug + ...AttributeValueFragment } } } diff --git a/src/fragments/products.ts b/src/fragments/products.ts index 7e2c4ff73..a1fce235f 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -1,5 +1,6 @@ import gql from "graphql-tag"; +import { attributeValueFragment } from "./attributes"; import { metadataFragment } from "./metadata"; import { taxTypeFragment } from "./taxes"; import { weightFragment } from "./weight"; @@ -113,6 +114,7 @@ export const productFragment = gql` export const productVariantAttributesFragment = gql` ${priceRangeFragment} + ${attributeValueFragment} fragment ProductVariantAttributesFragment on Product { id attributes { @@ -123,26 +125,20 @@ export const productVariantAttributesFragment = gql` inputType valueRequired values { - id - name - slug + ...AttributeValueFragment } } values { - id - name - slug + ...AttributeValueFragment } } productType { id - variantAttributes { + variantAttributes(variantSelection: VARIANT_SELECTION) { id name values { - id - name - slug + ...AttributeValueFragment } } } @@ -228,7 +224,35 @@ export const productFragmentDetails = gql` } `; +export const variantAttributeFragment = gql` + ${attributeValueFragment} + fragment VariantAttributeFragment on Attribute { + id + name + slug + inputType + valueRequired + values { + ...AttributeValueFragment + } + } +`; + +export const selectedVariantAttributeFragment = gql` + ${attributeValueFragment} + ${variantAttributeFragment} + fragment SelectedVariantAttributeFragment on SelectedAttribute { + attribute { + ...VariantAttributeFragment + } + values { + ...AttributeValueFragment + } + } +`; + export const fragmentVariant = gql` + ${selectedVariantAttributeFragment} ${priceRangeFragment} ${fragmentProductImage} ${stockFragment} @@ -238,23 +262,13 @@ export const fragmentVariant = gql` fragment ProductVariant on ProductVariant { id ...MetadataFragment - attributes { - attribute { - id - name - slug - valueRequired - values { - id - name - slug - } - } - values { - id - name - slug - } + selectionAttributes: attributes(variantSelection: VARIANT_SELECTION) { + ...SelectedVariantAttributeFragment + } + nonSelectionAttributes: attributes( + variantSelection: NOT_VARIANT_SELECTION + ) { + ...SelectedVariantAttributeFragment } images { id diff --git a/src/fragments/types/AttributeDetailsFragment.ts b/src/fragments/types/AttributeDetailsFragment.ts index 665617d63..de9a0d902 100644 --- a/src/fragments/types/AttributeDetailsFragment.ts +++ b/src/fragments/types/AttributeDetailsFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: AttributeDetailsFragment @@ -20,12 +20,18 @@ export interface AttributeDetailsFragment_privateMetadata { value: string; } +export interface AttributeDetailsFragment_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface AttributeDetailsFragment_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; - type: AttributeValueType | null; + file: AttributeDetailsFragment_values_file | null; } export interface AttributeDetailsFragment { diff --git a/src/fragments/types/AttributeValueFragment.ts b/src/fragments/types/AttributeValueFragment.ts new file mode 100644 index 000000000..e66f4af4d --- /dev/null +++ b/src/fragments/types/AttributeValueFragment.ts @@ -0,0 +1,21 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: AttributeValueFragment +// ==================================================== + +export interface AttributeValueFragment_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface AttributeValueFragment { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: AttributeValueFragment_file | null; +} diff --git a/src/fragments/types/FileFragment.ts b/src/fragments/types/FileFragment.ts new file mode 100644 index 000000000..573804bb7 --- /dev/null +++ b/src/fragments/types/FileFragment.ts @@ -0,0 +1,13 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: FileFragment +// ==================================================== + +export interface FileFragment { + __typename: "File"; + url: string; + contentType: string | null; +} diff --git a/src/fragments/types/PageAttributesFragment.ts b/src/fragments/types/PageAttributesFragment.ts index 244eb87db..aac6ad487 100644 --- a/src/fragments/types/PageAttributesFragment.ts +++ b/src/fragments/types/PageAttributesFragment.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL fragment: PageAttributesFragment // ==================================================== +export interface PageAttributesFragment_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageAttributesFragment_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageAttributesFragment_attributes_attribute_values_file | null; } export interface PageAttributesFragment_attributes_attribute { @@ -25,11 +32,18 @@ export interface PageAttributesFragment_attributes_attribute { values: (PageAttributesFragment_attributes_attribute_values | null)[] | null; } +export interface PageAttributesFragment_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageAttributesFragment_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageAttributesFragment_attributes_values_file | null; } export interface PageAttributesFragment_attributes { @@ -38,11 +52,18 @@ export interface PageAttributesFragment_attributes { values: (PageAttributesFragment_attributes_values | null)[]; } +export interface PageAttributesFragment_pageType_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageAttributesFragment_pageType_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageAttributesFragment_pageType_attributes_values_file | null; } export interface PageAttributesFragment_pageType_attributes { diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index fc69b5871..c150461ce 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL fragment: PageDetailsFragment // ==================================================== +export interface PageDetailsFragment_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetailsFragment_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetailsFragment_attributes_attribute_values_file | null; } export interface PageDetailsFragment_attributes_attribute { @@ -25,11 +32,18 @@ export interface PageDetailsFragment_attributes_attribute { values: (PageDetailsFragment_attributes_attribute_values | null)[] | null; } +export interface PageDetailsFragment_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetailsFragment_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetailsFragment_attributes_values_file | null; } export interface PageDetailsFragment_attributes { @@ -38,11 +52,18 @@ export interface PageDetailsFragment_attributes { values: (PageDetailsFragment_attributes_values | null)[]; } +export interface PageDetailsFragment_pageType_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetailsFragment_pageType_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetailsFragment_pageType_attributes_values_file | null; } export interface PageDetailsFragment_pageType_attributes { diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index 897ebdd24..890c4c77a 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTyp // GraphQL fragment: Product // ==================================================== +export interface Product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface Product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: Product_attributes_attribute_values_file | null; } export interface Product_attributes_attribute { @@ -25,11 +32,18 @@ export interface Product_attributes_attribute { values: (Product_attributes_attribute_values | null)[] | null; } +export interface Product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface Product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: Product_attributes_values_file | null; } export interface Product_attributes { @@ -38,11 +52,18 @@ export interface Product_attributes { values: (Product_attributes_values | null)[]; } +export interface Product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface Product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: Product_productType_variantAttributes_values_file | null; } export interface Product_productType_variantAttributes { diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index 6f1ecdba1..0139aa986 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductVariant @@ -20,33 +20,92 @@ export interface ProductVariant_privateMetadata { value: string; } -export interface ProductVariant_attributes_attribute_values { +export interface ProductVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariant_selectionAttributes_attribute_values_file | null; } -export interface ProductVariant_attributes_attribute { +export interface ProductVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (ProductVariant_attributes_attribute_values | null)[] | null; + values: (ProductVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface ProductVariant_attributes_values { +export interface ProductVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariant_selectionAttributes_values_file | null; } -export interface ProductVariant_attributes { +export interface ProductVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: ProductVariant_attributes_attribute; - values: (ProductVariant_attributes_values | null)[]; + attribute: ProductVariant_selectionAttributes_attribute; + values: (ProductVariant_selectionAttributes_values | null)[]; +} + +export interface ProductVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface ProductVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface ProductVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariant_nonSelectionAttributes_values_file | null; +} + +export interface ProductVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: ProductVariant_nonSelectionAttributes_attribute; + values: (ProductVariant_nonSelectionAttributes_values | null)[]; } export interface ProductVariant_images { @@ -195,7 +254,8 @@ export interface ProductVariant { id: string; metadata: (ProductVariant_metadata | null)[]; privateMetadata: (ProductVariant_privateMetadata | null)[]; - attributes: ProductVariant_attributes[]; + selectionAttributes: ProductVariant_selectionAttributes[]; + nonSelectionAttributes: ProductVariant_nonSelectionAttributes[]; images: (ProductVariant_images | null)[] | null; name: string; product: ProductVariant_product; diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index d05a0ce61..0345c4cf8 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL fragment: ProductVariantAttributesFragment // ==================================================== +export interface ProductVariantAttributesFragment_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantAttributesFragment_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantAttributesFragment_attributes_attribute_values_file | null; } export interface ProductVariantAttributesFragment_attributes_attribute { @@ -25,11 +32,18 @@ export interface ProductVariantAttributesFragment_attributes_attribute { values: (ProductVariantAttributesFragment_attributes_attribute_values | null)[] | null; } +export interface ProductVariantAttributesFragment_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantAttributesFragment_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantAttributesFragment_attributes_values_file | null; } export interface ProductVariantAttributesFragment_attributes { @@ -38,11 +52,18 @@ export interface ProductVariantAttributesFragment_attributes { values: (ProductVariantAttributesFragment_attributes_values | null)[]; } +export interface ProductVariantAttributesFragment_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantAttributesFragment_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantAttributesFragment_productType_variantAttributes_values_file | null; } export interface ProductVariantAttributesFragment_productType_variantAttributes { diff --git a/src/fragments/types/SelectedProductTypeVariantAttributeFragment.ts b/src/fragments/types/SelectedProductTypeVariantAttributeFragment.ts new file mode 100644 index 000000000..bafacf0ff --- /dev/null +++ b/src/fragments/types/SelectedProductTypeVariantAttributeFragment.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: SelectedProductTypeVariantAttributeFragment +// ==================================================== + +export interface SelectedProductTypeVariantAttributeFragment_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SelectedProductTypeVariantAttributeFragment_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SelectedProductTypeVariantAttributeFragment_values_file | null; +} + +export interface SelectedProductTypeVariantAttributeFragment { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SelectedProductTypeVariantAttributeFragment_values | null)[] | null; +} diff --git a/src/fragments/types/SelectedProductVariantAttributeFragment.ts b/src/fragments/types/SelectedProductVariantAttributeFragment.ts new file mode 100644 index 000000000..022e8e277 --- /dev/null +++ b/src/fragments/types/SelectedProductVariantAttributeFragment.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: SelectedProductVariantAttributeFragment +// ==================================================== + +export interface SelectedProductVariantAttributeFragment_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SelectedProductVariantAttributeFragment_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SelectedProductVariantAttributeFragment_attribute_values_file | null; +} + +export interface SelectedProductVariantAttributeFragment_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SelectedProductVariantAttributeFragment_attribute_values | null)[] | null; +} + +export interface SelectedProductVariantAttributeFragment_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SelectedProductVariantAttributeFragment_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SelectedProductVariantAttributeFragment_values_file | null; +} + +export interface SelectedProductVariantAttributeFragment { + __typename: "SelectedAttribute"; + attribute: SelectedProductVariantAttributeFragment_attribute; + values: (SelectedProductVariantAttributeFragment_values | null)[]; +} diff --git a/src/fragments/types/SelectedVariantAttributeFragment.ts b/src/fragments/types/SelectedVariantAttributeFragment.ts new file mode 100644 index 000000000..5b4ea1696 --- /dev/null +++ b/src/fragments/types/SelectedVariantAttributeFragment.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: SelectedVariantAttributeFragment +// ==================================================== + +export interface SelectedVariantAttributeFragment_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SelectedVariantAttributeFragment_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SelectedVariantAttributeFragment_attribute_values_file | null; +} + +export interface SelectedVariantAttributeFragment_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SelectedVariantAttributeFragment_attribute_values | null)[] | null; +} + +export interface SelectedVariantAttributeFragment_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SelectedVariantAttributeFragment_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SelectedVariantAttributeFragment_values_file | null; +} + +export interface SelectedVariantAttributeFragment { + __typename: "SelectedAttribute"; + attribute: SelectedVariantAttributeFragment_attribute; + values: (SelectedVariantAttributeFragment_values | null)[]; +} diff --git a/src/fragments/types/UploadErrorFragment.ts b/src/fragments/types/UploadErrorFragment.ts new file mode 100644 index 000000000..7b0578a66 --- /dev/null +++ b/src/fragments/types/UploadErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { UploadErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: UploadErrorFragment +// ==================================================== + +export interface UploadErrorFragment { + __typename: "UploadError"; + code: UploadErrorCode; + field: string | null; +} diff --git a/src/fragments/types/VariantAttributeFragment.ts b/src/fragments/types/VariantAttributeFragment.ts new file mode 100644 index 000000000..e455ede8e --- /dev/null +++ b/src/fragments/types/VariantAttributeFragment.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: VariantAttributeFragment +// ==================================================== + +export interface VariantAttributeFragment_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantAttributeFragment_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantAttributeFragment_values_file | null; +} + +export interface VariantAttributeFragment { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantAttributeFragment_values | null)[] | null; +} diff --git a/src/intl.ts b/src/intl.ts index 301bf3b7a..6f43317fa 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -7,6 +7,10 @@ export const commonMessages = defineMessages({ catalog: { defaultMessage: "Catalog" }, + chooseFile: { + defaultMessage: "Choose file", + description: "button" + }, customApps: { defaultMessage: "Local Apps" }, diff --git a/src/pages/components/PageAttributes/PageAttributes.tsx b/src/pages/components/PageAttributes/PageAttributes.tsx deleted file mode 100644 index 361ce3baa..000000000 --- a/src/pages/components/PageAttributes/PageAttributes.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import IconButton from "@material-ui/core/IconButton"; -import makeStyles from "@material-ui/core/styles/makeStyles"; -import Typography from "@material-ui/core/Typography"; -import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; -import CardTitle from "@saleor/components/CardTitle"; -import Grid from "@saleor/components/Grid"; -import Hr from "@saleor/components/Hr"; -import MultiAutocompleteSelectField, { - MultiAutocompleteChoiceType -} from "@saleor/components/MultiAutocompleteSelectField"; -import SingleAutocompleteSelectField, { - SingleAutocompleteChoiceType -} from "@saleor/components/SingleAutocompleteSelectField"; -import { PageDetailsFragment_pageType_attributes_values } from "@saleor/fragments/types/PageDetailsFragment"; -import { PageErrorWithAttributesFragment } from "@saleor/fragments/types/PageErrorWithAttributesFragment"; -import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; -import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; -import getPageErrorMessage from "@saleor/utils/errors/page"; -import classNames from "classnames"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -export interface PageAttributeInputData { - inputType: AttributeInputTypeEnum; - isRequired: boolean; - values: PageDetailsFragment_pageType_attributes_values[]; -} -export type PageAttributeInput = FormsetAtomicData< - PageAttributeInputData, - string[] ->; -export interface PageAttributesProps { - attributes: PageAttributeInput[]; - disabled: boolean; - errors: PageErrorWithAttributesFragment[]; - onChange: FormsetChange; - onMultiChange: FormsetChange; -} - -const useStyles = makeStyles( - theme => ({ - attributeSection: { - "&:last-of-type": { - paddingBottom: 0 - }, - padding: theme.spacing(2, 0) - }, - attributeSectionLabel: { - alignItems: "center", - display: "flex" - }, - card: { - overflow: "visible" - }, - cardContent: { - "&:last-child": { - paddingBottom: theme.spacing(1) - }, - paddingTop: theme.spacing(1) - }, - expansionBar: { - display: "flex" - }, - expansionBarButton: { - marginBottom: theme.spacing(1) - }, - expansionBarButtonIcon: { - transition: theme.transitions.duration.short + "ms" - }, - expansionBarLabel: { - color: theme.palette.text.disabled, - fontSize: 14 - }, - expansionBarLabelContainer: { - alignItems: "center", - display: "flex", - flex: 1 - }, - rotate: { - transform: "rotate(180deg)" - } - }), - { name: "PageAttributes" } -); - -function getMultiChoices( - values: PageDetailsFragment_pageType_attributes_values[] -): MultiAutocompleteChoiceType[] { - return values.map(value => ({ - label: value.name, - value: value.slug - })); -} - -function getMultiDisplayValue( - attribute: PageAttributeInput -): MultiAutocompleteChoiceType[] { - return attribute.value.map(attributeValue => { - const definedAttributeValue = attribute.data.values.find( - definedValue => definedValue.slug === attributeValue - ); - if (!!definedAttributeValue) { - return { - label: definedAttributeValue.name, - value: definedAttributeValue.slug - }; - } - - return { - label: attributeValue, - value: attributeValue - }; - }); -} - -function getSingleChoices( - values: PageDetailsFragment_pageType_attributes_values[] -): SingleAutocompleteChoiceType[] { - return values.map(value => ({ - label: value.name, - value: value.slug - })); -} - -const PageAttributes: React.FC = ({ - attributes, - disabled, - errors, - onChange, - onMultiChange -}) => { - const intl = useIntl(); - const classes = useStyles({}); - const [expanded, setExpansionStatus] = React.useState(true); - const toggleExpansion = () => setExpansionStatus(!expanded); - - return ( - - - -
-
- - - -
- - - -
- {expanded && attributes.length > 0 && ( - <> -
- {attributes.map((attribute, attributeIndex) => { - const error = errors.find(err => - err.attributes?.includes(attribute.id) - ); - - return ( - - {attributeIndex > 0 &&
} - -
- {attribute.label} -
-
- {attribute.data.inputType === - AttributeInputTypeEnum.DROPDOWN ? ( - value.slug === attribute.value[0] - )?.name || - attribute.value[0] || - "" - } - emptyOption={!attribute.data.isRequired} - error={!!error} - helperText={getPageErrorMessage(error, intl)} - name={`attribute:${attribute.label}`} - label={intl.formatMessage({ - defaultMessage: "Value", - description: "attribute value" - })} - value={attribute.value[0]} - onChange={event => - onChange(attribute.id, event.target.value) - } - allowCustomValues={!attribute.data.isRequired} - /> - ) : ( - - onMultiChange(attribute.id, event.target.value) - } - allowCustomValues={!attribute.data.isRequired} - /> - )} -
-
-
- ); - })} - - )} -
-
- ); -}; -PageAttributes.displayName = "PageAttributes"; -export default PageAttributes; diff --git a/src/pages/components/PageAttributes/index.ts b/src/pages/components/PageAttributes/index.ts deleted file mode 100644 index 0b41faf1b..000000000 --- a/src/pages/components/PageAttributes/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./PageAttributes"; -export * from "./PageAttributes"; diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index 93cac39a5..809ba0037 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -1,4 +1,5 @@ import AppHeader from "@saleor/components/AppHeader"; +import Attributes from "@saleor/components/Attributes"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; @@ -18,13 +19,12 @@ import React from "react"; import { useIntl } from "react-intl"; import { PageDetails_page } from "../../types/PageDetails"; -import PageAttributes from "../PageAttributes"; import PageInfo from "../PageInfo"; import PageOrganizeContent from "../PageOrganizeContent"; import PageForm, { PageData } from "./form"; export interface PageDetailsPageProps { - disabled: boolean; + loading: boolean; errors: PageErrorWithAttributesFragment[]; page: PageDetails_page; pageTypes?: SearchPageTypes_search_edges_node[]; @@ -38,7 +38,7 @@ export interface PageDetailsPageProps { } const PageDetailsPage: React.FC = ({ - disabled, + loading, errors, page, pageTypes, @@ -75,7 +75,7 @@ const PageDetailsPage: React.FC = ({
= ({ errors={errors} allowEmptySlug={!pageExists} description={data.seoDescription} - disabled={disabled} + disabled={loading} descriptionPlaceholder={""} // TODO: Cast description to string and trim it onChange={change} slug={data.slug} @@ -99,12 +99,14 @@ const PageDetailsPage: React.FC = ({ /> {data.attributes.length > 0 && ( - )} @@ -115,7 +117,7 @@ const PageDetailsPage: React.FC = ({ = ({ = ({
; content: OutputData; } @@ -43,8 +55,9 @@ interface PageUpdateHandlers { changeMetadata: FormChange; changeContent: RichTextEditorChange; selectPageType: FormChange; - changeAttribute: FormsetChange; - changeAttributeMulti: FormsetChange; + selectAttribute: FormsetChange; + selectAttributeMulti: FormsetChange; + selectAttributeFile: FormsetChange; } export interface UsePageUpdateFormResult { change: FormChange; @@ -72,16 +85,8 @@ function usePageForm( const pageExists = page !== null; - const attributesFromPage = React.useMemo( - () => getAttributeInputFromPage(page), - [page] - ); - - const { - change: changeAttributeData, - data: attributes, - set: setAttributeData - } = useFormset(attributesFromPage || []); + const attributes = useFormset(getAttributeInputFromPage(page)); + const attributesWithNewFileValue = useFormset([]); const form = useForm({ isPublished: page?.isPublished, @@ -118,31 +123,46 @@ function usePageForm( const changeMetadata = makeMetadataChangeHandler(handleChange); const selectPageType = createPageTypeSelectHandler( handleChange, - setAttributeData, + attributes.set, setPageType, pageTypes ); - const changeAttribute = createAttributeChangeHandler( - changeAttributeData, + const handleAttributeChange = createAttributeChangeHandler( + attributes.change, triggerChange ); - const changeAttributeMulti = createAttributeMultiChangeHandler( - changeAttributeData, - attributes, + const handleAttributeMultiChange = createAttributeMultiChangeHandler( + attributes.change, + attributes.data, triggerChange ); + const handleAttributeFileChange = createAttributeFileChangeHandler( + attributes.change, + attributesWithNewFileValue.data, + attributesWithNewFileValue.add, + attributesWithNewFileValue.change, + triggerChange + ); + + useEffect(() => { + attributesWithNewFileValue.set([]); + }, [page]); // Need to make it function to always have content.current up to date const getData = (): PageData => ({ ...form.data, - attributes, + attributes: getAttributesDisplayData( + attributes.data, + attributesWithNewFileValue.data + ), content: content.current }); - const getSubmitData = (): PageData => ({ + const getSubmitData = (): PageSubmitData => ({ ...getData(), ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified), - ...getPublicationData(form.data) + ...getPublicationData(form.data), + attributesWithNewFileValue: attributesWithNewFileValue.data }); const submit = () => @@ -154,10 +174,11 @@ function usePageForm( change: handleChange, data: getData(), handlers: { - changeAttribute, - changeAttributeMulti, changeContent, changeMetadata, + selectAttribute: handleAttributeChange, + selectAttributeFile: handleAttributeFileChange, + selectAttributeMulti: handleAttributeMultiChange, selectPageType }, hasChanged: changed, diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index bc010a95a..afb7bccab 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -52,19 +52,22 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODc=", name: "Suzanne Ellison", slug: "suzanne-ellison", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6ODg=", name: "Dennis Perkins", slug: "dennis-perkins", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "Attribute" @@ -74,7 +77,8 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "SelectedAttribute" @@ -91,25 +95,29 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTE=", name: "Support", slug: "support", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTI=", name: "Medical", slug: "medical", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTM=", name: "General", slug: "general", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "Attribute" @@ -119,7 +127,8 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "SelectedAttribute" @@ -150,19 +159,22 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6ODc=", name: "Suzanne Ellison", slug: "suzanne-ellison", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6ODg=", name: "Dennis Perkins", slug: "dennis-perkins", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6ODk=", name: "Dylan Lamb", slug: "dylan-lamb", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "Attribute" @@ -177,25 +189,29 @@ export const page: PageDetails_page = { id: "QXR0cmlidXRlVmFsdWU6OTA=", name: "Security", slug: "security", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTE=", name: "Support", slug: "support", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTI=", name: "Medical", slug: "medical", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null }, { id: "QXR0cmlidXRlVmFsdWU6OTM=", name: "General", slug: "general", - __typename: "AttributeValue" + __typename: "AttributeValue", + file: null } ], __typename: "Attribute" diff --git a/src/pages/mutations.ts b/src/pages/mutations.ts index 1b07d5025..7289fd292 100644 --- a/src/pages/mutations.ts +++ b/src/pages/mutations.ts @@ -3,6 +3,7 @@ import { pageErrorWithAttributesFragment } from "@saleor/fragments/errors"; import { pageDetailsFragment } from "@saleor/fragments/pages"; +import makeMutation from "@saleor/hooks/makeMutation"; import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; @@ -51,9 +52,10 @@ const pageUpdate = gql` } } `; -export const TypedPageUpdate = TypedMutation( - pageUpdate -); +export const usePageUpdateMutation = makeMutation< + PageUpdate, + PageUpdateVariables +>(pageUpdate); const pageRemove = gql` ${pageErrorFragment} @@ -65,9 +67,10 @@ const pageRemove = gql` } } `; -export const TypedPageRemove = TypedMutation( - pageRemove -); +export const usePageRemoveMutation = makeMutation< + PageRemove, + PageRemoveVariables +>(pageRemove); const pageBulkPublish = gql` mutation PageBulkPublish($ids: [ID]!, $isPublished: Boolean!) { diff --git a/src/pages/queries.ts b/src/pages/queries.ts index 8dd8618c6..b46a26384 100644 --- a/src/pages/queries.ts +++ b/src/pages/queries.ts @@ -2,7 +2,6 @@ import { pageDetailsFragment, pageFragment } from "@saleor/fragments/pages"; import makeQuery from "@saleor/hooks/makeQuery"; import gql from "graphql-tag"; -import { TypedQuery } from "../queries"; import { PageDetails, PageDetailsVariables } from "./types/PageDetails"; import { PageList, PageListVariables } from "./types/PageList"; @@ -48,7 +47,6 @@ const pageDetails = gql` } } `; -export const TypedPageDetailsQuery = TypedQuery< - PageDetails, - PageDetailsVariables ->(pageDetails); +export const usePageDetailsQuery = makeQuery( + pageDetails +); diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index 320ba975b..02ed59e41 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -16,11 +16,18 @@ export interface PageCreate_pageCreate_errors { message: string | null; } +export interface PageCreate_pageCreate_page_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageCreate_pageCreate_page_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageCreate_pageCreate_page_attributes_attribute_values_file | null; } export interface PageCreate_pageCreate_page_attributes_attribute { @@ -33,11 +40,18 @@ export interface PageCreate_pageCreate_page_attributes_attribute { values: (PageCreate_pageCreate_page_attributes_attribute_values | null)[] | null; } +export interface PageCreate_pageCreate_page_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageCreate_pageCreate_page_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageCreate_pageCreate_page_attributes_values_file | null; } export interface PageCreate_pageCreate_page_attributes { @@ -46,11 +60,18 @@ export interface PageCreate_pageCreate_page_attributes { values: (PageCreate_pageCreate_page_attributes_values | null)[]; } +export interface PageCreate_pageCreate_page_pageType_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageCreate_pageCreate_page_pageType_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageCreate_pageCreate_page_pageType_attributes_values_file | null; } export interface PageCreate_pageCreate_page_pageType_attributes { diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index c9fe07e7c..813cd49a4 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL query operation: PageDetails // ==================================================== +export interface PageDetails_page_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetails_page_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetails_page_attributes_attribute_values_file | null; } export interface PageDetails_page_attributes_attribute { @@ -25,11 +32,18 @@ export interface PageDetails_page_attributes_attribute { values: (PageDetails_page_attributes_attribute_values | null)[] | null; } +export interface PageDetails_page_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetails_page_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetails_page_attributes_values_file | null; } export interface PageDetails_page_attributes { @@ -38,11 +52,18 @@ export interface PageDetails_page_attributes { values: (PageDetails_page_attributes_values | null)[]; } +export interface PageDetails_page_pageType_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageDetails_page_pageType_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageDetails_page_pageType_attributes_values_file | null; } export interface PageDetails_page_pageType_attributes { diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index 08b87fc55..f3c1d5c2a 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -15,11 +15,18 @@ export interface PageUpdate_pageUpdate_errors { attributes: string[] | null; } +export interface PageUpdate_pageUpdate_page_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageUpdate_pageUpdate_page_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageUpdate_pageUpdate_page_attributes_attribute_values_file | null; } export interface PageUpdate_pageUpdate_page_attributes_attribute { @@ -32,11 +39,18 @@ export interface PageUpdate_pageUpdate_page_attributes_attribute { values: (PageUpdate_pageUpdate_page_attributes_attribute_values | null)[] | null; } +export interface PageUpdate_pageUpdate_page_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageUpdate_pageUpdate_page_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageUpdate_pageUpdate_page_attributes_values_file | null; } export interface PageUpdate_pageUpdate_page_attributes { @@ -45,11 +59,18 @@ export interface PageUpdate_pageUpdate_page_attributes { values: (PageUpdate_pageUpdate_page_attributes_values | null)[]; } +export interface PageUpdate_pageUpdate_page_pageType_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface PageUpdate_pageUpdate_page_pageType_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: PageUpdate_pageUpdate_page_pageType_attributes_values_file | null; } export interface PageUpdate_pageUpdate_page_pageType_attributes { diff --git a/src/pages/utils/data.ts b/src/pages/utils/data.ts index f426eb86d..e6dffacf4 100644 --- a/src/pages/utils/data.ts +++ b/src/pages/utils/data.ts @@ -1,4 +1,6 @@ -import { PageAttributeInput } from "../components/PageAttributes"; +import { AttributeInput } from "@saleor/components/Attributes"; +import { FormsetData } from "@saleor/hooks/useFormset"; + import { PageDetails_page, PageDetails_page_pageType @@ -6,11 +8,12 @@ import { export function getAttributeInputFromPage( page: PageDetails_page -): PageAttributeInput[] { +): AttributeInput[] { return page?.attributes.map(attribute => ({ data: { inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, values: attribute.attribute.values }, id: attribute.attribute.id, @@ -21,7 +24,7 @@ export function getAttributeInputFromPage( export function getAttributeInputFromPageType( pageType: PageDetails_page_pageType -): PageAttributeInput[] { +): AttributeInput[] { return pageType?.attributes.map(attribute => ({ data: { inputType: attribute.inputType, @@ -33,3 +36,23 @@ export function getAttributeInputFromPageType( value: [] })); } + +export const getAttributesDisplayData = ( + attributes: AttributeInput[], + attributesWithNewFileValue: FormsetData +) => + attributes.map(attribute => { + const attributeWithNewFileValue = attributesWithNewFileValue.find( + attributeWithNewFile => attribute.id === attributeWithNewFile.id + ); + + if (attributeWithNewFileValue) { + return { + ...attribute, + value: attributeWithNewFileValue?.value?.name + ? [attributeWithNewFileValue.value.name] + : [] + }; + } + return attribute; + }); diff --git a/src/pages/utils/handlers.test.ts b/src/pages/utils/handlers.test.ts index faaa40cbc..8b79dea92 100644 --- a/src/pages/utils/handlers.test.ts +++ b/src/pages/utils/handlers.test.ts @@ -1,10 +1,10 @@ +import { AttributeInputData } from "@saleor/components/Attributes"; import { FormsetData } from "@saleor/hooks/useFormset"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; -import { PageAttributeInputData } from "../components/PageAttributes"; import { createAttributeMultiChangeHandler } from "./handlers"; -const attributes: FormsetData = [ +const attributes: FormsetData = [ { data: { inputType: AttributeInputTypeEnum.DROPDOWN, @@ -12,6 +12,7 @@ const attributes: FormsetData = [ values: [ { __typename: "AttributeValue", + file: null, id: "attrv-1", name: "Attribute 1 Value 1", slug: "attr-1-v-1" @@ -29,18 +30,21 @@ const attributes: FormsetData = [ values: [ { __typename: "AttributeValue", + file: null, id: "attrv-2", name: "Attribute 2 Value 1", slug: "attr-2-v-1" }, { __typename: "AttributeValue", + file: null, id: "attrv-3", name: "Attribute 2 Value 2", slug: "attr-2-v-2" }, { __typename: "AttributeValue", + file: null, id: "attrv-4", name: "Attribute 2 Value 3", slug: "attr-2-v-3" @@ -50,6 +54,28 @@ const attributes: FormsetData = [ id: "attr-2", label: "Attribute 2", value: ["attr-2-v-3"] + }, + { + data: { + inputType: AttributeInputTypeEnum.FILE, + isRequired: false, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + }, + id: "ifudbgidfsb", + label: "File Attribute", + value: [] } ]; diff --git a/src/pages/utils/handlers.ts b/src/pages/utils/handlers.ts index 477c940d2..a97db47ed 100644 --- a/src/pages/utils/handlers.ts +++ b/src/pages/utils/handlers.ts @@ -1,14 +1,14 @@ +import { AttributeInputData } from "@saleor/components/Attributes"; import { FormChange } from "@saleor/hooks/useForm"; import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; import { toggle } from "@saleor/utils/lists"; -import { PageAttributeInputData } from "../components/PageAttributes"; import { PageDetails_page_pageType } from "../types/PageDetails"; import { getAttributeInputFromPageType } from "./data"; export function createPageTypeSelectHandler( change: FormChange, - setAttributes: (data: FormsetData) => void, + setAttributes: (data: FormsetData) => void, setPageType: (pageType: PageDetails_page_pageType) => void, pageTypeChoiceList: PageDetails_page_pageType[] ): FormChange { @@ -36,7 +36,7 @@ export function createAttributeChangeHandler( export function createAttributeMultiChangeHandler( changeAttributeData: FormsetChange, - attributes: FormsetData, + attributes: FormsetData, triggerChange: () => void ): FormsetChange { return (attributeId: string, value: string) => { diff --git a/src/pages/views/PageCreate.tsx b/src/pages/views/PageCreate.tsx index 8219263a7..39b50aa83 100644 --- a/src/pages/views/PageCreate.tsx +++ b/src/pages/views/PageCreate.tsx @@ -1,5 +1,11 @@ +import { getAttributesAfterFileAttributesUpdate } from "@saleor/attributes/utils/data"; +import { + handleUploadMultipleFiles, + prepareAttributesInput +} from "@saleor/attributes/utils/handlers"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { useFileUploadMutation } from "@saleor/files/mutations"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePageTypeSearch from "@saleor/searches/usePageTypeSearch"; @@ -12,7 +18,7 @@ import React from "react"; import { useIntl } from "react-intl"; import PageDetailsPage from "../components/PageDetailsPage"; -import { PageData } from "../components/PageDetailsPage/form"; +import { PageSubmitData } from "../components/PageDetailsPage/form"; import { TypedPageCreate } from "../mutations"; import { PageCreate as PageCreateData } from "../types/PageCreate"; import { pageListUrl, pageUrl } from "../urls"; @@ -36,6 +42,8 @@ export const PageCreate: React.FC = () => { variables: DEFAULT_INITIAL_SEARCH_DATA }); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + const handlePageCreate = (data: PageCreateData) => { if (data.pageCreate.errors.length === 0) { notify({ @@ -51,14 +59,24 @@ export const PageCreate: React.FC = () => { return ( {(pageCreate, pageCreateOpts) => { - const handleCreate = async (formData: PageData) => { + const handleCreate = async (formData: PageSubmitData) => { + const uploadFilesResult = await handleUploadMultipleFiles( + formData.attributesWithNewFileValue, + variables => uploadFile({ variables }) + ); + + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + formData.attributesWithNewFileValue, + uploadFilesResult + ); + const result = await pageCreate({ variables: { input: { - attributes: formData.attributes.map(attribute => ({ - id: attribute.id, - values: attribute.value - })), + attributes: prepareAttributesInput({ + attributes: formData.attributes, + updatedFileAttributes + }), contentJson: JSON.stringify(formData.content), isPublished: formData.isPublished, pageType: formData.pageType, @@ -90,7 +108,7 @@ export const PageCreate: React.FC = () => { })} /> ({ - attributes: data.attributes.map(attribute => ({ - id: attribute.id, - values: attribute.value - })), +const createPageInput = ( + data: PageData, + updatedFileAttributes: AttributeValueInput[] +): PageInput => ({ + attributes: prepareAttributesInput({ + attributes: data.attributes, + updatedFileAttributes + }), contentJson: JSON.stringify(data.content), isPublished: data.isPublished, publicationDate: data.publicationDate, @@ -49,96 +67,127 @@ export const PageDetails: React.FC = ({ id, params }) => { const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); - const handlePageRemove = (data: PageRemove) => { - if (data.pageDelete.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - navigate(pageListUrl()); + const pageDetails = usePageDetailsQuery({ + variables: { + id } + }); + + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + + const [pageUpdate, pageUpdateOpts] = usePageUpdateMutation({}); + + const [ + deleteAttributeValue, + deleteAttributeValueOpts + ] = useAttributeValueDeleteMutation({}); + + const [pageRemove, pageRemoveOpts] = usePageRemoveMutation({ + onCompleted: (data: PageRemove) => { + if (data.pageDelete.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate(pageListUrl()); + } + } + }); + + const handleUpdate = async (data: PageSubmitData) => { + let errors: Array< + AttributeErrorFragment | UploadErrorFragment | PageErrorFragment + > = []; + + const uploadFilesResult = await handleUploadMultipleFiles( + data.attributesWithNewFileValue, + variables => uploadFile({ variables }) + ); + + const deleteAttributeValuesResult = await handleDeleteMultipleAttributeValues( + data.attributesWithNewFileValue, + pageDetails?.data?.page?.attributes, + variables => deleteAttributeValue({ variables }) + ); + + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + data.attributesWithNewFileValue, + uploadFilesResult + ); + + const updateResult = await pageUpdate({ + variables: { + id, + input: createPageInput(data, updatedFileAttributes) + } + }); + + errors = [ + ...errors, + ...mergeFileUploadErrors(uploadFilesResult), + ...mergeAttributeValueDeleteErrors(deleteAttributeValuesResult), + ...updateResult.data.pageUpdate.errors + ]; + + return errors; }; + const handleSubmit = createMetadataUpdateHandler( + pageDetails.data?.page, + handleUpdate, + variables => updateMetadata({ variables }), + variables => updatePrivateMetadata({ variables }) + ); + return ( - - {(pageRemove, pageRemoveOpts) => ( - - {(pageUpdate, pageUpdateOpts) => ( - - {pageDetails => { - const handleUpdate = async (data: PageData) => { - const result = await pageUpdate({ - variables: { - id, - input: createPageInput(data) - } - }); - - return result.data.pageUpdate.errors; - }; - - const handleSubmit = createMetadataUpdateHandler( - pageDetails.data?.page, - handleUpdate, - variables => updateMetadata({ variables }), - variables => updatePrivateMetadata({ variables }) - ); - - return ( - <> - pageDetails.data.page.title)} - /> - navigate(pageListUrl())} - onRemove={() => - navigate( - pageUrl(id, { - action: "remove" - }) - ) - } - onSubmit={handleSubmit} - /> - navigate(pageUrl(id))} - onConfirm={pageRemove} - variant="delete" - > - - - {getStringOrPlaceholder( - pageDetails.data?.page?.title - )} - - ) - }} - /> - - - - ); - }} - - )} - - )} - + <> + pageDetails.data.page.title)} /> + navigate(pageListUrl())} + onRemove={() => + navigate( + pageUrl(id, { + action: "remove" + }) + ) + } + onSubmit={handleSubmit} + /> + navigate(pageUrl(id))} + onConfirm={() => pageRemove({ variables: { id } })} + variant="delete" + > + + + {getStringOrPlaceholder(pageDetails.data?.page?.title)} + + ) + }} + /> + + + ); }; PageDetails.displayName = "PageDetails"; diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index 462670717..d6235632e 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -23,6 +23,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", name: "John Doe", slug: "john-doe", @@ -32,6 +33,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", name: "Milionare Pirate", slug: "milionare-pirate", @@ -53,6 +55,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE1", name: "100g", slug: "100g", @@ -62,6 +65,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE2", name: "250g", slug: "250g", @@ -71,6 +75,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE3", name: "500g", slug: "500g", @@ -80,6 +85,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE4", name: "1kg", slug: "1kg", @@ -101,6 +107,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjY=", name: "Saleor", slug: "saleor", @@ -122,6 +129,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIx", name: "100g", slug: "100g", @@ -131,6 +139,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIy", name: "250g", slug: "250g", @@ -140,6 +149,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIz", name: "500g", slug: "500g", @@ -161,6 +171,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEz", name: "Arabica", slug: "arabica", @@ -170,6 +181,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE0", name: "Robusta", slug: "robusta", @@ -191,6 +203,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM=", name: "Round", slug: "round", @@ -200,6 +213,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjQ=", name: "V-Neck", slug: "v-neck", @@ -209,6 +223,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjU=", name: "Polo", slug: "polo", @@ -230,6 +245,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE=", name: "Blue", slug: "blue", @@ -239,6 +255,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI=", name: "White", slug: "white", @@ -260,6 +277,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMw", name: "Soft", slug: "soft", @@ -269,6 +287,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMx", name: "Hard", slug: "hard", @@ -278,6 +297,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMy", name: "Middle soft", slug: "middle-soft", @@ -287,6 +307,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMz", name: "Middle hard", slug: "middle-hard", @@ -296,6 +317,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM0", name: "Middle", slug: "middle", @@ -305,6 +327,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM1", name: "Very hard", slug: "very-hard", @@ -326,6 +349,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE5", name: "Sour", slug: "sour", @@ -335,6 +359,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIw", name: "Sweet", slug: "sweet", @@ -356,6 +381,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI4", name: "English", slug: "english", @@ -365,6 +391,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI5", name: "Pirate", slug: "pirate", @@ -386,6 +413,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI2", name: "Mirumee Press", slug: "mirumee-press", @@ -395,6 +423,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI3", name: "Saleor Publishing", slug: "saleor-publishing", @@ -416,6 +445,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ values: [ { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjc=", name: "XS", slug: "xs", @@ -425,6 +455,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjg=", name: "S", slug: "s", @@ -434,6 +465,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjk=", name: "M", slug: "m", @@ -443,6 +475,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEw", name: "L", slug: "l", @@ -452,6 +485,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEx", name: "XL", slug: "xl", @@ -461,6 +495,7 @@ export const attributes: SearchProductTypes_search_edges_node_productAttributes[ }, { __typename: "AttributeValue" as "AttributeValue", + file: null, id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEy", name: "XXL", slug: "xxl", diff --git a/src/products/components/ProductAttributes/index.ts b/src/products/components/ProductAttributes/index.ts deleted file mode 100644 index 340bf9b54..000000000 --- a/src/products/components/ProductAttributes/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./ProductAttributes"; -export * from "./ProductAttributes"; diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 82a560949..a5f85f6d2 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -1,6 +1,7 @@ import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; -import { AvailabilityCard } from "@saleor/components/AvailabilityCard"; +import Attributes from "@saleor/components/Attributes"; +import AvailabilityCard from "@saleor/components/AvailabilityCard"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; @@ -25,7 +26,6 @@ import React from "react"; import { useIntl } from "react-intl"; import { FetchMoreProps } from "../../../types"; -import ProductAttributes from "../ProductAttributes"; import ProductDetailsForm from "../ProductDetailsForm"; import ProductOrganization from "../ProductOrganization"; import ProductShipping from "../ProductShipping/ProductShipping"; @@ -43,7 +43,7 @@ interface ProductCreatePageProps { currentChannels: ChannelData[]; collections: SearchCollections_search_edges_node[]; categories: SearchCategories_search_edges_node[]; - disabled: boolean; + loading: boolean; fetchMoreCategories: FetchMoreProps; fetchMoreCollections: FetchMoreProps; fetchMoreProductTypes: FetchMoreProps; @@ -68,7 +68,7 @@ export const ProductCreatePage: React.FC = ({ allChannelsCount, channelsErrors, currentChannels, - disabled, + loading, categories: categoryChoiceList, collections: collectionChoiceList, errors, @@ -153,19 +153,21 @@ export const ProductCreatePage: React.FC = ({
{data.attributes.length > 0 && ( - )} @@ -173,7 +175,7 @@ export const ProductCreatePage: React.FC = ({ <> = ({ = ({ titlePlaceholder={data.name} description={data.seoDescription} descriptionPlaceholder={data.seoTitle} - loading={disabled} + loading={loading} onChange={change} /> @@ -227,7 +229,7 @@ export const ProductCreatePage: React.FC = ({ categoryInputDisplayValue={selectedCategory} collections={collections} data={data} - disabled={disabled} + disabled={loading} errors={errors} fetchCategories={fetchCategories} fetchCollections={fetchCollections} @@ -260,14 +262,14 @@ export const ProductCreatePage: React.FC = ({ selectedChannelsCount={data.channelListings.length} allChannelsCount={allChannelsCount} channels={data.channelListings} - disabled={disabled} + disabled={loading} onChange={handlers.changeChannels} openModal={openChannelsModal} /> = ({ onCancel={onBack} onSave={submit} state={saveButtonBarState} - disabled={disabled || !onSubmit || formDisabled || !hasChanged} + disabled={loading || !onSubmit || formDisabled || !hasChanged} /> ); diff --git a/src/products/components/ProductCreatePage/form.tsx b/src/products/components/ProductCreatePage/form.tsx index 11e4152e1..e5341ad6a 100644 --- a/src/products/components/ProductCreatePage/form.tsx +++ b/src/products/components/ProductCreatePage/form.tsx @@ -1,18 +1,27 @@ import { OutputData } from "@editorjs/editorjs"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; +import { + AttributeInput, + AttributeInputData +} from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { RichTextEditorChange } from "@saleor/components/RichTextEditor"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import useForm, { FormChange } from "@saleor/hooks/useForm"; -import useFormset, { FormsetChange } from "@saleor/hooks/useFormset"; +import useFormset, { + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { getAttributeInputFromProductType, + getAttributesDisplayData, ProductType } from "@saleor/products/utils/data"; import { createAttributeChangeHandler, + createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler, @@ -30,10 +39,6 @@ import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTr import useRichText from "@saleor/utils/richText/useRichText"; import React from "react"; -import { - ProductAttributeInput, - ProductAttributeInputData -} from "../ProductAttributes"; import { ProductStockInput } from "../ProductStocks"; export interface ProductCreateFormData extends MetadataFormData { @@ -57,7 +62,8 @@ export interface ProductCreateFormData extends MetadataFormData { weight: string; } export interface ProductCreateData extends ProductCreateFormData { - attributes: ProductAttributeInput[]; + attributes: AttributeInput[]; + attributesWithNewFileValue: FormsetData; stocks: ProductStockInput[]; } @@ -82,6 +88,7 @@ interface ProductCreateHandlers data: Omit ) => void >, + Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; } @@ -159,11 +166,12 @@ function useProductCreateForm( ...initial, ...defaultInitialFormData }); - const attributes = useFormset( + const attributes = useFormset( initial?.productType ? getAttributeInputFromProductType(initialProductType) : [] ); + const attributesWithNewFileValue = useFormset([]); const stocks = useFormset([]); const [productType, setProductType] = useStateFromProps( initialProductType || null @@ -201,6 +209,13 @@ function useProductCreateForm( attributes.data, triggerChange ); + const handleAttributeFileChange = createAttributeFileChangeHandler( + attributes.change, + attributesWithNewFileValue.data, + attributesWithNewFileValue.add, + attributesWithNewFileValue.change, + triggerChange + ); const handleProductTypeSelect = createProductTypeSelectHandler( attributes.set, setProductType, @@ -243,7 +258,11 @@ function useProductCreateForm( const getData = (): ProductCreateData => ({ ...form.data, - attributes: attributes.data, + attributes: getAttributesDisplayData( + attributes.data, + attributesWithNewFileValue.data + ), + attributesWithNewFileValue: attributesWithNewFileValue.data, description: description.current, productType, stocks: stocks.data @@ -276,6 +295,7 @@ function useProductCreateForm( changeStock: handleStockChange, deleteStock: handleStockDelete, selectAttribute: handleAttributeChange, + selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, selectCategory: handleCategorySelect, selectCollection: handleCollectionSelect, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx index 56fdf4eb6..bc872118d 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.test.tsx @@ -67,7 +67,7 @@ const props: ProductUpdatePageProps = { const selectors = { dropdown: `[data-test="autocomplete-dropdown"]`, empty: `[data-test-type="empty"]`, - input: `[data-test="product-attribute-value"] input` + input: `[data-test="attribute-value"] input` }; describe("Product details page", () => { diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 649886be5..225aa4aca 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -1,7 +1,8 @@ import { OutputData } from "@editorjs/editorjs"; import { ChannelData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; -import { AvailabilityCard } from "@saleor/components/AvailabilityCard"; +import Attributes, { AttributeInput } from "@saleor/components/Attributes"; +import AvailabilityCard from "@saleor/components/AvailabilityCard"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; @@ -16,6 +17,7 @@ import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/Prod import { TaxTypeFragment } from "@saleor/fragments/types/TaxTypeFragment"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { SubmitPromise } from "@saleor/hooks/useForm"; +import { FormsetData } from "@saleor/hooks/useFormset"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; @@ -37,7 +39,6 @@ import { ProductDetails_product_variants } from "../../types/ProductDetails"; import { getChoices, ProductUpdatePageFormData } from "../../utils/data"; -import ProductAttributes, { ProductAttributeInput } from "../ProductAttributes"; import ProductDetailsForm from "../ProductDetailsForm"; import ProductImages from "../ProductImages"; import ProductOrganization from "../ProductOrganization"; @@ -92,7 +93,8 @@ export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData, ChannelProps { addStocks: ProductStockInput[]; - attributes: ProductAttributeInput[]; + attributes: AttributeInput[]; + attributesWithNewFileValue: FormsetData; collections: string[]; description: OutputData; removeStocks: string[]; @@ -217,12 +219,14 @@ export const ProductUpdatePage: React.FC = ({ /> {data.attributes.length > 0 && ( - )} diff --git a/src/products/components/ProductUpdatePage/form.tsx b/src/products/components/ProductUpdatePage/form.tsx index c78caa8a7..2d21c9fce 100644 --- a/src/products/components/ProductUpdatePage/form.tsx +++ b/src/products/components/ProductUpdatePage/form.tsx @@ -1,22 +1,26 @@ import { OutputData } from "@editorjs/editorjs"; import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils"; +import { AttributeInput } from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { RichTextEditorChange } from "@saleor/components/RichTextEditor"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm"; import useFormset, { + FormsetAtomicData, FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; import { ProductDetails_product } from "@saleor/products/types/ProductDetails"; import { getAttributeInputFromProduct, + getAttributesDisplayData, getProductUpdatePageFormData, getStockInputFromProduct } from "@saleor/products/utils/data"; import { createAttributeChangeHandler, + createAttributeFileChangeHandler, createAttributeMultiChangeHandler, createChannelsChangeHandler, createChannelsPriceChangeHandler @@ -33,9 +37,8 @@ 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 React, { useEffect } from "react"; -import { ProductAttributeInput } from "../ProductAttributes"; import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks"; export interface ProductUpdateFormData extends MetadataFormData { @@ -55,13 +58,26 @@ export interface ProductUpdateFormData extends MetadataFormData { trackInventory: boolean; weight: string; } +export interface FileAttributeInputData { + attributeId: string; + file: File; +} +export type FileAttributeInput = FormsetAtomicData< + FileAttributeInputData, + string[] +>; + +export interface FileAttributesSubmitData { + fileAttributes: FileAttributeInput[]; +} export interface ProductUpdateData extends ProductUpdateFormData { - attributes: ProductAttributeInput[]; + attributes: AttributeInput[]; description: OutputData; stocks: ProductStockInput[]; } export interface ProductUpdateSubmitData extends ProductUpdateFormData { - attributes: ProductAttributeInput[]; + attributes: AttributeInput[]; + attributesWithNewFileValue: FormsetData; collections: string[]; description: OutputData; addStocks: ProductStockInput[]; @@ -89,6 +105,7 @@ interface ProductUpdateHandlers data: Omit ) => void >, + Record<"selectAttributeFile", FormsetChange>, Record<"addStock" | "deleteStock", (id: string) => void> { changeDescription: RichTextEditorChange; } @@ -165,6 +182,7 @@ function useProductUpdateForm( ) ); const attributes = useFormset(getAttributeInputFromProduct(product)); + const attributesWithNewFileValue = useFormset([]); const stocks = useFormset(getStockInputFromProduct(product)); const [description, changeDescription] = useRichText({ initial: product?.descriptionJson, @@ -201,6 +219,13 @@ function useProductUpdateForm( attributes.data, triggerChange ); + const handleAttributeFileChange = createAttributeFileChangeHandler( + attributes.change, + attributesWithNewFileValue.data, + attributesWithNewFileValue.add, + attributesWithNewFileValue.change, + triggerChange + ); const handleStockChange: FormsetChange = (id, value) => { triggerChange(); stocks.change(id, value); @@ -235,9 +260,16 @@ function useProductUpdateForm( triggerChange ); + useEffect(() => { + attributesWithNewFileValue.set([]); + }, [product]); + const data: ProductUpdateData = { ...form.data, - attributes: attributes.data, + attributes: getAttributesDisplayData( + attributes.data, + attributesWithNewFileValue.data + ), description: description.current, stocks: stocks.data }; @@ -248,10 +280,12 @@ function useProductUpdateForm( ...getMetadata(data, isMetadataModified, isPrivateMetadataModified), addStocks: [], attributes: attributes.data, + attributesWithNewFileValue: attributesWithNewFileValue.data, description: description.current }); - const submit = () => handleFormSubmit(getSubmitData(), onSubmit, setChanged); + const submit = async () => + handleFormSubmit(getSubmitData(), onSubmit, setChanged); const disabled = !opts.hasVariants && @@ -274,6 +308,7 @@ function useProductUpdateForm( changeStock: handleStockChange, deleteStock: handleStockDelete, selectAttribute: handleAttributeChange, + selectAttributeFile: handleAttributeFileChange, selectAttributeMultiple: handleAttributeMultiChange, selectCategory: handleCategorySelect, selectCollection: handleCollectionSelect, diff --git a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx index e1bddf7f2..061174d4f 100644 --- a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx +++ b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx @@ -9,7 +9,10 @@ import SingleAutocompleteSelectField, { } from "@saleor/components/SingleAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; -import { ProductVariant_attributes_attribute_values } from "@saleor/fragments/types/ProductVariant"; +import { + ProductVariant_nonSelectionAttributes_attribute_values, + ProductVariant_selectionAttributes_attribute_values +} from "@saleor/fragments/types/ProductVariant"; import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; import { commonMessages } from "@saleor/intl"; import { getProductVariantAttributeErrorMessage } from "@saleor/utils/errors/product"; @@ -17,7 +20,10 @@ import React from "react"; import { useIntl } from "react-intl"; export interface VariantAttributeInputData { - values: ProductVariant_attributes_attribute_values[]; + values: Array< + | ProductVariant_selectionAttributes_attribute_values + | ProductVariant_nonSelectionAttributes_attribute_values + >; } export type VariantAttributeInput = FormsetAtomicData< VariantAttributeInputData, diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index f8b45c959..757640156 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -1,5 +1,8 @@ import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import Attributes, { + VariantAttributeScope +} from "@saleor/components/Attributes"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; @@ -12,16 +15,34 @@ import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/Prod import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { ReorderAction } from "@saleor/types"; import React from "react"; -import { useIntl } from "react-intl"; +import { defineMessages, useIntl } from "react-intl"; import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData"; import ProductShipping from "../ProductShipping/ProductShipping"; import ProductStocks from "../ProductStocks"; -import ProductVariantAttributes from "../ProductVariantAttributes"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; import ProductVariantCreateForm, { ProductVariantCreateData } from "./form"; +const messages = defineMessages({ + attributesHeader: { + defaultMessage: "Variant Attributes", + description: "attributes, section header" + }, + attributesSelectionHeader: { + defaultMessage: "Variant Selection Attributes", + description: "attributes, section header" + }, + deleteVariant: { + defaultMessage: "Delete Variant", + description: "button" + }, + saveVariant: { + defaultMessage: "Save variant", + description: "button" + } +}); + interface ProductVariantCreatePageProps { channels: ChannelPriceData[]; channelErrors: ProductChannelListingErrorFragment[] | undefined; @@ -89,11 +110,34 @@ const ProductVariantCreatePage: React.FC = ({ />
- + attribute.data.variantAttributeScope === + VariantAttributeScope.NOT_VARIANT_SELECTION + )} + loading={disabled} disabled={disabled} errors={errors} onChange={handlers.selectAttribute} + onMultiChange={handlers.selectAttributeMultiple} + onFileChange={handlers.selectAttributeFile} + /> + + + attribute.data.variantAttributeScope === + VariantAttributeScope.VARIANT_SELECTION + )} + loading={disabled} + disabled={disabled} + errors={errors} + onChange={handlers.selectAttribute} + onMultiChange={handlers.selectAttributeMultiple} + onFileChange={handlers.selectAttributeFile} /> = ({ ; channelListings: FormsetData; + attributes: AttributeInput[]; + attributesWithNewFileValue: FormsetData; stocks: ProductStockInput[]; } @@ -35,18 +46,25 @@ export interface UseProductVariantCreateFormOpts { currentChannels: ChannelPriceData[]; } +interface ProductVariantCreateHandlers + extends Record< + | "changeStock" + | "selectAttribute" + | "selectAttributeMultiple" + | "changeChannels", + FormsetChange + >, + Record<"selectAttributeFile", FormsetChange>, + Record<"addStock" | "deleteStock", (id: string) => void> { + changeMetadata: FormChange; +} + export interface UseProductVariantCreateFormResult { change: FormChange; data: ProductVariantCreateData; disabled: boolean; // TODO: type FormsetChange - handlers: Record< - "changeStock" | "selectAttribute" | "changeChannels", - FormsetChange - > & - Record<"addStock" | "deleteStock", (id: string) => void> & { - changeMetadata: FormChange; - }; + handlers: ProductVariantCreateHandlers; hasChanged: boolean; submit: () => void; } @@ -79,6 +97,7 @@ function useProductVariantCreateForm( const form = useForm(initial); const attributes = useFormset(attributeInput); + const attributesWithNewFileValue = useFormset([]); const stocks = useFormset([]); const channels = useFormset(channelsInput); const { @@ -90,10 +109,22 @@ function useProductVariantCreateForm( triggerChange(); }; const changeMetadata = makeMetadataChangeHandler(handleChange); - const handleAttributeChange: FormsetChange = (id, value) => { - attributes.change(id, value); - triggerChange(); - }; + const handleAttributeChange = createAttributeChangeHandler( + attributes.change, + triggerChange + ); + const handleAttributeMultiChange = createAttributeMultiChangeHandler( + attributes.change, + attributes.data, + triggerChange + ); + const handleAttributeFileChange = createAttributeFileChangeHandler( + attributes.change, + attributesWithNewFileValue.data, + attributesWithNewFileValue.add, + attributesWithNewFileValue.change, + triggerChange + ); const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ @@ -124,7 +155,11 @@ function useProductVariantCreateForm( const data: ProductVariantCreateData = { ...form.data, - attributes: attributes.data, + attributes: getAttributesDisplayData( + attributes.data, + attributesWithNewFileValue.data + ), + attributesWithNewFileValue: attributesWithNewFileValue.data, channelListings: channels.data, stocks: stocks.data }; @@ -141,7 +176,9 @@ function useProductVariantCreateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, - selectAttribute: handleAttributeChange + selectAttribute: handleAttributeChange, + selectAttributeFile: handleAttributeFileChange, + selectAttributeMultiple: handleAttributeMultiChange }, hasChanged: changed, submit diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 8fc69efd9..113e4bc32 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -1,9 +1,14 @@ import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import Attributes, { + AttributeInput, + VariantAttributeScope +} from "@saleor/components/Attributes"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Grid from "@saleor/components/Grid"; +import { MetadataFormData } from "@saleor/components/Metadata"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; @@ -14,11 +19,11 @@ import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate"; import { ReorderAction } from "@saleor/types"; import React from "react"; +import { defineMessages, useIntl } from "react-intl"; import { maybe } from "../../../misc"; import ProductShipping from "../ProductShipping/ProductShipping"; -import ProductStocks from "../ProductStocks"; -import ProductVariantAttributes from "../ProductVariantAttributes"; +import ProductStocks, { ProductStockInput } from "../ProductStocks"; import ProductVariantImages from "../ProductVariantImages"; import ProductVariantImageSelectDialog from "../ProductVariantImageSelectDialog"; import ProductVariantNavigation from "../ProductVariantNavigation"; @@ -28,6 +33,33 @@ import ProductVariantUpdateForm, { ProductVariantUpdateSubmitData } from "./form"; +const messages = defineMessages({ + nonSelectionAttributes: { + defaultMessage: "Variant Attributes", + description: "attributes, section header" + }, + selectionAttributesHeader: { + defaultMessage: "Variant Selection Attributes", + description: "attributes, section header" + } +}); + +export interface ProductVariantPageFormData extends MetadataFormData { + costPrice: string; + price: string; + sku: string; + trackInventory: boolean; + weight: string; +} + +export interface ProductVariantPageSubmitData + extends ProductVariantPageFormData { + attributes: AttributeInput[]; + addStocks: ProductStockInput[]; + updateStocks: ProductStockInput[]; + removeStocks: string[]; +} + interface ProductVariantPageProps { defaultVariantId?: string; defaultWeightUnit: string; @@ -75,6 +107,8 @@ const ProductVariantPage: React.FC = ({ onSetDefaultVariant, onWarehouseConfigure }) => { + const intl = useIntl(); + const [isModalOpened, setModalStatus] = React.useState(false); const toggleModal = () => setModalStatus(!isModalOpened); @@ -131,11 +165,36 @@ const ProductVariantPage: React.FC = ({ />
- + attribute.data.variantAttributeScope === + VariantAttributeScope.NOT_VARIANT_SELECTION + )} + loading={loading} disabled={loading} errors={errors} onChange={handlers.selectAttribute} + onMultiChange={handlers.selectAttributeMultiple} + onFileChange={handlers.selectAttributeFile} + /> + + + attribute.data.variantAttributeScope === + VariantAttributeScope.VARIANT_SELECTION + )} + loading={loading} + disabled={loading} + errors={errors} + onChange={handlers.selectAttribute} + onMultiChange={handlers.selectAttributeMultiple} + onFileChange={handlers.selectAttributeFile} /> ; channelListings: FormsetData; + attributes: AttributeInput[]; stocks: ProductStockInput[]; } export interface ProductVariantUpdateSubmitData extends ProductVariantUpdateFormData { - attributes: FormsetData; + attributes: AttributeInput[]; + attributesWithNewFileValue: FormsetData; addStocks: ProductStockInput[]; channelListings: FormsetData; updateStocks: ProductStockInput[]; @@ -50,17 +59,24 @@ export interface UseProductVariantUpdateFormOpts { currentChannels: ChannelPriceData[]; } +interface ProductVariantUpdateHandlers + extends Record< + | "changeStock" + | "selectAttribute" + | "selectAttributeMultiple" + | "changeChannels", + FormsetChange + >, + Record<"selectAttributeFile", FormsetChange>, + Record<"addStock" | "deleteStock", (id: string) => void> { + changeMetadata: FormChange; +} + export interface UseProductVariantUpdateFormResult { change: FormChange; data: ProductVariantUpdateData; disabled: boolean; - handlers: Record< - "changeStock" | "selectAttribute" | "changeChannels", - FormsetChange - > & - Record<"addStock" | "deleteStock", (id: string) => void> & { - changeMetadata: FormChange; - }; + handlers: ProductVariantUpdateHandlers; hasChanged: boolean; submit: () => void; } @@ -94,6 +110,7 @@ function useProductVariantUpdateForm( const form = useForm(initial); const attributes = useFormset(attributeInput); + const attributesWithNewFileValue = useFormset([]); const stocks = useFormset(stockInput); const channels = useFormset(channelsInput); const { @@ -107,10 +124,22 @@ function useProductVariantUpdateForm( triggerChange(); }; const changeMetadata = makeMetadataChangeHandler(handleChange); - const handleAttributeChange: FormsetChange = (id, value) => { - attributes.change(id, value); - triggerChange(); - }; + const handleAttributeChange = createAttributeChangeHandler( + attributes.change, + triggerChange + ); + const handleAttributeMultiChange = createAttributeMultiChangeHandler( + attributes.change, + attributes.data, + triggerChange + ); + const handleAttributeFileChange = createAttributeFileChangeHandler( + attributes.change, + attributesWithNewFileValue.data, + attributesWithNewFileValue.add, + attributesWithNewFileValue.change, + triggerChange + ); const handleStockAdd = (id: string) => { triggerChange(); stocks.add({ @@ -137,6 +166,10 @@ function useProductVariantUpdateForm( triggerChange(); }; + useEffect(() => { + attributesWithNewFileValue.set([]); + }, [variant]); + const dataStocks = stocks.data.map(stock => stock.id); const variantStocks = variant?.stocks.map(stock => stock.warehouse.id) || []; const stockDiff = diff(variantStocks, dataStocks); @@ -155,7 +188,10 @@ function useProductVariantUpdateForm( ); const data: ProductVariantUpdateData = { ...form.data, - attributes: attributes.data, + attributes: getAttributesDisplayData( + attributes.data, + attributesWithNewFileValue.data + ), channelListings: channels.data, stocks: stocks.data }; @@ -164,6 +200,7 @@ function useProductVariantUpdateForm( ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified), addStocks, attributes: attributes.data, + attributesWithNewFileValue: attributesWithNewFileValue.data, channelListings: channels.data, removeStocks: stockDiff.removed, updateStocks @@ -181,7 +218,9 @@ function useProductVariantUpdateForm( changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, - selectAttribute: handleAttributeChange + selectAttribute: handleAttributeChange, + selectAttributeFile: handleAttributeFileChange, + selectAttributeMultiple: handleAttributeMultiChange }, hasChanged: changed, submit diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index e740ac9fd..7b7499327 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -30,12 +30,14 @@ export const product: ( values: [ { __typename: "AttributeValue", + file: null, id: "ptav47282", name: "portals", slug: "portals" }, { __typename: "AttributeValue", + file: null, id: "ptav17253", name: "Baht", slug: "Baht" @@ -45,6 +47,7 @@ export const product: ( values: [ { __typename: "AttributeValue", + file: null, id: "ptav47282", name: "portals", slug: "portals" @@ -63,24 +66,28 @@ export const product: ( values: [ { __typename: "AttributeValue", + file: null, id: "ptav31282", name: "payment", slug: "payment" }, { __typename: "AttributeValue", + file: null, id: "ptav14907", name: "Auto Loan Account", slug: "Auto-Loan-Account" }, { __typename: "AttributeValue", + file: null, id: "ptav27366", name: "Garden", slug: "Garden" }, { __typename: "AttributeValue", + file: null, id: "ptav11873", name: "override", slug: "override" @@ -90,6 +97,7 @@ export const product: ( values: [ { __typename: "AttributeValue", + file: null, id: "ptav14907", name: "Auto Loan Account", slug: "Auto-Loan-Account" @@ -243,6 +251,55 @@ export const product: ( hasVariants: true, id: "pt76406", name: "Versatile", + nonSelectionVariantAttributes: [ + { + __typename: "Attribute", + id: "isdugfhud", + inputType: AttributeInputTypeEnum.FILE, + name: "Attachment", + slug: "attachment", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + } + ], + selectionVariantAttributes: [ + { + __typename: "Attribute", + id: "pta18161", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Color", + slug: "color", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "ptvav47282", + name: "Black", + slug: "black" + }, + { + __typename: "AttributeValue", + file: null, + id: "ptvav17253", + name: "White", + slug: "white" + } + ] + } + ], taxType: { __typename: "TaxType", description: "standard", @@ -251,25 +308,46 @@ export const product: ( variantAttributes: [ { __typename: "Attribute", - id: "pta18161", - name: "Color", - slug: "color", - sortOrder: 0, + id: "isdugfhud", + inputType: AttributeInputTypeEnum.FILE, + name: "Attachment", + slug: "attachment", valueRequired: true, values: [ { __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + }, + { + __typename: "Attribute", + id: "pta18161", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Color", + slug: "color", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, id: "ptvav47282", name: "Black", - slug: "black", - sortOrder: 0 + slug: "black" }, { __typename: "AttributeValue", + file: null, id: "ptvav17253", name: "White", - slug: "white", - sortOrder: 1 + slug: "white" } ] } @@ -713,8 +791,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6MQ==", - name: "Pineapple" + name: "Pineapple", + slug: "pineapple" } ] } @@ -818,8 +898,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6Mg==", - name: "Coconut" + name: "Coconut", + slug: "coconut" } ] } @@ -923,8 +1005,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6Mw==", - name: "Apple" + name: "Apple", + slug: "apple" } ] } @@ -1029,8 +1113,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NDk=", - name: "Orange" + name: "Orange", + slug: "orange" } ] } @@ -1134,8 +1220,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NTA=", - name: "Banana" + name: "Banana", + slug: "banana" } ] } @@ -1239,8 +1327,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NTE=", - name: "Bean" + name: "Bean", + slug: "bean" } ] } @@ -1344,8 +1434,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NTI=", - name: "Carrot" + name: "Carrot", + slug: "carrot" } ] } @@ -1449,8 +1541,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NTM=", - name: "Sprouty" + name: "Sprouty", + slug: "sprouty" } ] } @@ -1554,8 +1648,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -1659,8 +1755,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -1764,8 +1862,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -1869,8 +1969,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -1974,8 +2076,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -2079,8 +2183,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -2184,8 +2290,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -2289,8 +2397,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6ODI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -2394,8 +2504,10 @@ export const products = ( values: [ { __typename: "AttributeValue", + file: null, id: "QXR0cmlidXRlVmFsdWU6NzI=", - name: "Cotton" + name: "Cotton", + slug: "cotton" } ] } @@ -2491,84 +2603,6 @@ export const products = ( export const variant = (placeholderImage: string): ProductVariant => ({ __typename: "ProductVariant", - attributes: [ - { - __typename: "SelectedAttribute", - attribute: { - __typename: "Attribute" as "Attribute", - id: "pta18161", - name: "Borders", - slug: "Borders", - valueRequired: true, - values: [ - { - __typename: "AttributeValue", - id: "ptav47282", - name: "portals", - slug: "portals" - }, - { - __typename: "AttributeValue", - id: "ptav17253", - name: "Baht", - slug: "Baht" - } - ] - }, - values: [ - { - __typename: "AttributeValue", - id: "ptav47282", - name: "portals", - slug: "portals" - } - ] - }, - { - __typename: "SelectedAttribute", - attribute: { - __typename: "Attribute" as "Attribute", - id: "pta22785", - name: "Legacy", - slug: "Legacy", - valueRequired: true, - values: [ - { - __typename: "AttributeValue", - id: "ptav31282", - name: "payment", - slug: "payment" - }, - { - __typename: "AttributeValue", - id: "ptav14907", - name: "Auto Loan Account", - slug: "Auto-Loan-Account" - }, - { - __typename: "AttributeValue", - id: "ptav27366", - name: "Garden", - slug: "Garden" - }, - { - __typename: "AttributeValue", - id: "ptav11873", - name: "override", - slug: "override" - } - ] - }, - values: [ - { - __typename: "AttributeValue", - id: "ptav14907", - name: "Auto Loan Account", - slug: "Auto-Loan-Account" - } - ] - } - ], channelListings: [ { __typename: "ProductVariantChannelListing", @@ -2640,6 +2674,45 @@ export const variant = (placeholderImage: string): ProductVariant => ({ } ], name: "Extended Hard", + nonSelectionAttributes: [ + { + __typename: "SelectedAttribute", + attribute: { + __typename: "Attribute", + id: "nfnyffcf8eyfm", + inputType: AttributeInputTypeEnum.FILE, + name: "Attachment", + slug: "attachment", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + }, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "gdghdgdhkkdae", + name: "File First Value", + slug: "file-first-value" + } + ] + } + ], privateMetadata: [], product: { __typename: "Product" as "Product", @@ -2834,6 +2907,94 @@ export const variant = (placeholderImage: string): ProductVariant => ({ } ] }, + selectionAttributes: [ + { + __typename: "SelectedAttribute", + attribute: { + __typename: "Attribute" as "Attribute", + id: "pta18161", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Borders", + slug: "Borders", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "ptav47282", + name: "portals", + slug: "portals" + }, + { + __typename: "AttributeValue", + file: null, + id: "ptav17253", + name: "Baht", + slug: "Baht" + } + ] + }, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "ptav47282", + name: "portals", + slug: "portals" + } + ] + }, + { + __typename: "SelectedAttribute", + attribute: { + __typename: "Attribute" as "Attribute", + id: "pta22785", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Legacy", + slug: "Legacy", + valueRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "ptav31282", + name: "payment", + slug: "payment" + }, + { + __typename: "AttributeValue", + file: null, + id: "ptav14907", + name: "Auto Loan Account", + slug: "Auto-Loan-Account" + }, + { + __typename: "AttributeValue", + file: null, + id: "ptav27366", + name: "Garden", + slug: "Garden" + }, + { + __typename: "AttributeValue", + file: null, + id: "ptav11873", + name: "override", + slug: "override" + } + ] + }, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "ptav14907", + name: "Auto Loan Account", + slug: "Auto-Loan-Account" + } + ] + } + ], sku: "1230959124123", stocks: [ { diff --git a/src/products/queries.ts b/src/products/queries.ts index 31d157ca6..f4b108cd3 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -1,9 +1,11 @@ +import { attributeValueFragment } from "@saleor/fragments/attributes"; import { pageInfoFragment } from "@saleor/fragments/pageInfo"; import { fragmentVariant, productFragment, productFragmentDetails, - productVariantAttributesFragment + productVariantAttributesFragment, + variantAttributeFragment } from "@saleor/fragments/products"; import { taxTypeFragment } from "@saleor/fragments/taxes"; import { warehouseFragment } from "@saleor/fragments/warehouses"; @@ -94,6 +96,7 @@ export const useInitialProductFilterDataQuery = makeQuery< const productListQuery = gql` ${productFragment} + ${attributeValueFragment} query ProductList( $first: Int $after: String @@ -118,8 +121,7 @@ const productListQuery = gql` id } values { - id - name + ...AttributeValueFragment } } } @@ -185,6 +187,7 @@ export const useProductVariantQuery = makeQuery< >(productVariantQuery); const productVariantCreateQuery = gql` + ${variantAttributeFragment} query ProductVariantCreateData($id: ID!) { product(id: $id) { id @@ -203,16 +206,15 @@ const productVariantCreateQuery = gql` name productType { id - variantAttributes { - id - slug - name - valueRequired - values { - id - name - slug - } + selectionVariantAttributes: variantAttributes( + variantSelection: VARIANT_SELECTION + ) { + ...VariantAttributeFragment + } + nonSelectionVariantAttributes: variantAttributes( + variantSelection: NOT_VARIANT_SELECTION + ) { + ...VariantAttributeFragment } } thumbnail { diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index cc073ea5a..279c7b2e1 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL query operation: CreateMultipleVariantsData // ==================================================== +export interface CreateMultipleVariantsData_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface CreateMultipleVariantsData_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: CreateMultipleVariantsData_product_attributes_attribute_values_file | null; } export interface CreateMultipleVariantsData_product_attributes_attribute { @@ -25,11 +32,18 @@ export interface CreateMultipleVariantsData_product_attributes_attribute { values: (CreateMultipleVariantsData_product_attributes_attribute_values | null)[] | null; } +export interface CreateMultipleVariantsData_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface CreateMultipleVariantsData_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: CreateMultipleVariantsData_product_attributes_values_file | null; } export interface CreateMultipleVariantsData_product_attributes { @@ -38,11 +52,18 @@ export interface CreateMultipleVariantsData_product_attributes { values: (CreateMultipleVariantsData_product_attributes_values | null)[]; } +export interface CreateMultipleVariantsData_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface CreateMultipleVariantsData_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: CreateMultipleVariantsData_product_productType_variantAttributes_values_file | null; } export interface CreateMultipleVariantsData_product_productType_variantAttributes { diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts index 680658fc9..ce34926e9 100644 --- a/src/products/types/ProductChannelListingUpdate.ts +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -8,11 +8,18 @@ import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, WeightUnitsEn // GraphQL mutation operation: ProductChannelListingUpdate // ==================================================== +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values_file | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute { @@ -25,11 +32,18 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values | null)[] | null; } +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values_file | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes { @@ -38,11 +52,18 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values | null)[]; } +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values_file | null; } export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index a00567b09..a3413fd25 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -15,11 +15,18 @@ export interface ProductCreate_productCreate_errors { attributes: string[] | null; } +export interface ProductCreate_productCreate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductCreate_productCreate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductCreate_productCreate_product_attributes_attribute_values_file | null; } export interface ProductCreate_productCreate_product_attributes_attribute { @@ -32,11 +39,18 @@ export interface ProductCreate_productCreate_product_attributes_attribute { values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; } +export interface ProductCreate_productCreate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductCreate_productCreate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductCreate_productCreate_product_attributes_values_file | null; } export interface ProductCreate_productCreate_product_attributes { @@ -45,11 +59,18 @@ export interface ProductCreate_productCreate_product_attributes { values: (ProductCreate_productCreate_product_attributes_values | null)[]; } +export interface ProductCreate_productCreate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductCreate_productCreate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductCreate_productCreate_product_productType_variantAttributes_values_file | null; } export interface ProductCreate_productCreate_product_productType_variantAttributes { diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 4a9320a5e..c3fbd17fb 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTyp // GraphQL query operation: ProductDetails // ==================================================== +export interface ProductDetails_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductDetails_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductDetails_product_attributes_attribute_values_file | null; } export interface ProductDetails_product_attributes_attribute { @@ -25,11 +32,18 @@ export interface ProductDetails_product_attributes_attribute { values: (ProductDetails_product_attributes_attribute_values | null)[] | null; } +export interface ProductDetails_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductDetails_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductDetails_product_attributes_values_file | null; } export interface ProductDetails_product_attributes { @@ -38,11 +52,18 @@ export interface ProductDetails_product_attributes { values: (ProductDetails_product_attributes_values | null)[]; } +export interface ProductDetails_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductDetails_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductDetails_product_productType_variantAttributes_values_file | null; } export interface ProductDetails_product_productType_variantAttributes { diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 8f8112e85..0c6a58dc4 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -14,11 +14,18 @@ export interface ProductImageCreate_productImageCreate_errors { field: string | null; } +export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageCreate_productImageCreate_product_attributes_attribute_values_file | null; } export interface ProductImageCreate_productImageCreate_product_attributes_attribute { @@ -31,11 +38,18 @@ export interface ProductImageCreate_productImageCreate_product_attributes_attrib values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null; } +export interface ProductImageCreate_productImageCreate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageCreate_productImageCreate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageCreate_productImageCreate_product_attributes_values_file | null; } export interface ProductImageCreate_productImageCreate_product_attributes { @@ -44,11 +58,18 @@ export interface ProductImageCreate_productImageCreate_product_attributes { values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[]; } +export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageCreate_productImageCreate_product_productType_variantAttributes_values_file | null; } export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index e930253a0..0a0451595 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -14,11 +14,18 @@ export interface ProductImageUpdate_productImageUpdate_errors { field: string | null; } +export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageUpdate_productImageUpdate_product_attributes_attribute_values_file | null; } export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute { @@ -31,11 +38,18 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_attrib values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null; } +export interface ProductImageUpdate_productImageUpdate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageUpdate_productImageUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageUpdate_productImageUpdate_product_attributes_values_file | null; } export interface ProductImageUpdate_productImageUpdate_product_attributes { @@ -44,11 +58,18 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes { values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[]; } +export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values_file | null; } export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts index cc5993ff3..30acab46a 100644 --- a/src/products/types/ProductList.ts +++ b/src/products/types/ProductList.ts @@ -76,10 +76,18 @@ export interface ProductList_products_edges_node_attributes_attribute { id: string; } +export interface ProductList_products_edges_node_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductList_products_edges_node_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; + slug: string | null; + file: ProductList_products_edges_node_attributes_values_file | null; } export interface ProductList_products_edges_node_attributes { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index e10d20cd1..45a2ee585 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -15,11 +15,18 @@ export interface ProductUpdate_productUpdate_errors { attributes: string[] | null; } +export interface ProductUpdate_productUpdate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductUpdate_productUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductUpdate_productUpdate_product_attributes_attribute_values_file | null; } export interface ProductUpdate_productUpdate_product_attributes_attribute { @@ -32,11 +39,18 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute { values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } +export interface ProductUpdate_productUpdate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductUpdate_productUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductUpdate_productUpdate_product_attributes_values_file | null; } export interface ProductUpdate_productUpdate_product_attributes { @@ -45,11 +59,18 @@ export interface ProductUpdate_productUpdate_product_attributes { values: (ProductUpdate_productUpdate_product_attributes_values | null)[]; } +export interface ProductUpdate_productUpdate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductUpdate_productUpdate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductUpdate_productUpdate_product_productType_variantAttributes_values_file | null; } export interface ProductUpdate_productUpdate_product_productType_variantAttributes { diff --git a/src/products/types/ProductVariantChannelListingUpdate.ts b/src/products/types/ProductVariantChannelListingUpdate.ts index 1041e7cd4..1981f7721 100644 --- a/src/products/types/ProductVariantChannelListingUpdate.ts +++ b/src/products/types/ProductVariantChannelListingUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductVariantChannelListingAddInput, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; +import { ProductVariantChannelListingAddInput, AttributeInputTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantChannelListingUpdate @@ -20,33 +20,92 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing value: string; } -export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute_values { +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values_file | null; } -export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute { +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute_values | null)[] | null; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values | null)[] | null; } -export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_values { +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_values_file | null; } -export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes { +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute; - values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_values | null)[]; + attribute: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_values | null)[]; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_values_file | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_values | null)[]; } export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_images { @@ -195,7 +254,8 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing id: string; metadata: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_metadata | null)[]; privateMetadata: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_privateMetadata | null)[]; - attributes: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes[]; + selectionAttributes: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes[]; + nonSelectionAttributes: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes[]; images: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_images | null)[] | null; name: string; product: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product; diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 55a8638d1..5967f7729 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL query operation: ProductVariantCreateData // ==================================================== @@ -25,26 +27,59 @@ export interface ProductVariantCreateData_product_channelListings { channel: ProductVariantCreateData_product_channelListings_channel; } -export interface ProductVariantCreateData_product_productType_variantAttributes_values { +export interface ProductVariantCreateData_product_productType_selectionVariantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantCreateData_product_productType_selectionVariantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantCreateData_product_productType_selectionVariantAttributes_values_file | null; } -export interface ProductVariantCreateData_product_productType_variantAttributes { +export interface ProductVariantCreateData_product_productType_selectionVariantAttributes { __typename: "Attribute"; id: string; - slug: string | null; name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (ProductVariantCreateData_product_productType_variantAttributes_values | null)[] | null; + values: (ProductVariantCreateData_product_productType_selectionVariantAttributes_values | null)[] | null; +} + +export interface ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values_file | null; +} + +export interface ProductVariantCreateData_product_productType_nonSelectionVariantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values | null)[] | null; } export interface ProductVariantCreateData_product_productType { __typename: "ProductType"; id: string; - variantAttributes: (ProductVariantCreateData_product_productType_variantAttributes | null)[] | null; + selectionVariantAttributes: (ProductVariantCreateData_product_productType_selectionVariantAttributes | null)[] | null; + nonSelectionVariantAttributes: (ProductVariantCreateData_product_productType_nonSelectionVariantAttributes | null)[] | null; } export interface ProductVariantCreateData_product_thumbnail { diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index 132ffc40f..a54c3baea 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductVariantDetails @@ -20,33 +20,92 @@ export interface ProductVariantDetails_productVariant_privateMetadata { value: string; } -export interface ProductVariantDetails_productVariant_attributes_attribute_values { +export interface ProductVariantDetails_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantDetails_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantDetails_productVariant_selectionAttributes_attribute_values_file | null; } -export interface ProductVariantDetails_productVariant_attributes_attribute { +export interface ProductVariantDetails_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (ProductVariantDetails_productVariant_attributes_attribute_values | null)[] | null; + values: (ProductVariantDetails_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface ProductVariantDetails_productVariant_attributes_values { +export interface ProductVariantDetails_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantDetails_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantDetails_productVariant_selectionAttributes_values_file | null; } -export interface ProductVariantDetails_productVariant_attributes { +export interface ProductVariantDetails_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: ProductVariantDetails_productVariant_attributes_attribute; - values: (ProductVariantDetails_productVariant_attributes_values | null)[]; + attribute: ProductVariantDetails_productVariant_selectionAttributes_attribute; + values: (ProductVariantDetails_productVariant_selectionAttributes_values | null)[]; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: ProductVariantDetails_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface ProductVariantDetails_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: ProductVariantDetails_productVariant_nonSelectionAttributes_attribute; + values: (ProductVariantDetails_productVariant_nonSelectionAttributes_values | null)[]; } export interface ProductVariantDetails_productVariant_images { @@ -195,7 +254,8 @@ export interface ProductVariantDetails_productVariant { id: string; metadata: (ProductVariantDetails_productVariant_metadata | null)[]; privateMetadata: (ProductVariantDetails_productVariant_privateMetadata | null)[]; - attributes: ProductVariantDetails_productVariant_attributes[]; + selectionAttributes: ProductVariantDetails_productVariant_selectionAttributes[]; + nonSelectionAttributes: ProductVariantDetails_productVariant_nonSelectionAttributes[]; images: (ProductVariantDetails_productVariant_images | null)[] | null; name: string; product: ProductVariantDetails_productVariant_product; diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index 53e2a1deb..53894d18a 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -14,11 +14,18 @@ export interface ProductVariantReorder_productVariantReorder_errors { field: string | null; } +export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantReorder_productVariantReorder_product_attributes_attribute_values_file | null; } export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute { @@ -31,11 +38,18 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes_ values: (ProductVariantReorder_productVariantReorder_product_attributes_attribute_values | null)[] | null; } +export interface ProductVariantReorder_productVariantReorder_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantReorder_productVariantReorder_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantReorder_productVariantReorder_product_attributes_values_file | null; } export interface ProductVariantReorder_productVariantReorder_product_attributes { @@ -44,11 +58,18 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes values: (ProductVariantReorder_productVariantReorder_product_attributes_values | null)[]; } +export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values_file | null; } export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes { diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index dd94f3d0e..732258c5c 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -14,11 +14,18 @@ export interface ProductVariantSetDefault_productVariantSetDefault_errors { field: string | null; } +export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values_file | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute { @@ -31,11 +38,18 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri values: (ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values | null)[] | null; } +export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantSetDefault_productVariantSetDefault_product_attributes_values_file | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_attributes { @@ -44,11 +58,18 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri values: (ProductVariantSetDefault_productVariantSetDefault_product_attributes_values | null)[]; } +export interface ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes_values_file | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_productType_variantAttributes { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 3fe444359..714bc782a 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -15,11 +15,18 @@ export interface SimpleProductUpdate_productUpdate_errors { attributes: string[] | null; } +export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productUpdate_product_attributes_attribute_values_file | null; } export interface SimpleProductUpdate_productUpdate_product_attributes_attribute { @@ -32,11 +39,18 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } +export interface SimpleProductUpdate_productUpdate_product_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface SimpleProductUpdate_productUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productUpdate_product_attributes_values_file | null; } export interface SimpleProductUpdate_productUpdate_product_attributes { @@ -45,11 +59,18 @@ export interface SimpleProductUpdate_productUpdate_product_attributes { values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[]; } +export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values_file | null; } export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes { @@ -275,33 +296,92 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_private value: string; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute_values { +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute { +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute_values | null)[] | null; + values: (SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes_values { +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file | null; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes { +export interface SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute; - values: (SimpleProductUpdate_productVariantUpdate_productVariant_attributes_values | null)[]; + attribute: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_values | null)[]; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values | null)[]; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_images { @@ -450,7 +530,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant { id: string; metadata: (SimpleProductUpdate_productVariantUpdate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantUpdate_productVariant_privateMetadata | null)[]; - attributes: SimpleProductUpdate_productVariantUpdate_productVariant_attributes[]; + selectionAttributes: SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes[]; + nonSelectionAttributes: SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes[]; images: (SimpleProductUpdate_productVariantUpdate_productVariant_images | null)[] | null; name: string; product: SimpleProductUpdate_productVariantUpdate_productVariant_product; @@ -486,33 +567,92 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_p value: string; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute_values { +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute { +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute_values | null)[] | null; + values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_values { +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes { +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute; - values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_values | null)[]; + attribute: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_values | null)[]; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_values | null)[]; } export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_images { @@ -661,7 +801,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant { id: string; metadata: (SimpleProductUpdate_productVariantStocksCreate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksCreate_productVariant_privateMetadata | null)[]; - attributes: SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes[]; + selectionAttributes: SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes[]; + nonSelectionAttributes: SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes[]; images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_images | null)[] | null; name: string; product: SimpleProductUpdate_productVariantStocksCreate_productVariant_product; @@ -696,33 +837,92 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_p value: string; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute_values { +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute { +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute_values | null)[] | null; + values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_values { +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes { +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute; - values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_values | null)[]; + attribute: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_values | null)[]; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_values | null)[]; } export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_images { @@ -871,7 +1071,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant { id: string; metadata: (SimpleProductUpdate_productVariantStocksDelete_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksDelete_productVariant_privateMetadata | null)[]; - attributes: SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes[]; + selectionAttributes: SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes[]; + nonSelectionAttributes: SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes[]; images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_images | null)[] | null; name: string; product: SimpleProductUpdate_productVariantStocksDelete_productVariant_product; @@ -907,33 +1108,92 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_p value: string; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values { +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute { +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values | null)[] | null; + values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_values { +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file | null; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes { +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute; - values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_values | null)[]; + attribute: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values | null)[]; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute; + values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values | null)[]; } export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_images { @@ -1082,7 +1342,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant { id: string; metadata: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_privateMetadata | null)[]; - attributes: SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes[]; + selectionAttributes: SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes[]; + nonSelectionAttributes: SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes[]; images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; name: string; product: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product; diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index e3605b6fe..7bda40386 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductVariantCreateInput, ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductVariantCreateInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantCreate @@ -27,33 +27,92 @@ export interface VariantCreate_productVariantCreate_productVariant_privateMetada value: string; } -export interface VariantCreate_productVariantCreate_productVariant_attributes_attribute_values { +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface VariantCreate_productVariantCreate_productVariant_attributes_attribute { +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (VariantCreate_productVariantCreate_productVariant_attributes_attribute_values | null)[] | null; + values: (VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface VariantCreate_productVariantCreate_productVariant_attributes_values { +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantCreate_productVariantCreate_productVariant_selectionAttributes_values_file | null; } -export interface VariantCreate_productVariantCreate_productVariant_attributes { +export interface VariantCreate_productVariantCreate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: VariantCreate_productVariantCreate_productVariant_attributes_attribute; - values: (VariantCreate_productVariantCreate_productVariant_attributes_values | null)[]; + attribute: VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute; + values: (VariantCreate_productVariantCreate_productVariant_selectionAttributes_values | null)[]; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute; + values: (VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_values | null)[]; } export interface VariantCreate_productVariantCreate_productVariant_images { @@ -202,7 +261,8 @@ export interface VariantCreate_productVariantCreate_productVariant { id: string; metadata: (VariantCreate_productVariantCreate_productVariant_metadata | null)[]; privateMetadata: (VariantCreate_productVariantCreate_productVariant_privateMetadata | null)[]; - attributes: VariantCreate_productVariantCreate_productVariant_attributes[]; + selectionAttributes: VariantCreate_productVariantCreate_productVariant_selectionAttributes[]; + nonSelectionAttributes: VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes[]; images: (VariantCreate_productVariantCreate_productVariant_images | null)[] | null; name: string; product: VariantCreate_productVariantCreate_productVariant_product; diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index 278b8c467..1ae3fce63 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantImageAssign @@ -26,33 +26,92 @@ export interface VariantImageAssign_variantImageAssign_productVariant_privateMet value: string; } -export interface VariantImageAssign_variantImageAssign_productVariant_attributes_attribute_values { +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values_file | null; } -export interface VariantImageAssign_variantImageAssign_productVariant_attributes_attribute { +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (VariantImageAssign_variantImageAssign_productVariant_attributes_attribute_values | null)[] | null; + values: (VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface VariantImageAssign_variantImageAssign_productVariant_attributes_values { +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_values_file | null; } -export interface VariantImageAssign_variantImageAssign_productVariant_attributes { +export interface VariantImageAssign_variantImageAssign_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: VariantImageAssign_variantImageAssign_productVariant_attributes_attribute; - values: (VariantImageAssign_variantImageAssign_productVariant_attributes_values | null)[]; + attribute: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_attribute; + values: (VariantImageAssign_variantImageAssign_productVariant_selectionAttributes_values | null)[]; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_attribute; + values: (VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes_values | null)[]; } export interface VariantImageAssign_variantImageAssign_productVariant_images { @@ -201,7 +260,8 @@ export interface VariantImageAssign_variantImageAssign_productVariant { id: string; metadata: (VariantImageAssign_variantImageAssign_productVariant_metadata | null)[]; privateMetadata: (VariantImageAssign_variantImageAssign_productVariant_privateMetadata | null)[]; - attributes: VariantImageAssign_variantImageAssign_productVariant_attributes[]; + selectionAttributes: VariantImageAssign_variantImageAssign_productVariant_selectionAttributes[]; + nonSelectionAttributes: VariantImageAssign_variantImageAssign_productVariant_nonSelectionAttributes[]; images: (VariantImageAssign_variantImageAssign_productVariant_images | null)[] | null; name: string; product: VariantImageAssign_variantImageAssign_productVariant_product; diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index b480a6912..12b186416 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantImageUnassign @@ -26,33 +26,92 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_privat value: string; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute_values { +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values_file | null; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute { +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute_values | null)[] | null; + values: (VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes_values { +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_values_file | null; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes { +export interface VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute; - values: (VariantImageUnassign_variantImageUnassign_productVariant_attributes_values | null)[]; + attribute: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_attribute; + values: (VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes_values | null)[]; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_attribute; + values: (VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes_values | null)[]; } export interface VariantImageUnassign_variantImageUnassign_productVariant_images { @@ -201,7 +260,8 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant { id: string; metadata: (VariantImageUnassign_variantImageUnassign_productVariant_metadata | null)[]; privateMetadata: (VariantImageUnassign_variantImageUnassign_productVariant_privateMetadata | null)[]; - attributes: VariantImageUnassign_variantImageUnassign_productVariant_attributes[]; + selectionAttributes: VariantImageUnassign_variantImageUnassign_productVariant_selectionAttributes[]; + nonSelectionAttributes: VariantImageUnassign_variantImageUnassign_productVariant_nonSelectionAttributes[]; images: (VariantImageUnassign_variantImageUnassign_productVariant_images | null)[] | null; name: string; product: VariantImageUnassign_variantImageUnassign_productVariant_product; diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index a0ddb1769..2696d52e3 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { StockInput, AttributeValueInput, ProductErrorCode, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; +import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantUpdate @@ -27,33 +27,92 @@ export interface VariantUpdate_productVariantUpdate_productVariant_privateMetada value: string; } -export interface VariantUpdate_productVariantUpdate_productVariant_attributes_attribute_values { +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface VariantUpdate_productVariantUpdate_productVariant_attributes_attribute { +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (VariantUpdate_productVariantUpdate_productVariant_attributes_attribute_values | null)[] | null; + values: (VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface VariantUpdate_productVariantUpdate_productVariant_attributes_values { +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_values_file | null; } -export interface VariantUpdate_productVariantUpdate_productVariant_attributes { +export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: VariantUpdate_productVariantUpdate_productVariant_attributes_attribute; - values: (VariantUpdate_productVariantUpdate_productVariant_attributes_values | null)[]; + attribute: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute; + values: (VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_values | null)[]; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute; + values: (VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_values | null)[]; } export interface VariantUpdate_productVariantUpdate_productVariant_images { @@ -202,7 +261,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant { id: string; metadata: (VariantUpdate_productVariantUpdate_productVariant_metadata | null)[]; privateMetadata: (VariantUpdate_productVariantUpdate_productVariant_privateMetadata | null)[]; - attributes: VariantUpdate_productVariantUpdate_productVariant_attributes[]; + selectionAttributes: VariantUpdate_productVariantUpdate_productVariant_selectionAttributes[]; + nonSelectionAttributes: VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes[]; images: (VariantUpdate_productVariantUpdate_productVariant_images | null)[] | null; name: string; product: VariantUpdate_productVariantUpdate_productVariant_product; @@ -238,33 +298,92 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_private value: string; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values { +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values_file | null; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute { +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute { __typename: "Attribute"; id: string; name: string | null; slug: string | null; + inputType: AttributeInputTypeEnum | null; valueRequired: boolean; - values: (VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values | null)[] | null; + values: (VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_values { +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values_file | null; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes { +export interface VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes { __typename: "SelectedAttribute"; - attribute: VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute; - values: (VariantUpdate_productVariantStocksUpdate_productVariant_attributes_values | null)[]; + attribute: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute; + values: (VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_values | null)[]; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values_file | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + file: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values_file | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes { + __typename: "SelectedAttribute"; + attribute: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute; + values: (VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_values | null)[]; } export interface VariantUpdate_productVariantStocksUpdate_productVariant_images { @@ -413,7 +532,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant { id: string; metadata: (VariantUpdate_productVariantStocksUpdate_productVariant_metadata | null)[]; privateMetadata: (VariantUpdate_productVariantStocksUpdate_productVariant_privateMetadata | null)[]; - attributes: VariantUpdate_productVariantStocksUpdate_productVariant_attributes[]; + selectionAttributes: VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes[]; + nonSelectionAttributes: VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes[]; images: (VariantUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; name: string; product: VariantUpdate_productVariantStocksUpdate_productVariant_product; diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 6e12826ba..98717ec8c 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -1,9 +1,15 @@ import { ChannelData } from "@saleor/channels/utils"; +import { + AttributeInput, + VariantAttributeScope +} from "@saleor/components/Attributes"; import { MetadataFormData } from "@saleor/components/Metadata/types"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; -import { FormsetAtomicData } from "@saleor/hooks/useFormset"; +import { SelectedVariantAttributeFragment } from "@saleor/fragments/types/SelectedVariantAttributeFragment"; +import { VariantAttributeFragment } from "@saleor/fragments/types/VariantAttributeFragment"; +import { FormsetAtomicData, FormsetData } from "@saleor/hooks/useFormset"; import { maybe } from "@saleor/misc"; import { ProductDetails_product, @@ -14,9 +20,7 @@ import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/ import { StockInput } from "@saleor/types/globalTypes"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; -import { ProductAttributeInput } from "../components/ProductAttributes"; import { ProductStockInput } from "../components/ProductStocks"; -import { VariantAttributeInput } from "../components/ProductVariantAttributes"; import { ProductVariantCreateData_product } from "../types/ProductVariantCreateData"; export interface Collection { @@ -38,13 +42,14 @@ export interface ProductType { export function getAttributeInputFromProduct( product: ProductDetails_product -): ProductAttributeInput[] { +): AttributeInput[] { return maybe( - (): ProductAttributeInput[] => + (): AttributeInput[] => product.attributes.map(attribute => ({ data: { inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, values: attribute.attribute.values }, id: attribute.attribute.id, @@ -77,7 +82,7 @@ export function getSelectedAttributesFromProduct( export function getAttributeInputFromProductType( productType: ProductType -): ProductAttributeInput[] { +): AttributeInput[] { return productType.productAttributes.map(attribute => ({ data: { inputType: attribute.inputType, @@ -90,20 +95,73 @@ export function getAttributeInputFromProductType( })); } +export function getAttributeInputFromAttributes( + variantAttributes: VariantAttributeFragment[], + variantAttributeScope: VariantAttributeScope +): AttributeInput[] { + return variantAttributes?.map(attribute => ({ + data: { + inputType: attribute.inputType, + isRequired: attribute.valueRequired, + values: attribute.values, + variantAttributeScope + }, + id: attribute.id, + label: attribute.name, + value: [""] + })); +} + +export function getAttributeInputFromSelectedAttributes( + variantAttributes: SelectedVariantAttributeFragment[], + variantAttributeScope: VariantAttributeScope +): AttributeInput[] { + return variantAttributes?.map(attribute => ({ + data: { + inputType: attribute.attribute.inputType, + isRequired: attribute.attribute.valueRequired, + selectedValues: attribute.values, + values: attribute.attribute.values, + variantAttributeScope + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: [(attribute.values.length && attribute.values[0]?.slug) || null] + })); +} + export function getAttributeInputFromVariant( variant: ProductVariant -): VariantAttributeInput[] { - return maybe( - (): VariantAttributeInput[] => - variant.attributes.map(attribute => ({ - data: { - values: attribute.attribute.values - }, - id: attribute.attribute.id, - label: attribute.attribute.name, - value: maybe(() => attribute.values[0].slug, null) - })), - [] +): AttributeInput[] { + const selectionAttributeInput = getAttributeInputFromSelectedAttributes( + variant?.selectionAttributes, + VariantAttributeScope.VARIANT_SELECTION + ); + const nonSelectionAttributeInput = getAttributeInputFromSelectedAttributes( + variant?.nonSelectionAttributes, + VariantAttributeScope.NOT_VARIANT_SELECTION + ); + + return ( + selectionAttributeInput?.concat(nonSelectionAttributeInput ?? []) ?? [] + ); +} + +export function getVariantAttributeInputFromProduct( + product: ProductVariantCreateData_product +): AttributeInput[] { + const selectionAttributeInput = getAttributeInputFromAttributes( + product?.productType?.selectionVariantAttributes, + VariantAttributeScope.VARIANT_SELECTION + ); + + const nonSelectionAttributeInput = getAttributeInputFromAttributes( + product?.productType?.nonSelectionVariantAttributes, + VariantAttributeScope.NOT_VARIANT_SELECTION + ); + + return ( + selectionAttributeInput?.concat(nonSelectionAttributeInput ?? []) ?? [] ); } @@ -122,19 +180,6 @@ export function getStockInputFromVariant( ); } -export function getVariantAttributeInputFromProduct( - product: ProductVariantCreateData_product -): VariantAttributeInput[] { - return product?.productType?.variantAttributes?.map(attribute => ({ - data: { - values: attribute.values - }, - id: attribute.id, - label: attribute.name, - value: "" - })); -} - export function getStockInputFromProduct( product: ProductDetails_product ): ProductStockInput[] { @@ -172,6 +217,26 @@ export function getChoices(nodes: Node[]): SingleAutocompleteChoiceType[] { ); } +export const getAttributesDisplayData = ( + attributes: AttributeInput[], + attributesWithNewFileValue: FormsetData +) => + attributes.map(attribute => { + const attributeWithNewFileValue = attributesWithNewFileValue.find( + attributeWithNewFile => attribute.id === attributeWithNewFile.id + ); + + if (attributeWithNewFileValue) { + return { + ...attribute, + value: attributeWithNewFileValue?.value?.name + ? [attributeWithNewFileValue.value.name] + : [] + }; + } + return attribute; + }); + export interface ProductUpdatePageFormData extends MetadataFormData { category: string | null; changeTaxCode: boolean; diff --git a/src/products/utils/handlers.test.ts b/src/products/utils/handlers.test.ts index ccc1690a9..c4309f937 100644 --- a/src/products/utils/handlers.test.ts +++ b/src/products/utils/handlers.test.ts @@ -1,10 +1,10 @@ +import { AttributeInputData } from "@saleor/components/Attributes"; import { FormsetData } from "@saleor/hooks/useFormset"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; -import { ProductAttributeInputData } from "../components/ProductAttributes"; import { createAttributeMultiChangeHandler } from "./handlers"; -const attributes: FormsetData = [ +const attributes: FormsetData = [ { data: { inputType: AttributeInputTypeEnum.DROPDOWN, @@ -12,6 +12,7 @@ const attributes: FormsetData = [ values: [ { __typename: "AttributeValue", + file: null, id: "attrv-1", name: "Attribute 1 Value 1", slug: "attr-1-v-1" @@ -29,18 +30,21 @@ const attributes: FormsetData = [ values: [ { __typename: "AttributeValue", + file: null, id: "attrv-2", name: "Attribute 2 Value 1", slug: "attr-2-v-1" }, { __typename: "AttributeValue", + file: null, id: "attrv-3", name: "Attribute 2 Value 2", slug: "attr-2-v-2" }, { __typename: "AttributeValue", + file: null, id: "attrv-4", name: "Attribute 2 Value 3", slug: "attr-2-v-3" @@ -50,6 +54,28 @@ const attributes: FormsetData = [ id: "attr-2", label: "Attribute 2", value: ["attr-2-v-3"] + }, + { + data: { + inputType: AttributeInputTypeEnum.FILE, + isRequired: false, + values: [ + { + __typename: "AttributeValue", + file: { + __typename: "File", + contentType: "image/png", + url: "some-non-existing-url" + }, + id: "attrv-5", + name: "File First Value", + slug: "file-first-value" + } + ] + }, + id: "attr-3", + label: "File Attribute", + value: [] } ]; diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts index 866e5813e..a07f440d5 100644 --- a/src/products/utils/handlers.ts +++ b/src/products/utils/handlers.ts @@ -3,11 +3,15 @@ import { ChannelPriceArgs, ChannelPriceData } from "@saleor/channels/utils"; +import { AttributeInputData } from "@saleor/components/Attributes"; import { FormChange } from "@saleor/hooks/useForm"; -import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; +import { + FormsetAtomicData, + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; import { toggle } from "@saleor/utils/lists"; -import { ProductAttributeInputData } from "../components/ProductAttributes"; import { getAttributeInputFromProductType, ProductType } from "./data"; export function createAttributeChangeHandler( @@ -101,7 +105,7 @@ export function createVariantChannelsChangeHandler( export function createAttributeMultiChangeHandler( changeAttributeData: FormsetChange, - attributes: FormsetData, + attributes: FormsetData, triggerChange: () => void ): FormsetChange { return (attributeId: string, value: string) => { @@ -120,8 +124,37 @@ export function createAttributeMultiChangeHandler( }; } +export function createAttributeFileChangeHandler( + changeAttributeData: FormsetChange, + attributesWithNewFileValue: FormsetData>, + addAttributeNewFileValue: (data: FormsetAtomicData) => void, + changeAttributeNewFileValue: FormsetChange, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: File) => { + triggerChange(); + + const newFileValueAssigned = attributesWithNewFileValue.find( + attribute => attribute.id === attributeId + ); + + if (newFileValueAssigned) { + changeAttributeNewFileValue(attributeId, value); + } else { + addAttributeNewFileValue({ + data: null, + id: attributeId, + label: null, + value + }); + } + + changeAttributeData(attributeId, value ? [value.name] : []); + }; +} + export function createProductTypeSelectHandler( - setAttributes: (data: FormsetData) => void, + setAttributes: (data: FormsetData) => void, setProductType: (productType: ProductType) => void, productTypeChoiceList: ProductType[], triggerChange: () => void diff --git a/src/products/views/ProductCreate/ProductCreate.tsx b/src/products/views/ProductCreate/ProductCreate.tsx index c19b87e70..a8e49afd4 100644 --- a/src/products/views/ProductCreate/ProductCreate.tsx +++ b/src/products/views/ProductCreate/ProductCreate.tsx @@ -4,6 +4,7 @@ import { ChannelData, createSortedChannelsData } from "@saleor/channels/utils"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { useFileUploadMutation } from "@saleor/files/mutations"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; @@ -124,6 +125,8 @@ export const ProductCreateView: React.FC = ({ params }) => { navigate(productUrl(productId)); }; + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + const [updateChannels, updateChannelsOpts] = useProductChannelListingUpdate( {} ); @@ -157,6 +160,7 @@ export const ProductCreateView: React.FC = ({ params }) => { const result = await createMetadataCreateHandler( createHandler( productTypes, + variables => uploadFile({ variables }), variables => productCreate({ variables }), variables => productVariantCreate({ variables }), updateChannels, @@ -214,7 +218,8 @@ export const ProductCreateView: React.FC = ({ params }) => { collections={(searchCollectionOpts?.data?.search?.edges || []).map( edge => edge.node )} - disabled={ + loading={ + uploadFileOpts.loading || productCreateOpts.loading || productVariantCreateOpts.loading || updateChannelsOpts.loading || diff --git a/src/products/views/ProductCreate/handlers.ts b/src/products/views/ProductCreate/handlers.ts index 2b1f20577..ad7f7233f 100644 --- a/src/products/views/ProductCreate/handlers.ts +++ b/src/products/views/ProductCreate/handlers.ts @@ -1,4 +1,18 @@ +import { + getAttributesAfterFileAttributesUpdate, + mergeFileUploadErrors +} from "@saleor/attributes/utils/data"; +import { + handleUploadMultipleFiles, + prepareAttributesInput +} from "@saleor/attributes/utils/handlers"; import { ChannelData } from "@saleor/channels/utils"; +import { + FileUpload, + FileUploadVariables +} from "@saleor/files/types/FileUpload"; +import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; +import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment"; import { weight } from "@saleor/misc"; import { ProductCreateData } from "@saleor/products/components/ProductCreatePage/form"; import { @@ -52,6 +66,9 @@ const getSimpleProductVariables = ( export function createHandler( productTypes: SearchProductTypes_search_edges_node[], + uploadFile: ( + variables: FileUploadVariables + ) => Promise>, productCreate: ( variables: ProductCreateVariables ) => Promise>, @@ -69,12 +86,25 @@ export function createHandler( }) => Promise> ) { return async (formData: ProductCreateData) => { + let errors: Array = []; + + const uploadFilesResult = await handleUploadMultipleFiles( + formData.attributesWithNewFileValue, + uploadFile + ); + + errors = [...errors, ...mergeFileUploadErrors(uploadFilesResult)]; + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + formData.attributesWithNewFileValue, + uploadFilesResult + ); + const productVariables: ProductCreateVariables = { input: { - attributes: formData.attributes.map(attribute => ({ - id: attribute.id, - values: attribute.value - })), + attributes: prepareAttributesInput({ + attributes: formData.attributes, + updatedFileAttributes + }), category: formData.category, chargeTaxes: formData.chargeTaxes, collections: formData.collections, @@ -93,12 +123,16 @@ export function createHandler( }; const result = await productCreate(productVariables); - let hasErrors = false; + let hasErrors = errors.length > 0; const hasVariants = productTypes.find( product => product.id === formData.productType.id ).hasVariants; - const productId = result.data.productCreate.product.id; + const productId = result.data.productCreate.product?.id; + + if (!productId) { + return null; + } if (!hasVariants) { const result = await Promise.all([ @@ -142,7 +176,7 @@ export function createHandler( A more robust solution would require merging create and update form into one to persist form state across redirects */ if (productId && hasErrors) { - productDelete({ variables: { id: productId } }); + await productDelete({ variables: { id: productId } }); return null; } diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 983657c89..9b1d67c02 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -2,6 +2,7 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import { useAttributeValueDeleteMutation } from "@saleor/attributes/mutations"; import { useChannelsList } from "@saleor/channels/queries"; import { ChannelData, @@ -14,6 +15,7 @@ import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityD import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { useFileUploadMutation } from "@saleor/files/mutations"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -117,6 +119,8 @@ export const ProductUpdate: React.FC = ({ id, params }) => { }); const { channel } = useAppChannel(); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + const handleUpdate = (data: ProductUpdateMutationResult) => { if (data.productUpdate.errors.length === 0) { notify({ @@ -243,6 +247,11 @@ export const ProductUpdate: React.FC = ({ id, params }) => { value: listing.channel.id })); + const [ + deleteAttributeValue, + deleteAttributeValueOpts + ] = useAttributeValueDeleteMutation({}); + const handleBack = () => navigate(productListUrl()); if (product === null) { @@ -259,11 +268,13 @@ export const ProductUpdate: React.FC = ({ id, params }) => { product, createUpdateHandler( product, + variables => uploadFile({ variables }), variables => updateProduct({ variables }), variables => updateSimpleProduct({ variables }), updateChannels, updateVariantChannels, - productVariantCreate + productVariantCreate, + variables => deleteAttributeValue({ variables }) ), variables => updateMetadata({ variables }), variables => updatePrivateMetadata({ variables }) @@ -286,6 +297,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { ); const disableFormSave = + uploadFileOpts.loading || createProductImageOpts.loading || deleteProductOpts.loading || reorderProductImagesOpts.loading || @@ -294,6 +306,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { updateChannelsOpts.loading || updateVariantChannelsOpts.loading || productVariantCreateOpts.loading || + deleteAttributeValueOpts.loading || loading; const formTransitionState = getMutationState( diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index 50f0b26d9..7a9c3d7fb 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -1,8 +1,28 @@ +import { + AttributeValueDelete, + AttributeValueDeleteVariables +} from "@saleor/attributes/types/AttributeValueDelete"; +import { + getAttributesAfterFileAttributesUpdate, + mergeAttributeValueDeleteErrors, + mergeFileUploadErrors +} from "@saleor/attributes/utils/data"; +import { + handleDeleteMultipleAttributeValues, + handleUploadMultipleFiles, + prepareAttributesInput +} from "@saleor/attributes/utils/handlers"; import { createSortedChannelsDataFromProduct } from "@saleor/channels/utils"; +import { + FileUpload, + FileUploadVariables +} from "@saleor/files/types/FileUpload"; +import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; import { BulkStockErrorFragment } from "@saleor/fragments/types/BulkStockErrorFragment"; import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { StockErrorFragment } from "@saleor/fragments/types/StockErrorFragment"; +import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment"; import { weight } from "@saleor/misc"; import { ProductUpdatePageSubmitData } from "@saleor/products/components/ProductUpdatePage"; import { @@ -42,6 +62,15 @@ import { diff } from "fast-array-diff"; import { MutationFetchResult } from "react-apollo"; import { arrayMove } from "react-sortable-hoc"; +type SubmitErrors = Array< + | ProductErrorFragment + | StockErrorFragment + | BulkStockErrorFragment + | AttributeErrorFragment + | UploadErrorFragment + | ProductChannelListingErrorFragment +>; + const getSimpleProductVariables = ( productVariables: ProductUpdateVariables, data: ProductUpdatePageSubmitData, @@ -100,6 +129,9 @@ const getVariantChannelsInput = (data: ProductUpdatePageSubmitData) => export function createUpdateHandler( product: ProductDetails_product, + uploadFile: ( + variables: FileUploadVariables + ) => Promise>, updateProduct: ( variables: ProductUpdateVariables ) => Promise>, @@ -114,16 +146,42 @@ export function createUpdateHandler( }) => Promise>, productVariantCreate: (options: { variables: VariantCreateVariables; - }) => Promise> + }) => Promise>, + deleteAttributeValue: ( + variables: AttributeValueDeleteVariables + ) => Promise> ) { return async (data: ProductUpdatePageSubmitData) => { + let errors: SubmitErrors = []; + + const uploadFilesResult = await handleUploadMultipleFiles( + data.attributesWithNewFileValue, + uploadFile + ); + + const deleteAttributeValuesResult = await handleDeleteMultipleAttributeValues( + data.attributesWithNewFileValue, + product?.attributes, + deleteAttributeValue + ); + + errors = [ + ...errors, + ...mergeFileUploadErrors(uploadFilesResult), + ...mergeAttributeValueDeleteErrors(deleteAttributeValuesResult) + ]; + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + data.attributesWithNewFileValue, + uploadFilesResult + ); + const productVariables: ProductUpdateVariables = { id: product.id, input: { - attributes: data.attributes.map(attribute => ({ - id: attribute.id, - values: attribute.value[0] === "" ? [] : attribute.value - })), + attributes: prepareAttributesInput({ + attributes: data.attributes, + updatedFileAttributes + }), category: data.category, chargeTaxes: data.chargeTaxes, collections: data.collections, @@ -139,18 +197,11 @@ export function createUpdateHandler( } }; - let errors: Array< - | ProductErrorFragment - | StockErrorFragment - | BulkStockErrorFragment - | ProductChannelListingErrorFragment - >; - if (product.productType.hasVariants) { const result = await updateProduct(productVariables); - errors = result.data.productUpdate.errors; + errors = [...errors, ...result.data.productUpdate.errors]; - updateChannels({ + await updateChannels({ variables: getChannelsVariables(data, product) }); } else { @@ -169,7 +220,10 @@ export function createUpdateHandler( } } }); - errors = productVariantResult.data.productVariantCreate.errors; + errors = [ + ...errors, + ...productVariantResult.data.productVariantCreate.errors + ]; const variantId = productVariantResult.data.productVariantCreate?.productVariant?.id; @@ -180,7 +234,7 @@ export function createUpdateHandler( input: getVariantChannelsInput(data) } }); - updateChannels({ + await updateChannels({ variables: getChannelsVariables(data, product) }); const result = await updateSimpleProduct( @@ -196,7 +250,7 @@ export function createUpdateHandler( product.variants[0].id ) ); - errors = getSimpleProductErrors(result.data); + errors = [...errors, ...getSimpleProductErrors(result.data)]; await updateChannels({ variables: getChannelsVariables(data, product) diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index f383700ae..2aa1f5bf7 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,7 +1,19 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; +import { useAttributeValueDeleteMutation } from "@saleor/attributes/mutations"; +import { + getAttributesAfterFileAttributesUpdate, + mergeAttributeValueDeleteErrors, + mergeFileUploadErrors +} from "@saleor/attributes/utils/data"; +import { + handleDeleteMultipleAttributeValues, + handleUploadMultipleFiles, + prepareAttributesInput +} from "@saleor/attributes/utils/handlers"; import { createVariantChannels } from "@saleor/channels/utils"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import { useFileUploadMutation } from "@saleor/files/mutations"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useOnSetDefaultVariant from "@saleor/hooks/useOnSetDefaultVariant"; @@ -97,6 +109,8 @@ export const ProductVariant: React.FC = ({ const handleBack = () => navigate(productUrl(productId)); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + const [assignImage, assignImageOpts] = useVariantImageAssignMutation({}); const [unassignImage, unassignImageOpts] = useVariantImageUnassignMutation( {} @@ -124,7 +138,12 @@ export const ProductVariant: React.FC = ({ } }); - const handleSubmitChannels = ( + const [ + deleteAttributeValue, + deleteAttributeValueOpts + ] = useAttributeValueDeleteMutation({}); + + const handleSubmitChannels = async ( data: ProductVariantUpdateSubmitData, variant: ProductVariantDetails_productVariant ) => { @@ -138,7 +157,7 @@ export const ProductVariant: React.FC = ({ ); }); if (isChannelPriceChange) { - updateChannels({ + await updateChannels({ variables: { id: variant.id, input: data.channelListings.map(listing => ({ @@ -172,11 +191,13 @@ export const ProductVariant: React.FC = ({ const disableFormSave = loading || + uploadFileOpts.loading || deleteVariantOpts.loading || updateVariantOpts.loading || assignImageOpts.loading || unassignImageOpts.loading || - reorderProductVariantsOpts.loading; + reorderProductVariantsOpts.loading || + deleteAttributeValueOpts.loading; const handleImageSelect = (id: string) => () => { if (variant) { @@ -199,13 +220,29 @@ export const ProductVariant: React.FC = ({ }; const handleUpdate = async (data: ProductVariantUpdateSubmitData) => { + const uploadFilesResult = await handleUploadMultipleFiles( + data.attributesWithNewFileValue, + variables => uploadFile({ variables }) + ); + + const deleteAttributeValuesResult = await handleDeleteMultipleAttributeValues( + data.attributesWithNewFileValue, + variant?.nonSelectionAttributes, + variables => deleteAttributeValue({ variables }) + ); + + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + data.attributesWithNewFileValue, + uploadFilesResult + ); + const result = await updateVariant({ variables: { addStocks: data.addStocks.map(mapFormsetStockToStockInput), - attributes: data.attributes.map(attribute => ({ - id: attribute.id, - values: [attribute.value] - })), + attributes: prepareAttributesInput({ + attributes: data.attributes, + updatedFileAttributes + }), id: variantId, removeStocks: data.removeStocks, sku: data.sku, @@ -214,9 +251,11 @@ export const ProductVariant: React.FC = ({ weight: weight(data.weight) } }); - handleSubmitChannels(data, variant); + await handleSubmitChannels(data, variant); return [ + ...mergeFileUploadErrors(uploadFilesResult), + ...mergeAttributeValueDeleteErrors(deleteAttributeValuesResult), ...result.data?.productVariantStocksCreate.errors, ...result.data?.productVariantStocksDelete.errors, ...result.data?.productVariantStocksUpdate.errors, @@ -255,9 +294,9 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} - onSubmit={data => { - handleSubmit(data); - handleSubmitChannels(data, variant); + onSubmit={async data => { + await handleSubmit(data); + await handleSubmitChannels(data, variant); }} onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantClick={variantId => { diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 8939631f1..1f24dd667 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -1,6 +1,12 @@ +import { getAttributesAfterFileAttributesUpdate } from "@saleor/attributes/utils/data"; +import { + handleUploadMultipleFiles, + prepareAttributesInput +} from "@saleor/attributes/utils/handlers"; import { ChannelPriceData } from "@saleor/channels/utils"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import { useFileUploadMutation } from "@saleor/files/mutations"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; @@ -65,6 +71,8 @@ export const ProductVariant: React.FC = ({ variables: { id: productId } }); + const [uploadFile, uploadFileOpts] = useFileUploadMutation({}); + const [ updateChannels, updateChannelsOpts @@ -119,15 +127,25 @@ export const ProductVariant: React.FC = ({ const handleBack = () => navigate(productUrl(productId)); const handleCreate = async (formData: ProductVariantCreateData) => { + const uploadFilesResult = await handleUploadMultipleFiles( + formData.attributesWithNewFileValue, + variables => uploadFile({ variables }) + ); + + const updatedFileAttributes = getAttributesAfterFileAttributesUpdate( + formData.attributesWithNewFileValue, + uploadFilesResult + ); + const result = await variantCreate({ variables: { input: { - attributes: formData.attributes - .filter(attribute => attribute.value !== "") - .map(attribute => ({ - id: attribute.id, - values: [attribute.value] - })), + attributes: prepareAttributesInput({ + attributes: formData.attributes.filter( + attribute => attribute.value?.length && attribute.value[0] !== "" + ), + updatedFileAttributes + }), product: productId, sku: formData.sku, stocks: formData.stocks.map(stock => ({ @@ -141,7 +159,7 @@ export const ProductVariant: React.FC = ({ }); const id = result.data?.productVariantCreate?.productVariant?.id; if (id) { - handleSubmitChannels(formData, id); + await handleSubmitChannels(formData, id); } return id || null; @@ -156,6 +174,7 @@ export const ProductVariant: React.FC = ({ const disableForm = productLoading || + uploadFileOpts.loading || variantCreateResult.loading || reorderProductVariantsOpts.loading; diff --git a/src/searches/types/SearchPageTypes.ts b/src/searches/types/SearchPageTypes.ts index b52b188fa..a2720f143 100644 --- a/src/searches/types/SearchPageTypes.ts +++ b/src/searches/types/SearchPageTypes.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL query operation: SearchPageTypes // ==================================================== +export interface SearchPageTypes_search_edges_node_attributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface SearchPageTypes_search_edges_node_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SearchPageTypes_search_edges_node_attributes_values_file | null; } export interface SearchPageTypes_search_edges_node_attributes { diff --git a/src/searches/types/SearchProductTypes.ts b/src/searches/types/SearchProductTypes.ts index 2d79b4665..2296ac29e 100644 --- a/src/searches/types/SearchProductTypes.ts +++ b/src/searches/types/SearchProductTypes.ts @@ -8,11 +8,18 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL query operation: SearchProductTypes // ==================================================== +export interface SearchProductTypes_search_edges_node_productAttributes_values_file { + __typename: "File"; + url: string; + contentType: string | null; +} + export interface SearchProductTypes_search_edges_node_productAttributes_values { __typename: "AttributeValue"; id: string; name: string | null; slug: string | null; + file: SearchProductTypes_search_edges_node_productAttributes_values_file | null; } export interface SearchProductTypes_search_edges_node_productAttributes { diff --git a/src/searches/usePageTypeSearch.ts b/src/searches/usePageTypeSearch.ts index df456e225..aef550016 100644 --- a/src/searches/usePageTypeSearch.ts +++ b/src/searches/usePageTypeSearch.ts @@ -1,3 +1,4 @@ +import { attributeValueFragment } from "@saleor/fragments/attributes"; import { pageInfoFragment } from "@saleor/fragments/pageInfo"; import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch"; import gql from "graphql-tag"; @@ -8,6 +9,7 @@ import { } from "./types/SearchPageTypes"; export const searchPageTypes = gql` + ${attributeValueFragment} ${pageInfoFragment} query SearchPageTypes($after: String, $first: Int!, $query: String!) { search: pageTypes( @@ -26,9 +28,7 @@ export const searchPageTypes = gql` name valueRequired values { - id - name - slug + ...AttributeValueFragment } } } diff --git a/src/searches/useProductTypeSearch.ts b/src/searches/useProductTypeSearch.ts index 6414b8d7d..b25c8784b 100644 --- a/src/searches/useProductTypeSearch.ts +++ b/src/searches/useProductTypeSearch.ts @@ -1,3 +1,4 @@ +import { attributeValueFragment } from "@saleor/fragments/attributes"; import { pageInfoFragment } from "@saleor/fragments/pageInfo"; import { taxTypeFragment } from "@saleor/fragments/taxes"; import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch"; @@ -9,6 +10,7 @@ import { } from "./types/SearchProductTypes"; export const searchProductTypes = gql` + ${attributeValueFragment} ${pageInfoFragment} ${taxTypeFragment} query SearchProductTypes($after: String, $first: Int!, $query: String!) { @@ -29,9 +31,7 @@ export const searchProductTypes = gql` name valueRequired values { - id - name - slug + ...AttributeValueFragment } } taxType { diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index d874fd1c6..282d2dbd5 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -24,6 +24,932 @@ exports[`Storyshots Attributes / Attribute value edit form errors 1`] = ` /> `; +exports[`Storyshots Attributes / Attributes default 1`] = ` +
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 3 Attributes +
+
+ +
+
+
+
+
+ Dropdown Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Multiselect Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ File Attribute +
+
+
+
+
+ +
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Attributes / Attributes disabled 1`] = ` +
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 3 Attributes +
+
+ +
+
+
+
+
+ Dropdown Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Multiselect Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ File Attribute +
+
+
+
+
+ +
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Attributes / Attributes selected 1`] = ` +
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 3 Attributes +
+
+ +
+
+
+
+
+ Dropdown Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Multiselect Attribute +
+
+
+
+
+ + +
+
+
+
+
+
+ Multiselect First Value +
+ +
+
+
+
+
+ Multiselect Second Value +
+ +
+
+
+
+
+
+
+
+
+ File Attribute +
+
+
+
+
+ + +
+
+ +
+
+
+
+
+`; + exports[`Storyshots Attributes / Delete multiple attributes default 1`] = `
`; +exports[`Storyshots Generics / File upload field default 1`] = ` +
+
+
+
+
+ +
+
+ +
+
+
+`; + +exports[`Storyshots Generics / File upload field interactive 1`] = ` +
+
+
+
+
+ +
+
+ +
+
+
+`; + +exports[`Storyshots Generics / File upload field with error 1`] = ` +
+
+
+
+
+ +
+
+ Something went wrong +
+
+ +
+
+
+`; + +exports[`Storyshots Generics / File upload field with ready to upload file 1`] = ` +
+
+
+
+
+ + +
+
+ +
+
+
+`; + +exports[`Storyshots Generics / File upload field with uploaded file 1`] = ` +
+
+
+
+
+ + +
+
+ +
+
+
+`; + exports[`Storyshots Generics / Filter default 1`] = `