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 <krzysztof.k.wolski@gmail.com>

* 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 <krzysztof.k.wolski@gmail.com>
This commit is contained in:
Dawid Tarasiuk 2020-12-16 11:53:28 +01:00 committed by GitHub
parent c3d97b9114
commit 1e140853ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
115 changed files with 7335 additions and 1857 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -47,6 +47,13 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
description: "product attribute type"
}),
value: AttributeInputTypeEnum.MULTISELECT
},
{
label: intl.formatMessage({
defaultMessage: "File",
description: "file attribute type"
}),
value: AttributeInputTypeEnum.FILE
}
];

View file

@ -172,15 +172,19 @@ const AttributePage: React.FC<AttributePageProps> = ({
errors={errors}
onChange={change}
/>
<CardSpacer />
<AttributeValues
disabled={disabled}
values={values}
onValueAdd={onValueAdd}
onValueDelete={onValueDelete}
onValueReorder={onValueReorder}
onValueUpdate={onValueUpdate}
/>
{data.inputType !== AttributeInputTypeEnum.FILE && (
<>
<CardSpacer />
<AttributeValues
disabled={disabled}
values={values}
onValueAdd={onValueAdd}
onValueDelete={onValueDelete}
onValueReorder={onValueReorder}
onValueUpdate={onValueUpdate}
/>
</>
)}
<CardSpacer />
<Metadata data={data} onChange={changeMetadata} />
</div>

View file

@ -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<AttributePropertiesProps> = ({
/>
</Typography>
<Hr />
{data.type === AttributeTypeEnum.PRODUCT_TYPE && (
<>
<ControlledCheckbox
name={"filterableInStorefront" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "Use in Faceted Navigation",
description: "attribute is filterable in storefront"
})}
checked={data.filterableInStorefront}
onChange={onChange}
disabled={disabled}
/>
<FormSpacer />
</>
)}
{data.filterableInStorefront &&
{data.inputType !== AttributeInputTypeEnum.FILE &&
data.type === AttributeTypeEnum.PRODUCT_TYPE && (
<TextField
disabled={disabled}
error={!!formErrors.storefrontSearchPosition}
fullWidth
helperText={getAttributeErrorMessage(
formErrors.storefrontSearchPosition,
intl
<>
<ControlledCheckbox
name={"filterableInStorefront" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "Use in Faceted Navigation",
description: "attribute is filterable in storefront"
})}
checked={data.filterableInStorefront}
onChange={onChange}
disabled={disabled}
/>
{data.filterableInStorefront && (
<>
<FormSpacer />
<TextField
disabled={disabled}
error={!!formErrors.storefrontSearchPosition}
fullWidth
helperText={getAttributeErrorMessage(
formErrors.storefrontSearchPosition,
intl
)}
name={
"storefrontSearchPosition" as keyof AttributePageFormData
}
label={intl.formatMessage({
defaultMessage: "Position in faceted navigation",
description: "attribute position in storefront filters"
})}
value={data.storefrontSearchPosition}
onChange={onChange}
/>
</>
)}
name={"storefrontSearchPosition" as keyof AttributePageFormData}
label={intl.formatMessage({
defaultMessage: "Position in faceted navigation",
description: "attribute position in storefront filters"
})}
value={data.storefrontSearchPosition}
onChange={onChange}
/>
</>
)}
<FormSpacer />
<ControlledSwitch
@ -129,50 +136,54 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
onChange={onChange}
disabled={disabled}
/>
<CardSpacer />
<Typography variant="subtitle1">
<FormattedMessage
defaultMessage="Dashboard Properties"
description="attribute properties regarding dashboard"
/>
</Typography>
<Hr />
<CardSpacer />
<ControlledCheckbox
name={"filterableInDashboard" as keyof FormData}
label={
<>
{data.inputType !== AttributeInputTypeEnum.FILE && (
<>
<CardSpacer />
<Typography variant="subtitle1">
<FormattedMessage
defaultMessage="Use in Filtering"
description="use attribute in filtering"
defaultMessage="Dashboard Properties"
description="attribute properties regarding dashboard"
/>
<Typography variant="caption">
<FormattedMessage defaultMessage="If enabled, youll be able to use this attribute to filter products in product list." />
</Typography>
</>
}
checked={data.filterableInDashboard}
onChange={onChange}
disabled={disabled}
/>
<FormSpacer />
<ControlledCheckbox
name={"availableInGrid" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="Add to Column Options"
description="add attribute as column in product list table"
/>
<Typography variant="caption">
<FormattedMessage defaultMessage="If enabled this attribute can be used as a column in product table." />
</Typography>
</>
}
checked={data.availableInGrid}
onChange={onChange}
disabled={disabled}
/>
</Typography>
<Hr />
<CardSpacer />
<ControlledCheckbox
name={"filterableInDashboard" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="Use in Filtering"
description="use attribute in filtering"
/>
<Typography variant="caption">
<FormattedMessage defaultMessage="If enabled, youll be able to use this attribute to filter products in product list." />
</Typography>
</>
}
checked={data.filterableInDashboard}
onChange={onChange}
disabled={disabled}
/>
<FormSpacer />
<ControlledCheckbox
name={"availableInGrid" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="Add to Column Options"
description="add attribute as column in product list table"
/>
<Typography variant="caption">
<FormattedMessage defaultMessage="If enabled this attribute can be used as a column in product table." />
</Typography>
</>
}
checked={data.availableInGrid}
onChange={onChange}
disabled={disabled}
/>
</>
)}
</CardContent>
</Card>
);

View file

@ -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<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0",
name: "John Doe",
slug: "john-doe",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1",
name: "Milionare Pirate",
slug: "milionare-pirate",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -93,38 +92,38 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE1",
name: "100g",
slug: "100g",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE2",
name: "250g",
slug: "250g",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE3",
name: "500g",
slug: "500g",
sortOrder: 2,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE4",
name: "1kg",
slug: "1kg",
sortOrder: 3,
type: AttributeValueType.STRING,
value: ""
}
],
@ -143,11 +142,11 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjY=",
name: "Saleor",
slug: "saleor",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
}
],
@ -166,29 +165,29 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIx",
name: "100g",
slug: "100g",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIy",
name: "250g",
slug: "250g",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIz",
name: "500g",
slug: "500g",
sortOrder: 2,
type: AttributeValueType.STRING,
value: ""
}
],
@ -207,20 +206,20 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEz",
name: "Arabica",
slug: "arabica",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE0",
name: "Robusta",
slug: "robusta",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -239,29 +238,29 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM=",
name: "Round",
slug: "round",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjQ=",
name: "V-Neck",
slug: "v-neck",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjU=",
name: "Polo",
slug: "polo",
sortOrder: 2,
type: AttributeValueType.STRING,
value: ""
}
],
@ -280,20 +279,20 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE=",
name: "Blue",
slug: "blue",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI=",
name: "White",
slug: "white",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -312,56 +311,56 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMw",
name: "Soft",
slug: "soft",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMx",
name: "Hard",
slug: "hard",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMy",
name: "Middle soft",
slug: "middle-soft",
sortOrder: 2,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMz",
name: "Middle hard",
slug: "middle-hard",
sortOrder: 3,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM0",
name: "Middle",
slug: "middle",
sortOrder: 4,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM1",
name: "Very hard",
slug: "very-hard",
sortOrder: 5,
type: AttributeValueType.STRING,
value: ""
}
],
@ -380,20 +379,20 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE5",
name: "Sour",
slug: "sour",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIw",
name: "Sweet",
slug: "sweet",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -412,20 +411,20 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI4",
name: "English",
slug: "english",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI5",
name: "Pirate",
slug: "pirate",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -444,20 +443,20 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI2",
name: "Mirumee Press",
slug: "mirumee-press",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI3",
name: "Saleor Publishing",
slug: "saleor-publishing",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
}
],
@ -476,56 +475,56 @@ export const attributes: Array<AttributeList_attributes_edges_node &
values: [
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjc=",
name: "XS",
slug: "xs",
sortOrder: 0,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjg=",
name: "S",
slug: "s",
sortOrder: 1,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjk=",
name: "M",
slug: "m",
sortOrder: 2,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEw",
name: "L",
slug: "l",
sortOrder: 3,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEx",
name: "XL",
slug: "xl",
sortOrder: 4,
type: AttributeValueType.STRING,
value: ""
},
{
__typename: "AttributeValue" as "AttributeValue",
file: null,
id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEy",
name: "XXL",
slug: "xxl",
sortOrder: 5,
type: AttributeValueType.STRING,
value: ""
}
],

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AttributeCreate
@ -20,12 +20,18 @@ export interface AttributeCreate_attributeCreate_attribute_privateMetadata {
value: string;
}
export interface AttributeCreate_attributeCreate_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeCreate_attributeCreate_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeCreate_attributeCreate_attribute_values_file | null;
}
export interface AttributeCreate_attributeCreate_attribute {

View file

@ -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 query operation: AttributeDetails
@ -20,12 +20,18 @@ export interface AttributeDetails_attribute_privateMetadata {
value: string;
}
export interface AttributeDetails_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeDetails_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeDetails_attribute_values_file | null;
}
export interface AttributeDetails_attribute {

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AttributeUpdate
@ -20,12 +20,18 @@ export interface AttributeUpdate_attributeUpdate_attribute_privateMetadata {
value: string;
}
export interface AttributeUpdate_attributeUpdate_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeUpdate_attributeUpdate_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeUpdate_attributeUpdate_attribute_values_file | null;
}
export interface AttributeUpdate_attributeUpdate_attribute {

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AttributeValueCreate
@ -20,12 +20,18 @@ export interface AttributeValueCreate_attributeValueCreate_attribute_privateMeta
value: string;
}
export interface AttributeValueCreate_attributeValueCreate_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeValueCreate_attributeValueCreate_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeValueCreate_attributeValueCreate_attribute_values_file | null;
}
export interface AttributeValueCreate_attributeValueCreate_attribute {

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AttributeValueDelete
@ -20,12 +20,18 @@ export interface AttributeValueDelete_attributeValueDelete_attribute_privateMeta
value: string;
}
export interface AttributeValueDelete_attributeValueDelete_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeValueDelete_attributeValueDelete_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeValueDelete_attributeValueDelete_attribute_values_file | null;
}
export interface AttributeValueDelete_attributeValueDelete_attribute {

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeValueType, AttributeErrorCode } from "./../../types/globalTypes";
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AttributeValueUpdate
@ -20,12 +20,18 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute_privateMeta
value: string;
}
export interface AttributeValueUpdate_attributeValueUpdate_attribute_values_file {
__typename: "File";
url: string;
contentType: string | null;
}
export interface AttributeValueUpdate_attributeValueUpdate_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
type: AttributeValueType | null;
file: AttributeValueUpdate_attributeValueUpdate_attribute_values_file | null;
}
export interface AttributeValueUpdate_attributeValueUpdate_attribute {

View file

@ -0,0 +1,106 @@
import { FileUpload } from "@saleor/files/types/FileUpload";
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
import { SelectedVariantAttributeFragment } from "@saleor/fragments/types/SelectedVariantAttributeFragment";
import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment";
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 } from "../types/AttributeValueDelete";
export const isFileValueUnused = (
attributesWithNewFileValue: FormsetData<null, File>,
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<MutationFetchResult<FileUpload>>
): UploadErrorFragment[] =>
uploadFilesResult.reduce((errors, uploadFileResult) => {
const uploadErrors = uploadFileResult?.data?.fileUpload?.uploadErrors;
if (uploadErrors) {
return [...errors, ...uploadErrors];
}
return errors;
}, []);
export const mergeAttributeValueDeleteErrors = (
deleteAttributeValuesResult: Array<MutationFetchResult<AttributeValueDelete>>
): AttributeErrorFragment[] =>
deleteAttributeValuesResult.reduce((errors, deleteValueResult) => {
const deleteErrors = deleteValueResult?.data?.attributeValueDelete?.errors;
if (deleteErrors) {
return [...errors, ...deleteErrors];
}
return errors;
}, []);
export const getFileValuesToUploadFromAttributes = (
attributesWithNewFileValue: FormsetData<null, File>
) => attributesWithNewFileValue.filter(fileAttribute => !!fileAttribute.value);
export const getFileValuesRemovedFromAttributes = (
attributesWithNewFileValue: FormsetData<null, File>
) => attributesWithNewFileValue.filter(attribute => !attribute.value);
export const getAttributesOfRemovedFiles = (
fileAttributesRemoved: FormsetData<null, File>
) =>
fileAttributesRemoved.map(attribute => ({
file: undefined,
id: attribute.id,
values: []
}));
export const getAttributesOfUploadedFiles = (
fileValuesToUpload: FormsetData<null, File>,
uploadFilesResult: Array<MutationFetchResult<FileUpload>>
) =>
uploadFilesResult.map((uploadFileResult, index) => {
const attribute = fileValuesToUpload[index];
return {
file: uploadFileResult.data.fileUpload.uploadedFile.url,
id: attribute.id,
values: []
};
});
export const getAttributesAfterFileAttributesUpdate = (
attributesWithNewFileValue: FormsetData<null, File>,
uploadFilesResult: Array<MutationFetchResult<FileUpload>>
): AttributeValueInput[] => {
const removedFileValues = getFileValuesRemovedFromAttributes(
attributesWithNewFileValue
);
const fileValuesToUpload = getFileValuesToUploadFromAttributes(
attributesWithNewFileValue
);
const removedFileAttributes = getAttributesOfRemovedFiles(removedFileValues);
const uploadedFileAttributes = getAttributesOfUploadedFiles(
fileValuesToUpload,
uploadFilesResult
);
return uploadedFileAttributes.concat(removedFileAttributes);
};

