Add product variant reference attribute (#2268)

* Handle variant references

* Update schema & types

* Update changelog

* Extract messages

* Fix handlers

* Update schema

* Build types

* Update types

* Improve assign reference value modals
This commit is contained in:
Michał Droń 2022-09-13 12:07:41 +02:00 committed by GitHub
parent ff09d04660
commit 19d5e7e71d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 781 additions and 112 deletions

View file

@ -5,6 +5,7 @@ All notable, unreleased changes to this project will be documented in this file.
## [Unreleased]
- Pass query params in `ORDER_DETAILS_MORE_ACTIONS` and `PRODUCT_DETAILS_MORE_ACTIONS` mounting points - #2100 by @witoszekdev
- Add product variant reference attribute - #2268 by @droniu
## 3.4

View file

@ -7065,6 +7065,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "PRODUCT_VARIANT",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
@ -9104,9 +9110,13 @@
"name": "id",
"description": "ID of the selected attribute.",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
@ -10106,6 +10116,87 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CalculateTaxes",
"description": "Synchronous webhook for calculating checkout/order taxes.\n\nAdded in Saleor 3.7.\n\nNote: this API is currently in Feature Preview and can be subject to changes at later point.",
"fields": [
{
"name": "issuedAt",
"description": "Time of the event.",
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "version",
"description": "Saleor version that triggered the event.",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issuingPrincipal",
"description": "The user or application that triggered the event.",
"args": [],
"type": {
"kind": "UNION",
"name": "IssuingPrincipal",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "recipient",
"description": "The application receiving the webhook.",
"args": [],
"type": {
"kind": "OBJECT",
"name": "App",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "taxBase",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "TaxableObject",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
{
"kind": "INTERFACE",
"name": "Event",
"ofType": null
}
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CardInput",
@ -25088,6 +25179,11 @@
"name": "AttributeValueUpdated",
"ofType": null
},
{
"kind": "OBJECT",
"name": "CalculateTaxes",
"ofType": null
},
{
"kind": "OBJECT",
"name": "CategoryCreated",
@ -56121,7 +56217,7 @@
},
{
"name": "deliveryMethod",
"description": "The delivery method selected for this checkout.\n\nAdded in Saleor 3.1.\n\nNote: this API is currently in Feature Preview and can be subject to changes at later point.",
"description": "The delivery method selected for this order.\n\nAdded in Saleor 3.1.\n\nNote: this API is currently in Feature Preview and can be subject to changes at later point.",
"args": [],
"type": {
"kind": "UNION",
@ -94029,6 +94125,48 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "UNION",
"name": "TaxSourceLine",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": [
{
"kind": "OBJECT",
"name": "CheckoutLine",
"ofType": null
},
{
"kind": "OBJECT",
"name": "OrderLine",
"ofType": null
}
]
},
{
"kind": "UNION",
"name": "TaxSourceObject",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": [
{
"kind": "OBJECT",
"name": "Checkout",
"ofType": null
},
{
"kind": "OBJECT",
"name": "Order",
"ofType": null
}
]
},
{
"kind": "OBJECT",
"name": "TaxType",
@ -94064,6 +94202,331 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TaxableObject",
"description": "Taxable object.",
"fields": [
{
"name": "sourceObject",
"description": "The source object related to this tax object.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "UNION",
"name": "TaxSourceObject",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pricesEnteredWithTax",
"description": "Determines if prices contain entered tax..",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "currency",
"description": "The currency of the object.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "shippingPrice",
"description": "The price of shipping method.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Money",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "address",
"description": "The address data.",
"args": [],
"type": {
"kind": "OBJECT",
"name": "Address",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "discounts",
"description": "List of discounts.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "TaxableObjectDiscount",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "lines",
"description": "List of lines assigned to the object.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "TaxableObjectLine",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "channel",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Channel",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TaxableObjectDiscount",
"description": "Taxable object discount.",
"fields": [
{
"name": "name",
"description": "The name of the discount.",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "amount",
"description": "The amount of the discount.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Money",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TaxableObjectLine",
"description": null,
"fields": [
{
"name": "sourceLine",
"description": "The source line related to this tax line.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "UNION",
"name": "TaxSourceLine",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "quantity",
"description": "Number of items.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "chargeTaxes",
"description": "Determines if taxes are being charged for the product.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "productName",
"description": "The product name.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "variantName",
"description": "The variant name.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "productSku",
"description": "The product sku.",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "unitPrice",
"description": "Price of the single item in the order line.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Money",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "totalPrice",
"description": "Price of the order line.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Money",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TaxedMoney",

View file

@ -889,6 +889,10 @@
"context": "alert",
"string": "Warehouse limit reached"
},
"5I7Lc2": {
"context": "dialog header",
"string": "Assign page"
},
"5JT4v2": {
"context": "dialog header",
"string": "Send Invoice"
@ -2265,10 +2269,6 @@
"GOdq5V": {
"string": "Catalog"
},
"GUlwXU": {
"context": "dialog header",
"string": "Assign Attribute Value"
},
"GVM/fi": {
"context": "order history message",
"string": "Payment was authorized"
@ -3235,10 +3235,6 @@
"context": "navigator placeholder",
"string": "Type Command"
},
"NsgWhZ": {
"context": "placeholder",
"string": "Search by value name, etc..."
},
"NtFVFS": {
"context": "weight",
"string": "{value} {unit}"
@ -3293,6 +3289,10 @@
"context": "WarehouseSettings disabled warehouse label",
"string": "Disabled"
},
"OFW7nq": {
"context": "placeholder",
"string": "Search by page name, etc..."
},
"OGemtu": {
"context": "payment status",
"string": "Partially refunded"
@ -3757,10 +3757,6 @@
"RlfqSV": {
"string": "No orders found"
},
"RoKOQJ": {
"context": "label",
"string": "Search Attribute Value"
},
"Rp/Okl": {
"string": "Shipping Weight Unit"
},
@ -5284,10 +5280,6 @@
"context": "product",
"string": "Digital"
},
"dTCDMn": {
"context": "dialog header",
"string": "Assign Product"
},
"dTCWqt": {
"string": "You are about to end your products preorder. You have sold {variantGlobalSoldUnits} units of this variant. Sold units will be allocated at appropriate warehouses. Remember to add remaining threshold stock to warehouses."
},
@ -5790,6 +5782,10 @@
"context": "customer",
"string": "Join Date"
},
"idr+JK": {
"context": "assign reference to a page, button",
"string": "Assign and save"
},
"ij7olm": {
"context": "error message",
"string": "This fulfillment cannot be cancelled"
@ -5820,6 +5816,10 @@
"ixjvkM": {
"string": "Weve created your default token. Make sure to copy your new personal access token now. You wont be able to see it again."
},
"izJvcM": {
"context": "label",
"string": "Search pages"
},
"j/vV0n": {
"context": "channel name",
"string": "Channel Name"
@ -5918,6 +5918,10 @@
"juBV+h": {
"string": "End Hour"
},
"juxCV3": {
"context": "dialog header",
"string": "Assign product"
},
"jvKNMP": {
"string": "Discount Code"
},
@ -7262,6 +7266,9 @@
"umsU70": {
"string": "Search Page Type"
},
"un+VWt": {
"string": "Search products"
},
"usSkzP": {
"context": "navigator order mode description",
"string": "Search Orders"
@ -7531,6 +7538,10 @@
"context": "WarehouseSettings private stock label",
"string": "Private Stock"
},
"wsDF7X": {
"context": "product variant attribute entity type",
"string": "Product variants"
},
"wyvzh9": {
"context": "dialog header",
"string": "Publish Pages"
@ -7744,10 +7755,6 @@
"context": "voucher requirements, header",
"string": "Minimum Requirements"
},
"ylobu9": {
"context": "assign reference to product, button",
"string": "Assign"
},
"ypW4Mi": {
"context": "button refreshing page",
"string": "Refresh page"

View file

@ -1384,6 +1384,7 @@ type AttributeDeleted implements Event {
enum AttributeEntityTypeEnum {
PAGE
PRODUCT
PRODUCT_VARIANT
}
type AttributeError {
@ -1823,7 +1824,7 @@ input AttributeValueFilterInput {
input AttributeValueInput {
"""ID of the selected attribute."""
id: ID
id: ID!
"""
The value or slug of an attribute to resolve. If the passed value is non-existent, it will be created.
@ -2052,6 +2053,28 @@ type BulkStockError {
index: Int
}
"""
Synchronous webhook for calculating checkout/order taxes.
Added in Saleor 3.7.
Note: this API is currently in Feature Preview and can be subject to changes at later point.
"""
type CalculateTaxes implements Event {
"""Time of the event."""
issuedAt: DateTime
"""Saleor version that triggered the event."""
version: String
"""The user or application that triggered the event."""
issuingPrincipal: IssuingPrincipal
"""The application receiving the webhook."""
recipient: App
taxBase: TaxableObject!
}
input CardInput {
"""
Payment method nonce, a token returned by the appropriate provider's SDK.
@ -12540,7 +12563,7 @@ type Order implements Node & ObjectWithMetadata {
isShippingRequired: Boolean!
"""
The delivery method selected for this checkout.
The delivery method selected for this order.
Added in Saleor 3.1.
@ -21189,6 +21212,10 @@ type Subscription {
event: Event
}
union TaxSourceLine = CheckoutLine | OrderLine
union TaxSourceObject = Checkout | Order
"""Representation of tax types fetched from tax gateway."""
type TaxType {
"""Description of the tax type."""
@ -21198,6 +21225,66 @@ type TaxType {
taxCode: String
}
"""Taxable object."""
type TaxableObject {
"""The source object related to this tax object."""
sourceObject: TaxSourceObject!
"""Determines if prices contain entered tax.."""
pricesEnteredWithTax: Boolean!
"""The currency of the object."""
currency: String!
"""The price of shipping method."""
shippingPrice: Money!
"""The address data."""
address: Address
"""List of discounts."""
discounts: [TaxableObjectDiscount!]!
"""List of lines assigned to the object."""
lines: [TaxableObjectLine!]!
channel: Channel!
}
"""Taxable object discount."""
type TaxableObjectDiscount {
"""The name of the discount."""
name: String
"""The amount of the discount."""
amount: Money!
}
type TaxableObjectLine {
"""The source line related to this tax line."""
sourceLine: TaxSourceLine!
"""Number of items."""
quantity: Int!
"""Determines if taxes are being charged for the product."""
chargeTaxes: Boolean!
"""The product name."""
productName: String!
"""The variant name."""
variantName: String!
"""The product sku."""
productSku: String
"""Price of the single item in the order line."""
unitPrice: Money!
"""Price of the order line."""
totalPrice: Money!
}
"""
Represents a monetary value with taxes. In cases where taxes were not applied, net and gross values will be equal.
"""

View file

@ -33,6 +33,11 @@ const entityTypeMessages = defineMessages({
defaultMessage: "Products",
description: "product attribute entity type",
},
productVariant: {
id: "wsDF7X",
defaultMessage: "Product variants",
description: "product variant attribute entity type",
},
});
const useStyles = makeStyles(
@ -129,6 +134,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = props => {
label: intl.formatMessage(entityTypeMessages.product),
value: AttributeEntityTypeEnum.PRODUCT,
},
{
label: intl.formatMessage(entityTypeMessages.productVariant),
value: AttributeEntityTypeEnum.PRODUCT_VARIANT,
},
];
const formApiErrors = getFormErrors(

View file

@ -420,6 +420,27 @@ export const getProductReferenceAttributeDisplayData = (
},
});
export const getProductVariantReferenceAttributeDisplayData = (
attribute: AttributeInput,
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>,
) => ({
...attribute,
data: {
...attribute.data,
references:
referenceProducts?.length > 0 && attribute.value?.length > 0
? mapNodeToChoice(
attribute.value.map(value => {
const reference = mapReferenceProductsToVariants(
referenceProducts,
).find(reference => reference.id === value);
return { ...reference };
}),
)
: [],
},
});
export const getReferenceAttributeDisplayData = (
attribute: AttributeInput,
referencePages: RelayToFlat<SearchPagesQuery["search"]>,
@ -432,6 +453,13 @@ export const getReferenceAttributeDisplayData = (
attribute,
referenceProducts,
);
} else if (
attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT_VARIANT
) {
return getProductVariantReferenceAttributeDisplayData(
attribute,
referenceProducts,
);
}
};
@ -464,22 +492,18 @@ export const getSelectedReferencesFromAttribute = <T extends Node>(
!attribute?.value?.some(selectedValue => selectedValue === value.id),
) || [];
export const getAttributeValuesFromReferences = (
export const getReferenceAttributeEntityTypeFromAttribute = (
attributeId: string,
attributes?: AttributeInput[],
referencePages?: RelayToFlat<SearchPagesQuery["search"]>,
referenceProducts?: RelayToFlat<SearchProductsQuery["search"]>,
) => {
const attribute = attributes?.find(attribute => attribute.id === attributeId);
): AttributeEntityTypeEnum | undefined =>
attributes?.find(attribute => attribute.id === attributeId)?.data?.entityType;
if (attribute?.data?.entityType === AttributeEntityTypeEnum.PAGE) {
return mapPagesToChoices(
getSelectedReferencesFromAttribute(attribute, referencePages),
);
} else if (attribute?.data?.entityType === AttributeEntityTypeEnum.PRODUCT) {
return mapNodeToChoice(
getSelectedReferencesFromAttribute(attribute, referenceProducts),
);
}
return [];
};
export const mapReferenceProductsToVariants = (
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>,
) =>
referenceProducts.flatMap(product =>
product.variants.map(variant => ({
...variant,
name: product.name + " " + variant.name,
})),
);

View file

@ -88,7 +88,10 @@ export function createFetchReferencesHandler(
) {
fetchReferencePages(value);
} else if (
attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT &&
[
AttributeEntityTypeEnum.PRODUCT,
AttributeEntityTypeEnum.PRODUCT_VARIANT,
].includes(attribute.data.entityType) &&
fetchReferenceProducts
) {
fetchReferenceProducts(value);
@ -112,7 +115,12 @@ export function createFetchMoreReferencesHandler(
if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) {
return fetchMoreReferencePages;
} else if (attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT) {
} else if (
[
AttributeEntityTypeEnum.PRODUCT,
AttributeEntityTypeEnum.PRODUCT_VARIANT,
].includes(attribute.data.entityType)
) {
return fetchMoreReferenceProducts;
}
}

View file

@ -1,65 +1,69 @@
import { AttributeReference } from "@saleor/attributes/utils/data";
import { AttributeEntityTypeEnum, SearchPagesQuery } from "@saleor/graphql";
import { RelayToFlat } from "@saleor/types";
import React from "react";
import { defineMessages, useIntl } from "react-intl";
import AssignContainerDialog, {
AssignContainerDialogProps,
} from "../AssignContainerDialog";
import AssignContainerDialog from "../AssignContainerDialog";
import AssignProductDialog, {
AssignProductDialogProps,
} from "../AssignProductDialog";
import AssignVariantDialog from "../AssignVariantDialog";
const messages = defineMessages({
const pagesMessages = defineMessages({
confirmBtn: {
id: "ylobu9",
defaultMessage: "Assign",
description: "assign reference to product, button",
id: "idr+JK",
defaultMessage: "Assign and save",
description: "assign reference to a page, button",
},
header: {
id: "GUlwXU",
defaultMessage: "Assign Attribute Value",
id: "5I7Lc2",
defaultMessage: "Assign page",
description: "dialog header",
},
searchLabel: {
id: "RoKOQJ",
defaultMessage: "Search Attribute Value",
id: "izJvcM",
defaultMessage: "Search pages",
description: "label",
},
searchPlaceholder: {
id: "NsgWhZ",
defaultMessage: "Search by value name, etc...",
id: "OFW7nq",
defaultMessage: "Search by page name, etc...",
description: "placeholder",
},
});
interface AssignAttributeValueDialogProps
extends Omit<
AssignContainerDialogProps,
"containers" | "title" | "search" | "confirmButtonState" | "labels"
> {
attributeValues: AttributeReference[];
}
type AssignAttributeValueDialogProps = AssignProductDialogProps & {
entityType: AttributeEntityTypeEnum;
pages: RelayToFlat<SearchPagesQuery["search"]>;
};
const AssignAttributeValueDialog: React.FC<AssignAttributeValueDialogProps> = ({
attributeValues,
entityType,
pages,
products,
...rest
}) => {
const intl = useIntl();
return (
<AssignContainerDialog
containers={attributeValues.map(value => ({
id: value.value,
name: value.label,
}))}
labels={{
confirmBtn: intl.formatMessage(messages.confirmBtn),
label: intl.formatMessage(messages.searchLabel),
placeholder: intl.formatMessage(messages.searchPlaceholder),
title: intl.formatMessage(messages.header),
}}
confirmButtonState="default"
{...rest}
/>
);
switch (entityType) {
case AttributeEntityTypeEnum.PAGE:
return (
<AssignContainerDialog
containers={pages.map(page => ({ id: page.id, name: page.title }))}
labels={{
confirmBtn: intl.formatMessage(pagesMessages.confirmBtn),
label: intl.formatMessage(pagesMessages.searchLabel),
placeholder: intl.formatMessage(pagesMessages.searchPlaceholder),
title: intl.formatMessage(pagesMessages.header),
}}
{...rest}
/>
);
case AttributeEntityTypeEnum.PRODUCT:
return <AssignProductDialog products={products} {...rest} />;
case AttributeEntityTypeEnum.PRODUCT_VARIANT:
return <AssignVariantDialog products={products} {...rest} />;
}
};
AssignAttributeValueDialog.displayName = "AssignAttributeValueDialog";
export default AssignAttributeValueDialog;

View file

@ -2,8 +2,8 @@ import { defineMessages } from "react-intl";
export const messages = defineMessages({
assignVariantDialogHeader: {
id: "dTCDMn",
defaultMessage: "Assign Product",
id: "juxCV3",
defaultMessage: "Assign product",
description: "dialog header",
},
assignProductDialogButton: {
@ -12,8 +12,8 @@ export const messages = defineMessages({
description: "button",
},
assignProductDialogContent: {
id: "/TF6BZ",
defaultMessage: "Search Products",
id: "un+VWt",
defaultMessage: "Search products",
},
assignProductDialogSearch: {
id: "SHm7ee",

View file

@ -45,7 +45,7 @@ export interface AssignVariantDialogProps extends FetchMoreProps, DialogProps {
products: RelayToFlat<SearchProductsQuery["search"]>;
loading: boolean;
onFetch: (value: string) => void;
onSubmit: (data: SearchVariant[]) => void;
onSubmit: (data: string[]) => void;
}
const scrollableTargetId = "assignVariantScrollableDialog";
@ -84,7 +84,7 @@ const AssignVariantDialog: React.FC<AssignVariantDialogProps> = props => {
)
: [];
const handleSubmit = () => onSubmit(variants);
const handleSubmit = () => onSubmit(variants.map(variant => variant.id));
return (
<Dialog

View file

@ -390,7 +390,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
...paginationState,
id,
input: {
variants: variants.map(variant => variant.id),
variants,
},
},
})

View file

@ -25,6 +25,7 @@
"AttributeValueCreated",
"AttributeValueDeleted",
"AttributeValueUpdated",
"CalculateTaxes",
"CategoryCreated",
"CategoryDeleted",
"CategoryUpdated",
@ -237,6 +238,14 @@
"Voucher",
"Warehouse"
],
"TaxSourceLine": [
"CheckoutLine",
"OrderLine"
],
"TaxSourceObject": [
"Checkout",
"Order"
],
"TranslatableItem": [
"ProductTranslatableContent",
"CollectionTranslatableContent",

View file

@ -607,6 +607,14 @@ export type BulkStockErrorFieldPolicy = {
values?: FieldPolicy<any> | FieldReadFunction<any>,
index?: FieldPolicy<any> | FieldReadFunction<any>
};
export type CalculateTaxesKeySpecifier = ('issuedAt' | 'version' | 'issuingPrincipal' | 'recipient' | 'taxBase' | CalculateTaxesKeySpecifier)[];
export type CalculateTaxesFieldPolicy = {
issuedAt?: FieldPolicy<any> | FieldReadFunction<any>,
version?: FieldPolicy<any> | FieldReadFunction<any>,
issuingPrincipal?: FieldPolicy<any> | FieldReadFunction<any>,
recipient?: FieldPolicy<any> | FieldReadFunction<any>,
taxBase?: FieldPolicy<any> | FieldReadFunction<any>
};
export type CategoryKeySpecifier = ('id' | 'privateMetadata' | 'privateMetafield' | 'privateMetafields' | 'metadata' | 'metafield' | 'metafields' | 'seoTitle' | 'seoDescription' | 'name' | 'description' | 'slug' | 'parent' | 'level' | 'descriptionJson' | 'ancestors' | 'products' | 'children' | 'backgroundImage' | 'translation' | CategoryKeySpecifier)[];
export type CategoryFieldPolicy = {
id?: FieldPolicy<any> | FieldReadFunction<any>,
@ -4634,6 +4642,33 @@ export type TaxTypeFieldPolicy = {
description?: FieldPolicy<any> | FieldReadFunction<any>,
taxCode?: FieldPolicy<any> | FieldReadFunction<any>
};
export type TaxableObjectKeySpecifier = ('sourceObject' | 'pricesEnteredWithTax' | 'currency' | 'shippingPrice' | 'address' | 'discounts' | 'lines' | 'channel' | TaxableObjectKeySpecifier)[];
export type TaxableObjectFieldPolicy = {
sourceObject?: FieldPolicy<any> | FieldReadFunction<any>,
pricesEnteredWithTax?: FieldPolicy<any> | FieldReadFunction<any>,
currency?: FieldPolicy<any> | FieldReadFunction<any>,
shippingPrice?: FieldPolicy<any> | FieldReadFunction<any>,
address?: FieldPolicy<any> | FieldReadFunction<any>,
discounts?: FieldPolicy<any> | FieldReadFunction<any>,
lines?: FieldPolicy<any> | FieldReadFunction<any>,
channel?: FieldPolicy<any> | FieldReadFunction<any>
};
export type TaxableObjectDiscountKeySpecifier = ('name' | 'amount' | TaxableObjectDiscountKeySpecifier)[];
export type TaxableObjectDiscountFieldPolicy = {
name?: FieldPolicy<any> | FieldReadFunction<any>,
amount?: FieldPolicy<any> | FieldReadFunction<any>
};
export type TaxableObjectLineKeySpecifier = ('sourceLine' | 'quantity' | 'chargeTaxes' | 'productName' | 'variantName' | 'productSku' | 'unitPrice' | 'totalPrice' | TaxableObjectLineKeySpecifier)[];
export type TaxableObjectLineFieldPolicy = {
sourceLine?: FieldPolicy<any> | FieldReadFunction<any>,
quantity?: FieldPolicy<any> | FieldReadFunction<any>,
chargeTaxes?: FieldPolicy<any> | FieldReadFunction<any>,
productName?: FieldPolicy<any> | FieldReadFunction<any>,
variantName?: FieldPolicy<any> | FieldReadFunction<any>,
productSku?: FieldPolicy<any> | FieldReadFunction<any>,
unitPrice?: FieldPolicy<any> | FieldReadFunction<any>,
totalPrice?: FieldPolicy<any> | FieldReadFunction<any>
};
export type TaxedMoneyKeySpecifier = ('currency' | 'gross' | 'net' | 'tax' | TaxedMoneyKeySpecifier)[];
export type TaxedMoneyFieldPolicy = {
currency?: FieldPolicy<any> | FieldReadFunction<any>,
@ -5498,6 +5533,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | BulkStockErrorKeySpecifier | (() => undefined | BulkStockErrorKeySpecifier),
fields?: BulkStockErrorFieldPolicy,
},
CalculateTaxes?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | CalculateTaxesKeySpecifier | (() => undefined | CalculateTaxesKeySpecifier),
fields?: CalculateTaxesFieldPolicy,
},
Category?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | CategoryKeySpecifier | (() => undefined | CategoryKeySpecifier),
fields?: CategoryFieldPolicy,
@ -7398,6 +7437,18 @@ export type StrictTypedTypePolicies = {
keyFields?: false | TaxTypeKeySpecifier | (() => undefined | TaxTypeKeySpecifier),
fields?: TaxTypeFieldPolicy,
},
TaxableObject?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | TaxableObjectKeySpecifier | (() => undefined | TaxableObjectKeySpecifier),
fields?: TaxableObjectFieldPolicy,
},
TaxableObjectDiscount?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | TaxableObjectDiscountKeySpecifier | (() => undefined | TaxableObjectDiscountKeySpecifier),
fields?: TaxableObjectDiscountFieldPolicy,
},
TaxableObjectLine?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | TaxableObjectLineKeySpecifier | (() => undefined | TaxableObjectLineKeySpecifier),
fields?: TaxableObjectLineFieldPolicy,
},
TaxedMoney?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | TaxedMoneyKeySpecifier | (() => undefined | TaxedMoneyKeySpecifier),
fields?: TaxedMoneyFieldPolicy,

View file

@ -334,7 +334,8 @@ export type AttributeCreateInput = {
/** An enumeration. */
export enum AttributeEntityTypeEnum {
PAGE = 'PAGE',
PRODUCT = 'PRODUCT'
PRODUCT = 'PRODUCT',
PRODUCT_VARIANT = 'PRODUCT_VARIANT'
}
/** An enumeration. */
@ -485,7 +486,7 @@ export type AttributeValueFilterInput = {
export type AttributeValueInput = {
/** ID of the selected attribute. */
id?: InputMaybe<Scalars['ID']>;
id: Scalars['ID'];
/** The value or slug of an attribute to resolve. If the passed value is non-existent, it will be created. */
values?: InputMaybe<Array<Scalars['String']>>;
/** URL of the file attribute. Every time, a new value is created. */

View file

@ -1,5 +1,5 @@
import {
getAttributeValuesFromReferences,
getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues,
} from "@saleor/attributes/utils/data";
import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog";
@ -264,12 +264,13 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
entityType={getReferenceAttributeEntityTypeFromAttribute(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts,
)}
confirmButtonState={"default"}
products={referenceProducts}
pages={referencePages}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}

View file

@ -1,5 +1,5 @@
import {
getAttributeValuesFromReferences,
getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues,
} from "@saleor/attributes/utils/data";
import CannotDefineChannelsAvailabilityCard from "@saleor/channels/components/CannotDefineChannelsAvailabilityCard/CannotDefineChannelsAvailabilityCard";
@ -376,12 +376,13 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
entityType={getReferenceAttributeEntityTypeFromAttribute(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts,
)}
confirmButtonState={"default"}
products={referenceProducts}
pages={referencePages}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}

View file

@ -5,7 +5,7 @@ import {
useExtensions,
} from "@saleor/apps/useExtensions";
import {
getAttributeValuesFromReferences,
getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues,
} from "@saleor/attributes/utils/data";
import { ChannelData } from "@saleor/channels/utils";
@ -525,12 +525,13 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
entityType={getReferenceAttributeEntityTypeFromAttribute(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts,
)}
confirmButtonState={"default"}
products={referenceProducts}
pages={referencePages}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}

View file

@ -1,5 +1,5 @@
import {
getAttributeValuesFromReferences,
getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues,
} from "@saleor/attributes/utils/data";
import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog";
@ -281,12 +281,13 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
entityType={getReferenceAttributeEntityTypeFromAttribute(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts,
)}
confirmButtonState={"default"}
products={referenceProducts}
pages={referencePages}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}

View file

@ -1,5 +1,5 @@
import {
getAttributeValuesFromReferences,
getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues,
} from "@saleor/attributes/utils/data";
import { ChannelPriceData } from "@saleor/channels/utils";
@ -381,12 +381,13 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
entityType={getReferenceAttributeEntityTypeFromAttribute(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts,
)}
confirmButtonState={"default"}
products={referenceProducts}
pages={referencePages}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}