View file

@ -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<null, File>,
uploadFile: (
variables: FileUploadVariables
) => Promise<MutationFetchResult<FileUpload>>
) =>
Promise.all(
getFileValuesToUploadFromAttributes(attributesWithNewFileValue).map(
fileAttribute =>
uploadFile({
file: fileAttribute.value
})
)
);
export const handleDeleteMultipleAttributeValues = async (
attributesWithNewFileValue: FormsetData<null, File>,
attributes: PageDetails_page_attributes[],
deleteAttributeValue: (
variables: AttributeValueDeleteVariables
) => Promise<MutationFetchResult<AttributeValueDelete>>
) =>
Promise.all(
attributes.map(existingAttribute => {
const fileValueUnused = isFileValueUnused(
attributesWithNewFileValue,
existingAttribute
);
if (fileValueUnused) {
return deleteAttributeValue({
id: existingAttribute.values[0].id
});
}
})
);

View file

@ -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<AttributeDetailsProps> = ({ params }) => {
const navigate = useNavigator();
const notify = useNotifier();
@ -115,15 +145,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ 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<AttributeDetailsProps> = ({ 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
}))}

View file

@ -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", () => <Attributes {...props} />)
.add("selected", () => (
<Attributes {...props} attributes={ATTRIBUTES_SELECTED} />
))
.add("disabled", () => <Attributes {...props} disabled={true} />);

View file

@ -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<AttributeInputData, string[]>;
export type AttributeFileInput = FormsetAtomicData<AttributeInputData, File[]>;
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<ProductAttributesProps> = ({
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<AttributesProps> = ({
attributes,
disabled,
loading,
errors,
title,
onChange,
onMultiChange
onMultiChange,
onFileChange
}) => {
const intl = useIntl();
const classes = useStyles({});
@ -138,19 +219,13 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
return (
<Card className={classes.card}>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Attributes",
description: "product attributes, section header"
})}
/>
<CardTitle title={title || intl.formatMessage(messages.header)} />
<CardContent className={classes.cardContent}>
<div className={classes.expansionBar}>
<div className={classes.expansionBarLabelContainer}>
<Typography className={classes.expansionBarLabel} variant="caption">
<FormattedMessage
defaultMessage="{number} Attributes"
description="number of product attributes"
{...messages.attributesNumber}
values={{
number: attributes.length
}}
@ -160,7 +235,7 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
<IconButton
className={classes.expansionBarButton}
onClick={toggleExpansion}
data-test="product-attributes-expand"
data-test="attributes-expand"
>
<ArrowDropDownIcon
className={classNames(classes.expansionBarButtonIcon, {
@ -183,13 +258,32 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
<Grid className={classes.attributeSection} variant="uniform">
<div
className={classes.attributeSectionLabel}
data-test="product-attribute-label"
data-test="attribute-label"
>
<Typography>{attribute.label}</Typography>
</div>
<div data-test="product-attribute-value">
<div data-test="attribute-value">
{attribute.data.inputType ===
AttributeInputTypeEnum.DROPDOWN ? (
AttributeInputTypeEnum.FILE ? (
<FileUploadField
className={classes.fileField}
disabled={disabled}
loading={loading}
file={getFileChoice(attribute)}
onFileUpload={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 ? (
<SingleAutocompleteSelectField
choices={getSingleChoices(attribute.data.values)}
disabled={disabled}
@ -202,12 +296,9 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
}
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<ProductAttributesProps> = ({
<MultiAutocompleteSelectField
choices={getMultiChoices(attribute.data.values)}
displayValues={getMultiDisplayValue(attribute)}
disabled={disabled}
error={!!error}
helperText={getProductErrorMessage(error, intl)}
label={intl.formatMessage({
defaultMessage: "Values",
description: "attribute values"
})}
helperText={getErrorMessage(error, intl)}
label={intl.formatMessage(
messages.multipleValueLable
)}
name={`attribute:${attribute.label}`}
value={attribute.value}
onChange={event =>
@ -243,5 +334,5 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
</Card>
);
};
ProductAttributes.displayName = "ProductAttributes";
export default ProductAttributes;
Attributes.displayName = "Attributes";
export default Attributes;

View file

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

View file

@ -0,0 +1,3 @@
export { default } from "./Attributes";
export * from "./Attributes";
export * from "./types";

View file

@ -0,0 +1,5 @@
export enum VariantAttributeScope {
ALL = "ALL",
VARIANT_SELECTION = "VARIANT_SELECTION",
NOT_VARIANT_SELECTION = "NOT_VARIANT_SELECTION"
}

View file

@ -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<File>();
return (
<FileUploadField
disabled={false}
loading={false}
file={{
label: file?.name,
value: file?.name
}}
onFileUpload={file => setFile(file)}
onFileDelete={() => setFile(null)}
/>
);
};
storiesOf("Generics / File upload field", module)
.addDecorator(CardDecorator)
.addDecorator(Decorator)
.add("default", () => <FileUploadField {...props} />)
.add("with ready to upload file", () => (
<FileUploadField
{...props}
file={{
label: "some_file.png",
value: "some_file.png"
}}
/>
))
.add("with uploaded file", () => (
<FileUploadField
{...props}
file={{
file: fixtures.UPLOADED_FILE,
label: "some_file_with_link.png",
value: "some_file_with_link.png"
}}
/>
))
.add("with error", () => (
<FileUploadField
{...props}
error={true}
helperText="Something went wrong"
/>
))
.add("interactive", () => <InteractiveStory />);

View file

@ -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>,
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<FileUploadFieldProps> = props => {
const {
loading,
disabled,
file,
className,
error,
helperText,
onFileUpload,
onFileDelete,
inputProps
} = props;
const classes = useStyles({});
const intl = useIntl();
const fileInputAnchor = React.createRef<HTMLInputElement>();
const clickFileInput = () => fileInputAnchor.current.click();
const handleFileDelete = () => {
fileInputAnchor.current.value = "";
onFileDelete();
};
React.useEffect(() => {
if (!file.value) {
fileInputAnchor.current.value = "";
}
}, [file]);
return (
<>
<div className={className}>
{file.label ? (
<div className={classes.uploadFileContent}>
<div className={classes.uploadFileName}>
{loading ? (
<Skeleton />
) : (
<a
href={file.file?.url}
target="blank"
className={classes.fileUrl}
>
{file.label}
</a>
)}
</div>
<IconButton
color="primary"
onClick={handleFileDelete}
disabled={disabled || loading}
data-test="button-delete-file"
>
<DeleteIcon />
</IconButton>
</div>
) : (
<div>
<Button
onClick={clickFileInput}
disabled={disabled || loading}
variant="outlined"
color="primary"
data-test="button-upload-file"
>
{intl.formatMessage(commonMessages.chooseFile)}
</Button>
</div>
)}
{error && (
<Typography variant="caption" className={classes.errorText}>
{helperText}
</Typography>
)}
</div>
<input
className={classes.fileField}
id="fileUpload"
onChange={event => onFileUpload(event.target.files[0])}
type="file"
data-test="upload-file-input"
ref={fileInputAnchor}
{...inputProps}
/>
</>
);
};
FileUploadField.displayName = "FileUploadField";
export default FileUploadField;

View file

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

View file

@ -0,0 +1,2 @@
export { default } from "./FileUploadField";
export * from "./FileUploadField";

View file

@ -83,6 +83,7 @@ export interface MultiAutocompleteSelectFieldProps
placeholder?: string;
helperText?: string;
label?: string;
disabled?: boolean;
fetchChoices?: (value: string) => void;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -105,6 +106,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
name,
placeholder,
value,
disabled,
fetchChoices,
onChange,
onFetchMore,
@ -167,6 +169,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
helperText={helperText}
label={label}
fullWidth={true}
disabled={disabled}
/>
{isOpen && (!!inputValue || !!choices.length) && (
<MultiAutocompleteSelectFieldContent

25
src/files/mutations.ts Normal file
View file

@ -0,0 +1,25 @@
import { uploadErrorFragment } from "@saleor/fragments/errors";
import { fileFragment } from "@saleor/fragments/file";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import { FileUpload, FileUploadVariables } from "./types/FileUpload";
const fileUploadMutation = gql`
${fileFragment}
${uploadErrorFragment}
mutation FileUpload($file: Upload!) {
fileUpload(file: $file) {
uploadedFile {
...FileFragment
}
uploadErrors {
...UploadErrorFragment
}
}
}
`;
export const useFileUploadMutation = makeMutation<
FileUpload,
FileUploadVariables
>(fileUploadMutation);

View file

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

View file

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

View file

@ -206,3 +206,10 @@ export const collectionsErrorFragment = gql`
field
}
`;
export const uploadErrorFragment = gql`
fragment UploadErrorFragment on UploadError {
code
field
}
`;

8
src/fragments/file.ts Normal file
View file

@ -0,0 +1,8 @@
import gql from "graphql-tag";
export const fileFragment = gql`
fragment FileFragment on File {
url
contentType
}
`;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)[];
}

View file

@ -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)[];
}

View file

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

View file

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

View file

@ -7,6 +7,10 @@ export const commonMessages = defineMessages({
catalog: {
defaultMessage: "Catalog"
},
chooseFile: {
defaultMessage: "Choose file",
description: "button"
},
customApps: {
defaultMessage: "Local Apps"
},

View file

@ -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<PageAttributesProps> = ({
attributes,
disabled,
errors,
onChange,
onMultiChange
}) => {
const intl = useIntl();
const classes = useStyles({});
const [expanded, setExpansionStatus] = React.useState(true);
const toggleExpansion = () => setExpansionStatus(!expanded);
return (
<Card className={classes.card}>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Attributes",
description: "page attributes, section header"
})}
/>
<CardContent className={classes.cardContent}>
<div className={classes.expansionBar}>
<div className={classes.expansionBarLabelContainer}>
<Typography className={classes.expansionBarLabel} variant="caption">
<FormattedMessage
defaultMessage="{number} Attributes"
description="number of page attributes"
values={{
number: attributes.length
}}
/>
</Typography>
</div>
<IconButton
className={classes.expansionBarButton}
onClick={toggleExpansion}
data-test="page-attributes-expand"
>
<ArrowDropDownIcon
className={classNames(classes.expansionBarButtonIcon, {
[classes.rotate]: expanded
})}
/>
</IconButton>
</div>
{expanded && attributes.length > 0 && (
<>
<Hr />
{attributes.map((attribute, attributeIndex) => {
const error = errors.find(err =>
err.attributes?.includes(attribute.id)
);
return (
<React.Fragment key={attribute.id}>
{attributeIndex > 0 && <Hr />}
<Grid className={classes.attributeSection} variant="uniform">
<div
className={classes.attributeSectionLabel}
data-test="page-attribute-label"
>
<Typography>{attribute.label}</Typography>
</div>
<div data-test="page-attribute-value">
{attribute.data.inputType ===
AttributeInputTypeEnum.DROPDOWN ? (
<SingleAutocompleteSelectField
choices={getSingleChoices(attribute.data.values)}
disabled={disabled}
displayValue={
attribute.data.values.find(
value => 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}
/>
) : (
<MultiAutocompleteSelectField
choices={getMultiChoices(attribute.data.values)}
displayValues={getMultiDisplayValue(attribute)}
error={!!error}
helperText={getPageErrorMessage(error, intl)}
label={intl.formatMessage({
defaultMessage: "Values",
description: "attribute values"
})}
name={`attribute:${attribute.label}`}
value={attribute.value}
onChange={event =>
onMultiChange(attribute.id, event.target.value)
}
allowCustomValues={!attribute.data.isRequired}
/>
)}
</div>
</Grid>
</React.Fragment>
);
})}
</>
)}
</CardContent>
</Card>
);
};
PageAttributes.displayName = "PageAttributes";
export default PageAttributes;

View file

@ -1,2 +0,0 @@
export { default } from "./PageAttributes";
export * from "./PageAttributes";

View file

@ -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<PageDetailsPageProps> = ({
disabled,
loading,
errors,
page,
pageTypes,
@ -75,7 +75,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
<div>
<PageInfo
data={data}
disabled={disabled}
disabled={loading}
errors={errors}
onChange={change}
onContentChange={handlers.changeContent}
@ -85,7 +85,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
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<PageDetailsPageProps> = ({
/>
<CardSpacer />
{data.attributes.length > 0 && (
<PageAttributes
<Attributes
attributes={data.attributes}
disabled={disabled}
disabled={loading}
loading={loading}
errors={errors}
onChange={handlers.changeAttribute}
onMultiChange={handlers.changeAttributeMulti}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMulti}
onFileChange={handlers.selectAttributeFile}
/>
)}
<CardSpacer />
@ -115,7 +117,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
<VisibilityCard
data={data}
errors={errors}
disabled={disabled}
disabled={loading}
messages={{
hiddenLabel: intl.formatMessage({
defaultMessage: "Hidden",
@ -141,7 +143,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
<PageOrganizeContent
data={data}
errors={errors}
disabled={disabled}
disabled={loading}
pageTypes={pageTypes}
pageType={pageType}
pageTypeInputDisplayValue={pageType?.name || ""}
@ -153,7 +155,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
</div>
</Grid>
<SaveButtonBar
disabled={disabled || !hasChanged}
disabled={loading || !hasChanged}
state={saveButtonBarState}
onCancel={onBack}
onDelete={page === null ? undefined : onRemove}

View file

@ -1,18 +1,26 @@
import { OutputData } from "@editorjs/editorjs";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
import { PageTypeFragment } from "@saleor/fragments/types/PageTypeFragment";
import useForm, { FormChange, SubmitPromise } 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 {
PageDetails_page,
PageDetails_page_pageType
} from "@saleor/pages/types/PageDetails";
import { getAttributeInputFromPage } from "@saleor/pages/utils/data";
import {
getAttributeInputFromPage,
getAttributesDisplayData
} from "@saleor/pages/utils/data";
import { createPageTypeSelectHandler } from "@saleor/pages/utils/handlers";
import {
createAttributeChangeHandler,
createAttributeFileChangeHandler,
createAttributeMultiChangeHandler
} from "@saleor/products/utils/handlers";
import getPublicationData from "@saleor/utils/data/getPublicationData";
@ -21,9 +29,7 @@ import { mapMetadataItemToInput } from "@saleor/utils/maps";
import getMetadata from "@saleor/utils/metadata/getMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import useRichText from "@saleor/utils/richText/useRichText";
import React from "react";
import { PageAttributeInput, PageAttributeInputData } from "../PageAttributes";
import React, { useEffect } from "react";
export interface PageFormData extends MetadataFormData {
isPublished: boolean;
@ -35,7 +41,13 @@ export interface PageFormData extends MetadataFormData {
pageType: string;
}
export interface PageData extends PageFormData {
attributes: PageAttributeInput[];
attributes: AttributeInput[];
content: OutputData;
}
export interface PageSubmitData extends PageFormData {
attributes: AttributeInput[];
attributesWithNewFileValue: FormsetData<null, File>;
content: OutputData;
}
@ -43,8 +55,9 @@ interface PageUpdateHandlers {
changeMetadata: FormChange;
changeContent: RichTextEditorChange;
selectPageType: FormChange;
changeAttribute: FormsetChange<string>;
changeAttributeMulti: FormsetChange<string>;
selectAttribute: FormsetChange<string>;
selectAttributeMulti: FormsetChange<string>;
selectAttributeFile: FormsetChange<File>;
}
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<PageAttributeInputData>(attributesFromPage || []);
const attributes = useFormset(getAttributeInputFromPage(page));
const attributesWithNewFileValue = useFormset<null, File>([]);
const form = useForm<PageFormData>({
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,

View file

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

View file

@ -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, PageUpdateVariables>(
pageUpdate
);
export const usePageUpdateMutation = makeMutation<
PageUpdate,
PageUpdateVariables
>(pageUpdate);
const pageRemove = gql`
${pageErrorFragment}
@ -65,9 +67,10 @@ const pageRemove = gql`
}
}
`;
export const TypedPageRemove = TypedMutation<PageRemove, PageRemoveVariables>(
pageRemove
);
export const usePageRemoveMutation = makeMutation<
PageRemove,
PageRemoveVariables
>(pageRemove);
const pageBulkPublish = gql`
mutation PageBulkPublish($ids: [ID]!, $isPublished: Boolean!) {

View file

@ -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, PageDetailsVariables>(
pageDetails
);

View file

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

View file

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

View file

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

View file

@ -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<null, File>
) =>
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;
});

View file

@ -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<PageAttributeInputData, string[]> = [
const attributes: FormsetData<AttributeInputData, string[]> = [
{
data: {
inputType: AttributeInputTypeEnum.DROPDOWN,
@ -12,6 +12,7 @@ const attributes: FormsetData<PageAttributeInputData, string[]> = [
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<PageAttributeInputData, string[]> = [
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<PageAttributeInputData, string[]> = [
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: []
}
];

View file

@ -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<PageAttributeInputData>) => void,
setAttributes: (data: FormsetData<AttributeInputData>) => void,
setPageType: (pageType: PageDetails_page_pageType) => void,
pageTypeChoiceList: PageDetails_page_pageType[]
): FormChange {
@ -36,7 +36,7 @@ export function createAttributeChangeHandler(
export function createAttributeMultiChangeHandler(
changeAttributeData: FormsetChange<string[]>,
attributes: FormsetData<PageAttributeInputData, string[]>,
attributes: FormsetData<AttributeInputData, string[]>,
triggerChange: () => void
): FormsetChange {
return (attributeId: string, value: string) => {

View file

@ -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<PageCreateProps> = () => {
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<PageCreateProps> = () => {
return (
<TypedPageCreate onCompleted={handlePageCreate}>
{(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<PageCreateProps> = () => {
})}
/>
<PageDetailsPage
disabled={pageCreateOpts.loading}
loading={pageCreateOpts.loading || uploadFileOpts.loading}
errors={pageCreateOpts.data?.pageCreate.errors || []}
saveButtonBarState={pageCreateOpts.status}
page={null}

View file

@ -1,6 +1,21 @@
import DialogContentText from "@material-ui/core/DialogContentText";
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 ActionDialog from "@saleor/components/ActionDialog";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { useFileUploadMutation } from "@saleor/files/mutations";
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment";
import { UploadErrorFragment } from "@saleor/fragments/types/UploadErrorFragment";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl";
@ -13,11 +28,11 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { getStringOrPlaceholder, maybe } from "../../misc";
import { PageInput } from "../../types/globalTypes";
import { AttributeValueInput, PageInput } from "../../types/globalTypes";
import PageDetailsPage from "../components/PageDetailsPage";
import { PageData } from "../components/PageDetailsPage/form";
import { TypedPageRemove, TypedPageUpdate } from "../mutations";
import { TypedPageDetailsQuery } from "../queries";
import { PageData, PageSubmitData } from "../components/PageDetailsPage/form";
import { usePageRemoveMutation, usePageUpdateMutation } from "../mutations";
import { usePageDetailsQuery } from "../queries";
import { PageRemove } from "../types/PageRemove";
import { pageListUrl, pageUrl, PageUrlQueryParams } from "../urls";
@ -26,11 +41,14 @@ export interface PageDetailsProps {
params: PageUrlQueryParams;
}
const createPageInput = (data: PageData): PageInput => ({
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<PageDetailsProps> = ({ 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 (
<TypedPageRemove variables={{ id }} onCompleted={handlePageRemove}>
{(pageRemove, pageRemoveOpts) => (
<TypedPageUpdate>
{(pageUpdate, pageUpdateOpts) => (
<TypedPageDetailsQuery variables={{ id }}>
{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 (
<>
<WindowTitle
title={maybe(() => pageDetails.data.page.title)}
/>
<PageDetailsPage
disabled={pageDetails.loading}
errors={pageUpdateOpts.data?.pageUpdate.errors || []}
saveButtonBarState={pageUpdateOpts.status}
page={pageDetails.data?.page}
onBack={() => navigate(pageListUrl())}
onRemove={() =>
navigate(
pageUrl(id, {
action: "remove"
})
)
}
onSubmit={handleSubmit}
/>
<ActionDialog
open={params.action === "remove"}
confirmButtonState={pageRemoveOpts.status}
title={intl.formatMessage({
defaultMessage: "Delete Page",
description: "dialog header"
})}
onClose={() => navigate(pageUrl(id))}
onConfirm={pageRemove}
variant="delete"
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {title}?"
description="delete page"
values={{
title: (
<strong>
{getStringOrPlaceholder(
pageDetails.data?.page?.title
)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
}}
</TypedPageDetailsQuery>
)}
</TypedPageUpdate>
)}
</TypedPageRemove>
<>
<WindowTitle title={maybe(() => pageDetails.data.page.title)} />
<PageDetailsPage
loading={
pageDetails.loading ||
pageUpdateOpts.loading ||
uploadFileOpts.loading ||
deleteAttributeValueOpts.loading
}
errors={pageUpdateOpts.data?.pageUpdate.errors || []}
saveButtonBarState={pageUpdateOpts.status}
page={pageDetails.data?.page}
onBack={() => navigate(pageListUrl())}
onRemove={() =>
navigate(
pageUrl(id, {
action: "remove"
})
)
}
onSubmit={handleSubmit}
/>
<ActionDialog
open={params.action === "remove"}
confirmButtonState={pageRemoveOpts.status}
title={intl.formatMessage({
defaultMessage: "Delete Page",
description: "dialog header"
})}
onClose={() => navigate(pageUrl(id))}
onConfirm={() => pageRemove({ variables: { id } })}
variant="delete"
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {title}?"
description="delete page"
values={{
title: (
<strong>
{getStringOrPlaceholder(pageDetails.data?.page?.title)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
};
PageDetails.displayName = "PageDetails";

View file

@ -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",

View file

@ -1,2 +0,0 @@
export { default } from "./ProductAttributes";
export * from "./ProductAttributes";

View file

@ -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<ProductCreatePageProps> = ({
allChannelsCount,
channelsErrors,
currentChannels,
disabled,
loading,
categories: categoryChoiceList,
collections: collectionChoiceList,
errors,
@ -153,19 +153,21 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
<div>
<ProductDetailsForm
data={data}
disabled={disabled}
disabled={loading}
errors={errors}
onChange={change}
onDescriptionChange={handlers.changeDescription}
/>
<CardSpacer />
{data.attributes.length > 0 && (
<ProductAttributes
<Attributes
attributes={data.attributes}
disabled={disabled}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
)}
<CardSpacer />
@ -173,7 +175,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
<>
<ProductShipping
data={data}
disabled={disabled}
disabled={loading}
errors={errors}
weightUnit={weightUnit}
onChange={change}
@ -182,13 +184,13 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
<ProductVariantPrice
ProductVariantChannelListings={data.channelListings}
errors={channelsErrors}
loading={disabled}
loading={loading}
onChange={handlers.changeChannelPrice}
/>
<CardSpacer />
<ProductStocks
data={data}
disabled={disabled}
disabled={loading}
hasVariants={false}
onFormDataChange={change}
errors={errors}
@ -214,7 +216,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
titlePlaceholder={data.name}
description={data.seoDescription}
descriptionPlaceholder={data.seoTitle}
loading={disabled}
loading={loading}
onChange={change}
/>
<CardSpacer />
@ -227,7 +229,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
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<ProductCreatePageProps> = ({
selectedChannelsCount={data.channelListings.length}
allChannelsCount={allChannelsCount}
channels={data.channelListings}
disabled={disabled}
disabled={loading}
onChange={handlers.changeChannels}
openModal={openChannelsModal}
/>
<CardSpacer />
<ProductTaxes
data={data}
disabled={disabled}
disabled={loading}
onChange={change}
onTaxTypeChange={handlers.selectTaxRate}
selectedTaxTypeDisplayName={selectedTaxType}
@ -279,7 +281,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
onCancel={onBack}
onSave={submit}
state={saveButtonBarState}
disabled={disabled || !onSubmit || formDisabled || !hasChanged}
disabled={loading || !onSubmit || formDisabled || !hasChanged}
/>
</Container>
);

View file

@ -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<null, File>;
stocks: ProductStockInput[];
}
@ -82,6 +88,7 @@ interface ProductCreateHandlers
data: Omit<ChannelData, "name" | "price" | "currency" | "id">
) => void
>,
Record<"selectAttributeFile", FormsetChange<File>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changeDescription: RichTextEditorChange;
}
@ -159,11 +166,12 @@ function useProductCreateForm(
...initial,
...defaultInitialFormData
});
const attributes = useFormset<ProductAttributeInputData>(
const attributes = useFormset<AttributeInputData>(
initial?.productType
? getAttributeInputFromProductType(initialProductType)
: []
);
const attributesWithNewFileValue = useFormset<null, File>([]);
const stocks = useFormset<null, string>([]);
const [productType, setProductType] = useStateFromProps<ProductType>(
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,

View file

@ -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", () => {

View file

@ -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<null, File>;
collections: string[];
description: OutputData;
removeStocks: string[];
@ -217,12 +219,14 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
/>
<CardSpacer />
{data.attributes.length > 0 && (
<ProductAttributes
<Attributes
attributes={data.attributes}
errors={errors}
loading={disabled}
disabled={disabled}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
)}
<CardSpacer />

View file

@ -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<null, File>;
collections: string[];
description: OutputData;
addStocks: ProductStockInput[];
@ -89,6 +105,7 @@ interface ProductUpdateHandlers
data: Omit<ChannelData, "name" | "price" | "currency" | "id">
) => void
>,
Record<"selectAttributeFile", FormsetChange<File>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changeDescription: RichTextEditorChange;
}
@ -165,6 +182,7 @@ function useProductUpdateForm(
)
);
const attributes = useFormset(getAttributeInputFromProduct(product));
const attributesWithNewFileValue = useFormset<null, File>([]);
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<string> = (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,

View file

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

View file

@ -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<ProductVariantCreatePageProps> = ({
/>
</div>
<div>
<ProductVariantAttributes
attributes={data.attributes}
<Attributes
title={intl.formatMessage(messages.attributesHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.NOT_VARIANT_SELECTION
)}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
<CardSpacer />
<Attributes
title={intl.formatMessage(messages.attributesSelectionHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.VARIANT_SELECTION
)}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
<CardSpacer />
<ProductShipping
@ -136,14 +180,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
<SaveButtonBar
disabled={disabled || formDisabled || !onSubmit || !hasChanged}
labels={{
delete: intl.formatMessage({
defaultMessage: "Delete Variant",
description: "button"
}),
save: intl.formatMessage({
defaultMessage: "Save variant",
description: "button"
})
delete: intl.formatMessage(messages.deleteVariant),
save: intl.formatMessage(messages.saveVariant)
}}
state={saveButtonBarState}
onCancel={onBack}

View file

@ -1,4 +1,5 @@
import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import useForm, { FormChange } from "@saleor/hooks/useForm";
import useFormset, {
@ -6,8 +7,18 @@ import useFormset, {
FormsetData
} from "@saleor/hooks/useFormset";
import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData";
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
import { getChannelsInput } from "@saleor/products/utils/handlers";
import {
getAttributesDisplayData,
getVariantAttributeInputFromProduct
} from "@saleor/products/utils/data";
import {
createAttributeFileChangeHandler,
getChannelsInput
} from "@saleor/products/utils/handlers";
import {
createAttributeChangeHandler,
createAttributeMultiChangeHandler
} from "@saleor/products/utils/handlers";
import {
validateCostPrice,
validatePrice
@ -17,7 +28,6 @@ import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTr
import React from "react";
import { ProductStockInput } from "../ProductStocks";
import { VariantAttributeInputData } from "../ProductVariantAttributes";
export interface ProductVariantCreateFormData extends MetadataFormData {
sku: string;
@ -25,8 +35,9 @@ export interface ProductVariantCreateFormData extends MetadataFormData {
weight: string;
}
export interface ProductVariantCreateData extends ProductVariantCreateFormData {
attributes: FormsetData<VariantAttributeInputData, string>;
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
attributes: AttributeInput[];
attributesWithNewFileValue: FormsetData<null, File>;
stocks: ProductStockInput[];
}
@ -35,18 +46,25 @@ export interface UseProductVariantCreateFormOpts {
currentChannels: ChannelPriceData[];
}
interface ProductVariantCreateHandlers
extends Record<
| "changeStock"
| "selectAttribute"
| "selectAttributeMultiple"
| "changeChannels",
FormsetChange
>,
Record<"selectAttributeFile", FormsetChange<File>>,
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<null, File>([]);
const stocks = useFormset<null, string>([]);
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

View file

@ -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<ProductVariantPageProps> = ({
onSetDefaultVariant,
onWarehouseConfigure
}) => {
const intl = useIntl();
const [isModalOpened, setModalStatus] = React.useState(false);
const toggleModal = () => setModalStatus(!isModalOpened);
@ -131,11 +165,36 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
/>
</div>
<div>
<ProductVariantAttributes
attributes={data.attributes}
<Attributes
title={intl.formatMessage(messages.nonSelectionAttributes)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.NOT_VARIANT_SELECTION
)}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
<CardSpacer />
<Attributes
title={intl.formatMessage(
messages.selectionAttributesHeader
)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.VARIANT_SELECTION
)}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
/>
<CardSpacer />
<ProductVariantImages

View file

@ -1,4 +1,5 @@
import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import { ProductVariant } from "@saleor/fragments/types/ProductVariant";
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
@ -8,9 +9,17 @@ import useFormset, {
} from "@saleor/hooks/useFormset";
import {
getAttributeInputFromVariant,
getAttributesDisplayData,
getStockInputFromVariant
} from "@saleor/products/utils/data";
import { getChannelsInput } from "@saleor/products/utils/handlers";
import {
createAttributeFileChangeHandler,
getChannelsInput
} from "@saleor/products/utils/handlers";
import {
createAttributeChangeHandler,
createAttributeMultiChangeHandler
} from "@saleor/products/utils/handlers";
import {
validateCostPrice,
validatePrice
@ -20,11 +29,10 @@ import { mapMetadataItemToInput } from "@saleor/utils/maps";
import getMetadata from "@saleor/utils/metadata/getMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import { diff } from "fast-array-diff";
import React from "react";
import React, { useEffect } from "react";
import handleFormSubmit from "../../../utils/handlers/handleFormSubmit";
import { ProductStockInput } from "../ProductStocks";
import { VariantAttributeInputData } from "../ProductVariantAttributes";
export interface ProductVariantUpdateFormData extends MetadataFormData {
sku: string;
@ -32,13 +40,14 @@ export interface ProductVariantUpdateFormData extends MetadataFormData {
weight: string;
}
export interface ProductVariantUpdateData extends ProductVariantUpdateFormData {
attributes: FormsetData<VariantAttributeInputData, string>;
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
attributes: AttributeInput[];
stocks: ProductStockInput[];
}
export interface ProductVariantUpdateSubmitData
extends ProductVariantUpdateFormData {
attributes: FormsetData<VariantAttributeInputData, string>;
attributes: AttributeInput[];
attributesWithNewFileValue: FormsetData<null, File>;
addStocks: ProductStockInput[];
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
updateStocks: ProductStockInput[];
@ -50,17 +59,24 @@ export interface UseProductVariantUpdateFormOpts {
currentChannels: ChannelPriceData[];
}
interface ProductVariantUpdateHandlers
extends Record<
| "changeStock"
| "selectAttribute"
| "selectAttributeMultiple"
| "changeChannels",
FormsetChange
>,
Record<"selectAttributeFile", FormsetChange<File>>,
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<null, File>([]);
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<null, File>
) =>
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;

View file

@ -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<ProductAttributeInputData, string[]> = [
const attributes: FormsetData<AttributeInputData, string[]> = [
{
data: {
inputType: AttributeInputTypeEnum.DROPDOWN,
@ -12,6 +12,7 @@ const attributes: FormsetData<ProductAttributeInputData, string[]> = [
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<ProductAttributeInputData, string[]> = [
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<ProductAttributeInputData, string[]> = [
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: []
}
];

View file

@ -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<string[]>,
attributes: FormsetData<ProductAttributeInputData, string[]>,
attributes: FormsetData<AttributeInputData, string[]>,
triggerChange: () => void
): FormsetChange<string> {
return (attributeId: string, value: string) => {
@ -120,8 +124,37 @@ export function createAttributeMultiChangeHandler(
};
}
export function createAttributeFileChangeHandler(
changeAttributeData: FormsetChange<string[]>,
attributesWithNewFileValue: FormsetData<FormsetData<null, File>>,
addAttributeNewFileValue: (data: FormsetAtomicData<null, File>) => void,
changeAttributeNewFileValue: FormsetChange<File>,
triggerChange: () => void
): FormsetChange<File> {
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<ProductAttributeInputData>) => void,
setAttributes: (data: FormsetData<AttributeInputData>) => void,
setProductType: (productType: ProductType) => void,
productTypeChoiceList: ProductType[],
triggerChange: () => void

View file

@ -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<ProductCreateProps> = ({ params }) => {
navigate(productUrl(productId));
};
const [uploadFile, uploadFileOpts] = useFileUploadMutation({});
const [updateChannels, updateChannelsOpts] = useProductChannelListingUpdate(
{}
);
@ -157,6 +160,7 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ 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<ProductCreateProps> = ({ params }) => {
collections={(searchCollectionOpts?.data?.search?.edges || []).map(
edge => edge.node
)}
disabled={
loading={
uploadFileOpts.loading ||
productCreateOpts.loading ||
productVariantCreateOpts.loading ||
updateChannelsOpts.loading ||

View file

@ -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<MutationFetchResult<FileUpload>>,
productCreate: (
variables: ProductCreateVariables
) => Promise<MutationFetchResult<ProductCreate>>,
@ -69,12 +86,25 @@ export function createHandler(
}) => Promise<MutationFetchResult<ProductDelete>>
) {
return async (formData: ProductCreateData) => {
let errors: Array<AttributeErrorFragment | UploadErrorFragment> = [];
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;
}

Some files were not shown because too many files have changed in this diff Show more