Add variants to sale view (#1420)
* Update schema and biuld types for sale per variant * Create variant search module and generate types for it * Add listing component for sale view * Create dialog for variant assignment * Expand sale page with vairnats * Add new sale fixtures * Add transaltions for variants on sale view * Update snapshot * Refactor sales dialogs and tables, move styles and ittl to local files * Rework search dialog. Create item/subitem selectable table for variants, update spapshot * Adjust table columns width * Standardize the tables * Unify messages * Drop whole variant object in favor of just ids, simplify filtring functions * Update snapshots Co-authored-by: Jakub Majorek <majorek.jakub@gmail.com>
This commit is contained in:
parent
2407ae6f76
commit
ec230e55c0
50 changed files with 2006 additions and 392 deletions
|
@ -492,17 +492,9 @@
|
|||
"context": "number of products",
|
||||
"string": "Products ({quantity})"
|
||||
},
|
||||
"saleDetailsUnassignCategory": {
|
||||
"context": "unassign category from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"saleDetailsUnassignCollection": {
|
||||
"context": "unassign collection from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"saleDetailsUnassignProduct": {
|
||||
"context": "unassign product from sale, button",
|
||||
"string": "Unassign"
|
||||
"saleDetailsPageVariantsQuantity": {
|
||||
"context": "number of variants",
|
||||
"string": "Variants ({quantity})"
|
||||
},
|
||||
"set availability date": {
|
||||
"context": "product availability date label",
|
||||
|
@ -1889,44 +1881,69 @@
|
|||
"context": "placeholder",
|
||||
"string": "Search by value name, etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_3125506097": {
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_assignCategoryDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Category"
|
||||
},
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_3690273268": {
|
||||
"string": "Search by category name, etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_3841025483": {
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_assignCategoryDialogLabel": {
|
||||
"context": "dialog header",
|
||||
"string": "Search Category"
|
||||
},
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_2605414502": {
|
||||
"string": "Search by collection name, etc..."
|
||||
"src_dot_components_dot_AssignCategoryDialog_dot_assignCategoryDialogPlaceholder": {
|
||||
"context": "dialog search placeholder",
|
||||
"string": "Search by category name, etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_3992923611": {
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_assignCollectionDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Collection"
|
||||
},
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_4057224233": {
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_assignCollectionDialogLabel": {
|
||||
"context": "dialog header",
|
||||
"string": "Search Collection"
|
||||
},
|
||||
"src_dot_components_dot_AssignContainerDialog_dot_1731102929": {
|
||||
"src_dot_components_dot_AssignCollectionDialog_dot_assignCollectionDialogPlaceholder": {
|
||||
"context": "dialog search placeholder",
|
||||
"string": "Search by collection name, etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignContainerDialog_dot_assignContainerDialogButton": {
|
||||
"context": "button",
|
||||
"string": "Assign"
|
||||
},
|
||||
"src_dot_components_dot_AssignProductDialog_dot_2100305525": {
|
||||
"src_dot_components_dot_AssignProductDialog_dot_assignProductDialogButton": {
|
||||
"context": "button",
|
||||
"string": "Assign products"
|
||||
"string": "Assign"
|
||||
},
|
||||
"src_dot_components_dot_AssignProductDialog_dot_2336947364": {
|
||||
"string": "Search by product name, attribute, product type etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignProductDialog_dot_2850255786": {
|
||||
"src_dot_components_dot_AssignProductDialog_dot_assignProductDialogContent": {
|
||||
"string": "Search Products"
|
||||
},
|
||||
"src_dot_components_dot_AssignProductDialog_dot_649693468": {
|
||||
"src_dot_components_dot_AssignProductDialog_dot_assignProductDialogSearch": {
|
||||
"string": "Search by product name, attribute, product type etc..."
|
||||
},
|
||||
"src_dot_components_dot_AssignProductDialog_dot_assignVariantDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Product"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_3284796469": {
|
||||
"string": "No products available in order channel matching given query"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_assignVariantDialogButton": {
|
||||
"context": "button",
|
||||
"string": "Assign"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_assignVariantDialogContent": {
|
||||
"string": "Search Variants"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_assignVariantDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Variant"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_assignVariantDialogSKU": {
|
||||
"context": "variant sku",
|
||||
"string": "SKU {sku}"
|
||||
},
|
||||
"src_dot_components_dot_AssignVariantDialog_dot_assignVariantDialogSearch": {
|
||||
"string": "Search by product name, attribute, product type etc..."
|
||||
},
|
||||
"src_dot_components_dot_AttributeUnassignDialog_dot_2037985699": {
|
||||
"string": "Are you sure you want to unassign {attributeName} from {itemTypeName}?"
|
||||
},
|
||||
|
@ -2835,42 +2852,46 @@
|
|||
"src_dot_discounts": {
|
||||
"string": "Discounts"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_1567318211": {
|
||||
"string": "Category name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_1681512341": {
|
||||
"context": "section header",
|
||||
"string": "Eligible Categories"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_2054128296": {
|
||||
"string": "No categories found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_2968663655": {
|
||||
"context": "number of products",
|
||||
"string": "Products"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_3973677075": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_discountCategoriesButton": {
|
||||
"context": "button",
|
||||
"string": "Assign categories"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_1035511604": {
|
||||
"context": "button",
|
||||
"string": "Assign collections"
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_discountCategoriesHeader": {
|
||||
"context": "section header",
|
||||
"string": "Eligible Categories"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_2137803833": {
|
||||
"string": "No collections found"
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_discountCategoriesNotFound": {
|
||||
"context": "no categories",
|
||||
"string": "No categories found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_2968663655": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_discountCategoriesTableProductHeader": {
|
||||
"context": "table head",
|
||||
"string": "Category Name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCategories_dot_discountCategoriesTableProductNumber": {
|
||||
"context": "number of products",
|
||||
"string": "Products"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_3011396316": {
|
||||
"string": "Collection name"
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_discountCollectionsButton": {
|
||||
"context": "button",
|
||||
"string": "Assign collections"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_452750900": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_discountCollectionsHeader": {
|
||||
"context": "section header",
|
||||
"string": "Eligible Collections"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_discountCollectionsNotFound": {
|
||||
"context": "no collections",
|
||||
"string": "No collections found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_discountCollectionsTableProductHeader": {
|
||||
"context": "table head",
|
||||
"string": "Collection Name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCollections_dot_discountCollectionsTableProductNumber": {
|
||||
"context": "number of products",
|
||||
"string": "Products"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountCountrySelectDialog_dot_1585396479": {
|
||||
"context": "dialog header",
|
||||
"string": "Assign Countries"
|
||||
|
@ -2902,26 +2923,53 @@
|
|||
"context": "time during discount is active, header",
|
||||
"string": "Active Dates"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_1657559629": {
|
||||
"string": "No products found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_2100305525": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsButton": {
|
||||
"context": "button",
|
||||
"string": "Assign products"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_2697405188": {
|
||||
"string": "Product Name"
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsHeader": {
|
||||
"context": "section header",
|
||||
"string": "Eligible Products"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_3326160357": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsNotFound": {
|
||||
"context": "no products",
|
||||
"string": "No products found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsTableAvailabilityHeader": {
|
||||
"context": "product availability",
|
||||
"string": "Availability"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_4257289053": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsTableProductHeader": {
|
||||
"context": "table head",
|
||||
"string": "Product Name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_discountProductsTableTypeHeader": {
|
||||
"context": "product type",
|
||||
"string": "Product Type"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountProducts_dot_919175218": {
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsButton": {
|
||||
"context": "button",
|
||||
"string": "Assign variants"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsHeader": {
|
||||
"context": "section header",
|
||||
"string": "Eligible Products"
|
||||
"string": "Eligible Variants"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsNotFound": {
|
||||
"context": "no variants",
|
||||
"string": "No variants found"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsTableProductHeader": {
|
||||
"context": "table head",
|
||||
"string": "Product Name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsTableTypeHeader": {
|
||||
"context": "table head",
|
||||
"string": "Product Type"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_DiscountVariants_dot_discountVariantsTableVariantHeader": {
|
||||
"context": "table head",
|
||||
"string": "Variant Name"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_SaleCreatePage_dot_3866518732": {
|
||||
"context": "page header",
|
||||
|
@ -3288,44 +3336,74 @@
|
|||
"src_dot_discounts_dot_views_dot_SaleCreate_dot_480188715": {
|
||||
"string": "Manage Sales Channel Availability"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_1457489953": {
|
||||
"context": "dialog content",
|
||||
"string": "Are you sure you want to delete {saleName}?"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_1827854264": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Categories From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_1952217501": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Collections From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_2353723060": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this category?} other{Are you sure you want to unassign {displayQuantity} categories?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_2534378844": {
|
||||
"string": "Removed sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_3215481647": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this product?} other{Are you sure you want to unassign {displayQuantity} products?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_3395246518": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Products From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_3823295269": {
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsChannelAvailabilityDialogHeader": {
|
||||
"context": "channel availability dialog header",
|
||||
"string": "Manage Channel Availability"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_506030254": {
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsSaleDelate": {
|
||||
"context": "sale Details delete button",
|
||||
"string": "Removed sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsSaleDeleteDialog": {
|
||||
"context": "dialog content",
|
||||
"string": "Removed sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsSaleDeleteDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Delete Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_767268203": {
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCategory": {
|
||||
"context": "unassign category from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCategoryDialog": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this category?} other{Are you sure you want to unassign {displayQuantity} categories?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCategoryDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Categories From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCollection": {
|
||||
"context": "unassign collection from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCollectionDialog": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this collection?} other{Are you sure you want to unassign {displayQuantity} collections?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignCollectionDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Collection From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignDialogDelete": {
|
||||
"context": "dialog content",
|
||||
"string": "Are you sure you want to delete {saleName}?"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignProduct": {
|
||||
"context": "unassign product from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignProductDialog": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this product?} other{Are you sure you want to unassign {displayQuantity} products?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignProductDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Product From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignVariant": {
|
||||
"context": "unassign variant from sale, button",
|
||||
"string": "Unassign"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignVariantDialog": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this variant?} other{Are you sure you want to unassign {displayQuantity} variants?}}"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleDetails_dot_saleDetailsUnassignVariantDialogHeader": {
|
||||
"context": "dialog header",
|
||||
"string": "Unassign Variant From Sale"
|
||||
},
|
||||
"src_dot_discounts_dot_views_dot_SaleList_dot_2809303671": {
|
||||
"context": "dialog header",
|
||||
"string": "Delete Sales"
|
||||
|
|
|
@ -802,6 +802,7 @@ input CatalogueInput {
|
|||
products: [ID]
|
||||
categories: [ID]
|
||||
collections: [ID]
|
||||
variants: [ID]
|
||||
}
|
||||
|
||||
type Category implements Node & ObjectWithMetadata {
|
||||
|
@ -5905,6 +5906,7 @@ type Sale implements Node & ObjectWithMetadata {
|
|||
categories(before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||
collections(before: String, after: String, first: Int, last: Int): CollectionCountableConnection
|
||||
products(before: String, after: String, first: Int, last: Int): ProductCountableConnection
|
||||
variants(before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
||||
translation(languageCode: LanguageCodeEnum!): SaleTranslation
|
||||
channelListings: [SaleChannelListing!]
|
||||
discountValue: Float
|
||||
|
@ -5982,6 +5984,7 @@ input SaleInput {
|
|||
type: DiscountValueTypeEnum
|
||||
value: PositiveDecimal
|
||||
products: [ID]
|
||||
variants: [ID]
|
||||
categories: [ID]
|
||||
collections: [ID]
|
||||
startDate: DateTime
|
||||
|
@ -7297,4 +7300,4 @@ union _Entity = App | Address | User | Group | ProductVariant | Product | Produc
|
|||
|
||||
type _Service {
|
||||
sdl: String
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,7 +358,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||
variables: {
|
||||
...paginationState,
|
||||
collectionId: id,
|
||||
productIds: products.map(product => product.id)
|
||||
productIds: products
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useIntl } from "react-intl";
|
|||
import AssignContainerDialog, {
|
||||
AssignContainerDialogProps
|
||||
} from "../AssignContainerDialog";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface AssignCategoryDialogProps
|
||||
extends Omit<AssignContainerDialogProps, "containers" | "title" | "search"> {
|
||||
|
@ -21,17 +22,12 @@ const AssignCategoryDialog: React.FC<AssignCategoryDialogProps> = ({
|
|||
<AssignContainerDialog
|
||||
containers={categories}
|
||||
search={{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Search Category"
|
||||
}),
|
||||
placeholder: intl.formatMessage({
|
||||
defaultMessage: "Search by category name, etc..."
|
||||
})
|
||||
label: intl.formatMessage(messages.assignCategoryDialogLabel),
|
||||
placeholder: intl.formatMessage(
|
||||
messages.assignCategoryDialogPlaceholder
|
||||
)
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Assign Category",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(messages.assignCategoryDialogHeader)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
|
16
src/components/AssignCategoryDialog/messages.ts
Normal file
16
src/components/AssignCategoryDialog/messages.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
assignCategoryDialogLabel: {
|
||||
defaultMessage: "Search Category",
|
||||
description: "dialog header"
|
||||
},
|
||||
assignCategoryDialogPlaceholder: {
|
||||
defaultMessage: "Search by category name, etc...",
|
||||
description: "dialog search placeholder"
|
||||
},
|
||||
assignCategoryDialogHeader: {
|
||||
defaultMessage: "Assign Category",
|
||||
description: "dialog header"
|
||||
}
|
||||
});
|
|
@ -5,6 +5,7 @@ import { useIntl } from "react-intl";
|
|||
import AssignContainerDialog, {
|
||||
AssignContainerDialogProps
|
||||
} from "../AssignContainerDialog";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface AssignCollectionDialogProps
|
||||
extends Omit<AssignContainerDialogProps, "containers" | "title" | "search"> {
|
||||
|
@ -21,17 +22,12 @@ const AssignCollectionDialog: React.FC<AssignCollectionDialogProps> = ({
|
|||
<AssignContainerDialog
|
||||
containers={collections}
|
||||
search={{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Search Collection"
|
||||
}),
|
||||
placeholder: intl.formatMessage({
|
||||
defaultMessage: "Search by collection name, etc..."
|
||||
})
|
||||
label: intl.formatMessage(messages.assignCollectionDialogLabel),
|
||||
placeholder: intl.formatMessage(
|
||||
messages.assignCollectionDialogPlaceholder
|
||||
)
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Assign Collection",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(messages.assignCollectionDialogHeader)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
|
16
src/components/AssignCollectionDialog/messages.ts
Normal file
16
src/components/AssignCollectionDialog/messages.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
assignCollectionDialogLabel: {
|
||||
defaultMessage: "Search Collection",
|
||||
description: "dialog header"
|
||||
},
|
||||
assignCollectionDialogPlaceholder: {
|
||||
defaultMessage: "Search by collection name, etc...",
|
||||
description: "dialog search placeholder"
|
||||
},
|
||||
assignCollectionDialogHeader: {
|
||||
defaultMessage: "Assign Collection",
|
||||
description: "dialog header"
|
||||
}
|
||||
});
|
|
@ -13,49 +13,33 @@ import {
|
|||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import useScrollableDialogStyle from "@saleor/styles/useScrollableDialogStyle";
|
||||
import { FetchMoreProps, Node } from "@saleor/types";
|
||||
import { DialogProps, FetchMoreProps, Node } from "@saleor/types";
|
||||
import React from "react";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Checkbox from "../Checkbox";
|
||||
import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton";
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export interface FormData {
|
||||
export interface AssignContainerDialogFormData {
|
||||
containers: string[];
|
||||
query: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
avatar: {
|
||||
"&:first-child": {
|
||||
paddingLeft: 0
|
||||
}
|
||||
},
|
||||
checkboxCell: {
|
||||
paddingLeft: 0
|
||||
},
|
||||
wideCell: {
|
||||
width: "100%"
|
||||
}
|
||||
},
|
||||
{ name: "AssignContainerDialog" }
|
||||
);
|
||||
|
||||
interface Container extends Node {
|
||||
name: string;
|
||||
}
|
||||
export interface AssignContainerDialogProps extends FetchMoreProps {
|
||||
export interface AssignContainerDialogProps
|
||||
extends FetchMoreProps,
|
||||
DialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
containers: Container[];
|
||||
loading: boolean;
|
||||
open: boolean;
|
||||
search: Record<"label" | "placeholder", string>;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
onFetch: (value: string) => void;
|
||||
onSubmit: (data: string[]) => void;
|
||||
}
|
||||
|
@ -188,7 +172,7 @@ const AssignContainerDialog: React.FC<AssignContainerDialogProps> = props => {
|
|||
type="submit"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<FormattedMessage defaultMessage="Assign" description="button" />
|
||||
<FormattedMessage {...messages.assignContainerDialogButton} />
|
||||
</ConfirmButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
8
src/components/AssignContainerDialog/messages.ts
Normal file
8
src/components/AssignContainerDialog/messages.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
assignContainerDialogButton: {
|
||||
defaultMessage: "Assign",
|
||||
description: "button"
|
||||
}
|
||||
});
|
18
src/components/AssignContainerDialog/styles.ts
Normal file
18
src/components/AssignContainerDialog/styles.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
{
|
||||
avatar: {
|
||||
"&:first-child": {
|
||||
paddingLeft: 0
|
||||
}
|
||||
},
|
||||
checkboxCell: {
|
||||
paddingLeft: 0
|
||||
},
|
||||
wideCell: {
|
||||
width: "100%"
|
||||
}
|
||||
},
|
||||
{ name: "AssignContainerDialog" }
|
||||
);
|
|
@ -17,65 +17,43 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
|||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
|
||||
import useScrollableDialogStyle from "@saleor/styles/useScrollableDialogStyle";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import { DialogProps, FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Checkbox from "../Checkbox";
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export interface FormData {
|
||||
export interface AssignProductDialogFormData {
|
||||
products: SearchProducts_search_edges_node[];
|
||||
query: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
avatar: {
|
||||
"&&:first-child": {
|
||||
paddingLeft: 0
|
||||
},
|
||||
width: 72
|
||||
},
|
||||
checkboxCell: {
|
||||
paddingLeft: 0,
|
||||
width: 88
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0
|
||||
}
|
||||
},
|
||||
{ name: "AssignProductDialog" }
|
||||
);
|
||||
|
||||
export interface AssignProductDialogProps extends FetchMoreProps {
|
||||
export interface AssignProductDialogProps extends FetchMoreProps, DialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
products: SearchProducts_search_edges_node[];
|
||||
loading: boolean;
|
||||
onClose: () => void;
|
||||
onFetch: (value: string) => void;
|
||||
onSubmit: (data: SearchProducts_search_edges_node[]) => void;
|
||||
onSubmit: (data: string[]) => void;
|
||||
}
|
||||
|
||||
function handleProductAssign(
|
||||
product: SearchProducts_search_edges_node,
|
||||
productID: string,
|
||||
isSelected: boolean,
|
||||
selectedProducts: SearchProducts_search_edges_node[],
|
||||
setSelectedProducts: (data: SearchProducts_search_edges_node[]) => void
|
||||
selectedProducts: string[],
|
||||
setSelectedProducts: (data: string[]) => void
|
||||
) {
|
||||
if (isSelected) {
|
||||
setSelectedProducts(
|
||||
selectedProducts.filter(
|
||||
selectedProduct => selectedProduct.id !== product.id
|
||||
)
|
||||
selectedProducts.filter(selectedProduct => selectedProduct !== productID)
|
||||
);
|
||||
} else {
|
||||
setSelectedProducts([...selectedProducts, product]);
|
||||
setSelectedProducts([...selectedProducts, productID]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +76,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
|
||||
const intl = useIntl();
|
||||
const [query, onQueryChange] = useSearchQuery(onFetch);
|
||||
const [selectedProducts, setSelectedProducts] = React.useState<
|
||||
SearchProducts_search_edges_node[]
|
||||
>([]);
|
||||
const [selectedProducts, setSelectedProducts] = React.useState<string[]>([]);
|
||||
|
||||
const handleSubmit = () => onSubmit(selectedProducts);
|
||||
|
||||
|
@ -113,23 +89,15 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign Product"
|
||||
description="dialog header"
|
||||
/>
|
||||
<FormattedMessage {...messages.assignVariantDialogHeader} />
|
||||
</DialogTitle>
|
||||
<DialogContent className={scrollableDialogClasses.topArea}>
|
||||
<TextField
|
||||
name="query"
|
||||
value={query}
|
||||
onChange={onQueryChange}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Search Products"
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Search by product name, attribute, product type etc..."
|
||||
})}
|
||||
label={intl.formatMessage(messages.assignProductDialogSearch)}
|
||||
placeholder={intl.formatMessage(messages.assignProductDialogContent)}
|
||||
fullWidth
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
|
@ -158,7 +126,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
{products &&
|
||||
products.map(product => {
|
||||
const isSelected = selectedProducts.some(
|
||||
selectedProduct => selectedProduct.id === product.id
|
||||
selectedProduct => selectedProduct === product.id
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -181,7 +149,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
checked={isSelected}
|
||||
onChange={() =>
|
||||
handleProductAssign(
|
||||
product,
|
||||
product.id,
|
||||
isSelected,
|
||||
selectedProducts,
|
||||
setSelectedProducts
|
||||
|
@ -208,10 +176,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
type="submit"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign products"
|
||||
description="button"
|
||||
/>
|
||||
<FormattedMessage {...messages.assignProductDialogButton} />
|
||||
</ConfirmButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
18
src/components/AssignProductDialog/messages.ts
Normal file
18
src/components/AssignProductDialog/messages.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
assignVariantDialogHeader: {
|
||||
defaultMessage: "Assign Product",
|
||||
description: "dialog header"
|
||||
},
|
||||
assignProductDialogButton: {
|
||||
defaultMessage: "Assign",
|
||||
description: "button"
|
||||
},
|
||||
assignProductDialogContent: {
|
||||
defaultMessage: "Search Products"
|
||||
},
|
||||
assignProductDialogSearch: {
|
||||
defaultMessage: "Search by product name, attribute, product type etc..."
|
||||
}
|
||||
});
|
20
src/components/AssignProductDialog/styles.ts
Normal file
20
src/components/AssignProductDialog/styles.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
{
|
||||
avatar: {
|
||||
"&&:first-child": {
|
||||
paddingLeft: 0
|
||||
},
|
||||
width: 72
|
||||
},
|
||||
checkboxCell: {
|
||||
paddingLeft: 0,
|
||||
width: 88
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0
|
||||
}
|
||||
},
|
||||
{ name: "AssignProductDialog" }
|
||||
);
|
298
src/components/AssignVariantDialog/AssignVariantDialog.tsx
Normal file
298
src/components/AssignVariantDialog/AssignVariantDialog.tsx
Normal file
|
@ -0,0 +1,298 @@
|
|||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import ConfirmButton, {
|
||||
ConfirmButtonTransitionState
|
||||
} from "@saleor/components/ConfirmButton";
|
||||
import Money from "@saleor/components/Money";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { maybe, renderCollection } from "@saleor/misc";
|
||||
import {
|
||||
getById,
|
||||
getByUnmatchingId
|
||||
} from "@saleor/orders/components/OrderReturnPage/utils";
|
||||
import {
|
||||
SearchProducts_search_edges_node,
|
||||
SearchProducts_search_edges_node_variants
|
||||
} from "@saleor/searches/types/SearchProducts";
|
||||
import useScrollableDialogStyle from "@saleor/styles/useScrollableDialogStyle";
|
||||
import { DialogProps, FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Checkbox from "../Checkbox";
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
type SetVariantsAction = (
|
||||
data: SearchProducts_search_edges_node_variants[]
|
||||
) => void;
|
||||
export interface AssignVariantDialogFormData {
|
||||
products: SearchProducts_search_edges_node[];
|
||||
query: string;
|
||||
}
|
||||
export interface AssignVariantDialogProps extends FetchMoreProps, DialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
products: SearchProducts_search_edges_node[];
|
||||
loading: boolean;
|
||||
onFetch: (value: string) => void;
|
||||
onSubmit: (data: SearchProducts_search_edges_node_variants[]) => void;
|
||||
}
|
||||
|
||||
function isVariantSelected(
|
||||
variant: SearchProducts_search_edges_node_variants,
|
||||
selectedVariantsToProductsMap: SearchProducts_search_edges_node_variants[]
|
||||
): boolean {
|
||||
return !!selectedVariantsToProductsMap.find(getById(variant.id));
|
||||
}
|
||||
|
||||
const handleProductAssign = (
|
||||
product: SearchProducts_search_edges_node,
|
||||
productIndex: number,
|
||||
productsWithAllVariantsSelected: boolean[],
|
||||
variants: SearchProducts_search_edges_node_variants[],
|
||||
setVariants: SetVariantsAction
|
||||
) =>
|
||||
productsWithAllVariantsSelected[productIndex]
|
||||
? setVariants(
|
||||
variants.filter(
|
||||
selectedVariant => !product.variants.find(getById(selectedVariant.id))
|
||||
)
|
||||
)
|
||||
: setVariants([
|
||||
...variants,
|
||||
...product.variants.filter(
|
||||
productVariant => !variants.find(getById(productVariant.id))
|
||||
)
|
||||
]);
|
||||
|
||||
const handleVariantAssign = (
|
||||
variant: SearchProducts_search_edges_node_variants,
|
||||
variantIndex: number,
|
||||
productIndex: number,
|
||||
variants: SearchProducts_search_edges_node_variants[],
|
||||
selectedVariantsToProductsMap: boolean[][],
|
||||
setVariants: SetVariantsAction
|
||||
) =>
|
||||
selectedVariantsToProductsMap[productIndex][variantIndex]
|
||||
? setVariants(variants.filter(getByUnmatchingId(variant.id)))
|
||||
: setVariants([...variants, variant]);
|
||||
|
||||
function hasAllVariantsSelected(
|
||||
productVariants: SearchProducts_search_edges_node_variants[],
|
||||
selectedVariantsToProductsMap: SearchProducts_search_edges_node_variants[]
|
||||
): boolean {
|
||||
return productVariants.reduce(
|
||||
(acc, productVariant) =>
|
||||
acc && !!selectedVariantsToProductsMap.find(getById(productVariant.id)),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
const scrollableTargetId = "assignVariantScrollableDialog";
|
||||
|
||||
const AssignVariantDialog: React.FC<AssignVariantDialogProps> = props => {
|
||||
const {
|
||||
confirmButtonState,
|
||||
hasMore,
|
||||
open,
|
||||
loading,
|
||||
products,
|
||||
onClose,
|
||||
onFetch,
|
||||
onFetchMore,
|
||||
onSubmit
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
const scrollableDialogClasses = useScrollableDialogStyle({});
|
||||
|
||||
const intl = useIntl();
|
||||
const [query, onQueryChange] = useSearchQuery(onFetch);
|
||||
const [variants, setVariants] = React.useState<
|
||||
SearchProducts_search_edges_node_variants[]
|
||||
>([]);
|
||||
|
||||
const productChoices =
|
||||
products?.filter(product => product?.variants?.length > 0) || [];
|
||||
|
||||
const selectedVariantsToProductsMap = productChoices
|
||||
? productChoices.map(product =>
|
||||
product.variants.map(variant => isVariantSelected(variant, variants))
|
||||
)
|
||||
: [];
|
||||
|
||||
const productsWithAllVariantsSelected = productChoices
|
||||
? productChoices.map(product =>
|
||||
hasAllVariantsSelected(product.variants, variants)
|
||||
)
|
||||
: [];
|
||||
|
||||
const handleSubmit = () => onSubmit(variants);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
classes={{ paper: scrollableDialogClasses.dialog }}
|
||||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>
|
||||
<FormattedMessage {...messages.assignVariantDialogHeader} />
|
||||
</DialogTitle>
|
||||
<DialogContent className={scrollableDialogClasses.topArea}>
|
||||
<TextField
|
||||
name="query"
|
||||
value={query}
|
||||
onChange={onQueryChange}
|
||||
label={intl.formatMessage(messages.assignVariantDialogSearch)}
|
||||
placeholder={intl.formatMessage(messages.assignVariantDialogContent)}
|
||||
fullWidth
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
endAdornment: loading && <CircularProgress size={16} />
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogContent
|
||||
className={scrollableDialogClasses.scrollArea}
|
||||
id={scrollableTargetId}
|
||||
>
|
||||
<InfiniteScroll
|
||||
dataLength={variants?.length}
|
||||
next={onFetchMore}
|
||||
hasMore={hasMore}
|
||||
scrollThreshold="100px"
|
||||
loader={
|
||||
<div className={scrollableDialogClasses.loadMoreLoaderContainer}>
|
||||
<CircularProgress size={16} />
|
||||
</div>
|
||||
}
|
||||
scrollableTarget={scrollableTargetId}
|
||||
>
|
||||
<ResponsiveTable key="table">
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
products,
|
||||
(product, productIndex) => (
|
||||
<React.Fragment key={product ? product.id : "skeleton"}>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
padding="checkbox"
|
||||
className={classes.productCheckboxCell}
|
||||
>
|
||||
<Checkbox
|
||||
checked={
|
||||
productsWithAllVariantsSelected[productIndex]
|
||||
}
|
||||
disabled={loading}
|
||||
onChange={() =>
|
||||
handleProductAssign(
|
||||
product,
|
||||
productIndex,
|
||||
productsWithAllVariantsSelected,
|
||||
variants,
|
||||
setVariants
|
||||
)
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCellAvatar
|
||||
className={classes.avatar}
|
||||
thumbnail={maybe(() => product.thumbnail.url)}
|
||||
/>
|
||||
<TableCell className={classes.colName} colSpan={2}>
|
||||
{maybe(() => product.name)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{maybe(() => product.variants, []).map(
|
||||
(variant, variantIndex) => (
|
||||
<TableRow key={variant.id}>
|
||||
<TableCell />
|
||||
<TableCell className={classes.colVariantCheckbox}>
|
||||
<Checkbox
|
||||
className={classes.variantCheckbox}
|
||||
checked={
|
||||
selectedVariantsToProductsMap[productIndex][
|
||||
variantIndex
|
||||
]
|
||||
}
|
||||
disabled={loading}
|
||||
onChange={() =>
|
||||
handleVariantAssign(
|
||||
variant,
|
||||
variantIndex,
|
||||
productIndex,
|
||||
variants,
|
||||
selectedVariantsToProductsMap,
|
||||
setVariants
|
||||
)
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
<div>{variant.name}</div>
|
||||
<div className={classes.grayText}>
|
||||
<FormattedMessage
|
||||
{...messages.assignVariantDialogSKU}
|
||||
values={{
|
||||
sku: variant.sku
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className={classes.textRight}>
|
||||
{variant?.channelListings[0]?.price && (
|
||||
<Money money={variant.channelListings[0].price} />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</React.Fragment>
|
||||
),
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<FormattedMessage defaultMessage="No products available in order channel matching given query" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</InfiniteScroll>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose}>
|
||||
<FormattedMessage {...buttonMessages.back} />
|
||||
</Button>
|
||||
<ConfirmButton
|
||||
data-test="submit"
|
||||
transitionState={confirmButtonState}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<FormattedMessage {...messages.assignVariantDialogButton} />
|
||||
</ConfirmButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
AssignVariantDialog.displayName = "AssignVariantDialog";
|
||||
export default AssignVariantDialog;
|
2
src/components/AssignVariantDialog/index.ts
Normal file
2
src/components/AssignVariantDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./AssignVariantDialog";
|
||||
export * from "./AssignVariantDialog";
|
22
src/components/AssignVariantDialog/messages.ts
Normal file
22
src/components/AssignVariantDialog/messages.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
assignVariantDialogHeader: {
|
||||
defaultMessage: "Assign Variant",
|
||||
description: "dialog header"
|
||||
},
|
||||
assignVariantDialogButton: {
|
||||
defaultMessage: "Assign",
|
||||
description: "button"
|
||||
},
|
||||
assignVariantDialogContent: {
|
||||
defaultMessage: "Search Variants"
|
||||
},
|
||||
assignVariantDialogSearch: {
|
||||
defaultMessage: "Search by product name, attribute, product type etc..."
|
||||
},
|
||||
assignVariantDialogSKU: {
|
||||
defaultMessage: "SKU {sku}",
|
||||
description: "variant sku"
|
||||
}
|
||||
});
|
56
src/components/AssignVariantDialog/styles.ts
Normal file
56
src/components/AssignVariantDialog/styles.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
avatar: {
|
||||
paddingLeft: 0,
|
||||
width: 64
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0
|
||||
},
|
||||
colVariantCheckbox: {
|
||||
padding: 0
|
||||
},
|
||||
content: {
|
||||
overflowY: "scroll",
|
||||
paddingTop: 0,
|
||||
marginBottom: theme.spacing(3)
|
||||
},
|
||||
grayText: {
|
||||
color: theme.palette.text.disabled
|
||||
},
|
||||
loadMoreLoaderContainer: {
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
height: theme.spacing(3),
|
||||
justifyContent: "center",
|
||||
marginTop: theme.spacing(3)
|
||||
},
|
||||
overflow: {
|
||||
overflowY: "hidden"
|
||||
},
|
||||
topArea: {
|
||||
overflowY: "hidden",
|
||||
paddingBottom: theme.spacing(6),
|
||||
margin: theme.spacing(0, 3, 3, 3)
|
||||
},
|
||||
productCheckboxCell: {
|
||||
"&:first-child": {
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0
|
||||
}
|
||||
},
|
||||
textRight: {
|
||||
textAlign: "right"
|
||||
},
|
||||
variantCheckbox: {
|
||||
left: theme.spacing(),
|
||||
position: "relative"
|
||||
},
|
||||
wideCell: {
|
||||
width: "100%"
|
||||
}
|
||||
}),
|
||||
{ name: "AssignVariantDialog" }
|
||||
);
|
|
@ -14,7 +14,6 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
|||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
@ -23,35 +22,14 @@ import { maybe, renderCollection } from "../../../misc";
|
|||
import { ListActions, ListProps } from "../../../types";
|
||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
export interface DiscountCategoriesProps extends ListProps, ListActions {
|
||||
discount: SaleDetails_sale | VoucherDetails_voucher;
|
||||
onCategoryAssign: () => void;
|
||||
onCategoryUnassign: (id: string) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: 80
|
||||
},
|
||||
colName: {
|
||||
width: "auto"
|
||||
},
|
||||
colProducts: {
|
||||
textAlign: "right",
|
||||
width: 140
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
{ name: "DiscountCategories" }
|
||||
);
|
||||
|
||||
const numberOfColumns = 4;
|
||||
|
||||
const DiscountCategories: React.FC<DiscountCategoriesProps> = props => {
|
||||
|
@ -77,16 +55,10 @@ const DiscountCategories: React.FC<DiscountCategoriesProps> = props => {
|
|||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Eligible Categories",
|
||||
description: "section header"
|
||||
})}
|
||||
title={intl.formatMessage(messages.discountCategoriesHeader)}
|
||||
toolbar={
|
||||
<Button color="primary" onClick={onCategoryAssign}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign categories"
|
||||
description="button"
|
||||
/>
|
||||
<FormattedMessage {...messages.discountCategoriesButton} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
@ -107,12 +79,13 @@ const DiscountCategories: React.FC<DiscountCategoriesProps> = props => {
|
|||
>
|
||||
<>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Category name" />
|
||||
<FormattedMessage
|
||||
{...messages.discountCategoriesTableProductHeader}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Products"
|
||||
description="number of products"
|
||||
{...messages.discountCategoriesTableProductNumber}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
|
@ -179,7 +152,7 @@ const DiscountCategories: React.FC<DiscountCategoriesProps> = props => {
|
|||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No categories found" />
|
||||
<FormattedMessage {...messages.discountCategoriesNotFound} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
|
24
src/discounts/components/DiscountCategories/messages.ts
Normal file
24
src/discounts/components/DiscountCategories/messages.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
discountCategoriesHeader: {
|
||||
defaultMessage: "Eligible Categories",
|
||||
description: "section header"
|
||||
},
|
||||
discountCategoriesButton: {
|
||||
defaultMessage: "Assign categories",
|
||||
description: "button"
|
||||
},
|
||||
discountCategoriesTableProductHeader: {
|
||||
defaultMessage: "Category Name",
|
||||
description: "table head"
|
||||
},
|
||||
discountCategoriesTableProductNumber: {
|
||||
defaultMessage: "Products",
|
||||
description: "number of products"
|
||||
},
|
||||
discountCategoriesNotFound: {
|
||||
defaultMessage: "No categories found",
|
||||
description: "no categories"
|
||||
}
|
||||
});
|
23
src/discounts/components/DiscountCategories/styles.ts
Normal file
23
src/discounts/components/DiscountCategories/styles.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
{
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: 80
|
||||
},
|
||||
colName: {
|
||||
width: "auto"
|
||||
},
|
||||
colProducts: {
|
||||
textAlign: "right",
|
||||
width: 140
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
{ name: "DiscountCategories" }
|
||||
);
|
|
@ -14,7 +14,6 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
|||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
@ -23,36 +22,14 @@ import { maybe, renderCollection } from "../../../misc";
|
|||
import { ListActions, ListProps } from "../../../types";
|
||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
export interface DiscountCollectionsProps extends ListProps, ListActions {
|
||||
discount: SaleDetails_sale | VoucherDetails_voucher;
|
||||
onCollectionAssign: () => void;
|
||||
onCollectionUnassign: (id: string) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: 80
|
||||
},
|
||||
colName: {
|
||||
width: "auto"
|
||||
},
|
||||
colProducts: {
|
||||
textAlign: "right",
|
||||
width: 140
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
},
|
||||
textRight: {}
|
||||
},
|
||||
{ name: "DiscountCollections" }
|
||||
);
|
||||
|
||||
const numberOfColumns = 4;
|
||||
|
||||
const DiscountCollections: React.FC<DiscountCollectionsProps> = props => {
|
||||
|
@ -79,16 +56,10 @@ const DiscountCollections: React.FC<DiscountCollectionsProps> = props => {
|
|||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Eligible Collections",
|
||||
description: "section header"
|
||||
})}
|
||||
title={intl.formatMessage(messages.discountCollectionsHeader)}
|
||||
toolbar={
|
||||
<Button color="primary" onClick={onCollectionAssign}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign collections"
|
||||
description="button"
|
||||
/>
|
||||
<FormattedMessage {...messages.discountCollectionsButton} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
@ -108,12 +79,13 @@ const DiscountCollections: React.FC<DiscountCollectionsProps> = props => {
|
|||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Collection name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.textRight}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Products"
|
||||
description="number of products"
|
||||
{...messages.discountCollectionsTableProductHeader}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage
|
||||
{...messages.discountCollectionsTableProductNumber}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
|
@ -181,7 +153,7 @@ const DiscountCollections: React.FC<DiscountCollectionsProps> = props => {
|
|||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No collections found" />
|
||||
<FormattedMessage {...messages.discountCollectionsNotFound} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
|
24
src/discounts/components/DiscountCollections/messages.ts
Normal file
24
src/discounts/components/DiscountCollections/messages.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
discountCollectionsHeader: {
|
||||
defaultMessage: "Eligible Collections",
|
||||
description: "section header"
|
||||
},
|
||||
discountCollectionsButton: {
|
||||
defaultMessage: "Assign collections",
|
||||
description: "button"
|
||||
},
|
||||
discountCollectionsTableProductHeader: {
|
||||
defaultMessage: "Collection Name",
|
||||
description: "table head"
|
||||
},
|
||||
discountCollectionsTableProductNumber: {
|
||||
defaultMessage: "Products",
|
||||
description: "number of products"
|
||||
},
|
||||
discountCollectionsNotFound: {
|
||||
defaultMessage: "No collections found",
|
||||
description: "no collections"
|
||||
}
|
||||
});
|
23
src/discounts/components/DiscountCollections/styles.ts
Normal file
23
src/discounts/components/DiscountCollections/styles.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
{
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: 80
|
||||
},
|
||||
colName: {
|
||||
width: "auto"
|
||||
},
|
||||
colProducts: {
|
||||
textAlign: "right",
|
||||
width: 140
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
{ name: "DiscountCollections" }
|
||||
);
|
|
@ -14,63 +14,32 @@ import Checkbox from "@saleor/components/Checkbox";
|
|||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||
import { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar/Avatar";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { maybe, renderCollection } from "../../../misc";
|
||||
import { ListActions, ListProps } from "../../../types";
|
||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||
|
||||
import { SaleDetails_sale_products_edges_node } from "../../types/SaleDetails";
|
||||
import { VoucherDetails_voucher_products_edges_node } from "../../types/VoucherDetails";
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
export interface SaleProductsProps extends ListProps, ListActions {
|
||||
discount: SaleDetails_sale | VoucherDetails_voucher;
|
||||
products:
|
||||
| SaleDetails_sale_products_edges_node[]
|
||||
| VoucherDetails_voucher_products_edges_node[];
|
||||
channelsCount: number;
|
||||
onProductAssign: () => void;
|
||||
onProductUnassign: (id: string) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: `calc(76px + ${theme.spacing(0.5)})`
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0,
|
||||
width: "auto"
|
||||
},
|
||||
colNameLabel: {
|
||||
marginLeft: `calc(${AVATAR_MARGIN}px + ${theme.spacing(3)})`
|
||||
},
|
||||
colPublished: {
|
||||
width: 150
|
||||
},
|
||||
colType: {
|
||||
width: 200
|
||||
},
|
||||
table: {
|
||||
tableLayout: "fixed"
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
}),
|
||||
{ name: "DiscountProducts" }
|
||||
);
|
||||
|
||||
const numberOfColumns = 5;
|
||||
|
||||
const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
||||
const {
|
||||
channelsCount,
|
||||
discount: sale,
|
||||
products,
|
||||
disabled,
|
||||
pageInfo,
|
||||
onRowClick,
|
||||
|
@ -91,20 +60,14 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
|||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Eligible Products",
|
||||
description: "section header"
|
||||
})}
|
||||
title={intl.formatMessage(messages.discountProductsHeader)}
|
||||
toolbar={
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={onProductAssign}
|
||||
data-test-id="assign-products"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Assign products"
|
||||
description="button"
|
||||
/>
|
||||
<FormattedMessage {...messages.discountProductsButton} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
@ -120,22 +83,23 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
|||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={mapEdgesToItems(sale?.products)}
|
||||
items={products}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<span className={classes.colNameLabel}>
|
||||
<FormattedMessage defaultMessage="Product Name" />
|
||||
<span className={products?.length > 0 && classes.colNameLabel}>
|
||||
<FormattedMessage
|
||||
{...messages.discountProductsTableProductHeader}
|
||||
/>
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
<FormattedMessage defaultMessage="Product Type" />
|
||||
<FormattedMessage {...messages.discountProductsTableTypeHeader} />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colPublished}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Availability"
|
||||
description="product availability"
|
||||
{...messages.discountProductsTableAvailabilityHeader}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions} />
|
||||
|
@ -155,7 +119,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
|||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
mapEdgesToItems(sale?.products),
|
||||
products,
|
||||
product => {
|
||||
const isSelected = product ? isChecked(product.id) : false;
|
||||
|
||||
|
@ -216,7 +180,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
|||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No products found" />
|
||||
<FormattedMessage {...messages.discountProductsNotFound} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
|
28
src/discounts/components/DiscountProducts/messages.ts
Normal file
28
src/discounts/components/DiscountProducts/messages.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
discountProductsHeader: {
|
||||
defaultMessage: "Eligible Products",
|
||||
description: "section header"
|
||||
},
|
||||
discountProductsButton: {
|
||||
defaultMessage: "Assign products",
|
||||
description: "button"
|
||||
},
|
||||
discountProductsTableProductHeader: {
|
||||
defaultMessage: "Product Name",
|
||||
description: "table head"
|
||||
},
|
||||
discountProductsTableTypeHeader: {
|
||||
defaultMessage: "Product Type",
|
||||
description: "product type"
|
||||
},
|
||||
discountProductsTableAvailabilityHeader: {
|
||||
defaultMessage: "Availability",
|
||||
description: "product availability"
|
||||
},
|
||||
discountProductsNotFound: {
|
||||
defaultMessage: "No products found",
|
||||
description: "no products"
|
||||
}
|
||||
});
|
36
src/discounts/components/DiscountProducts/styles.ts
Normal file
36
src/discounts/components/DiscountProducts/styles.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar/Avatar";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: `calc(76px + ${theme.spacing(0.5)})`
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0,
|
||||
width: "auto",
|
||||
minWidth: 200
|
||||
},
|
||||
colNameLabel: {
|
||||
marginLeft: `calc(${AVATAR_MARGIN}px + ${theme.spacing(3)})`
|
||||
},
|
||||
colPublished: {
|
||||
width: "auto",
|
||||
minWidth: 150
|
||||
},
|
||||
colType: {
|
||||
width: "auto",
|
||||
minWidth: 150
|
||||
},
|
||||
table: {
|
||||
tableLayout: "fixed"
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
}),
|
||||
{ name: "DiscountProducts" }
|
||||
);
|
189
src/discounts/components/DiscountVariants/DiscountVariants.tsx
Normal file
189
src/discounts/components/DiscountVariants/DiscountVariants.tsx
Normal file
|
@ -0,0 +1,189 @@
|
|||
import {
|
||||
Button,
|
||||
Card,
|
||||
IconButton,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableRow
|
||||
} from "@material-ui/core";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import Checkbox from "@saleor/components/Checkbox";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { maybe, renderCollection } from "../../../misc";
|
||||
import { ListActions, ListProps } from "../../../types";
|
||||
import { SaleDetails_sale_variants_edges_node } from "../../types/SaleDetails";
|
||||
import { messages } from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
export interface SaleVariantsProps
|
||||
extends Omit<ListProps, "onRowClick">,
|
||||
ListActions {
|
||||
variants: SaleDetails_sale_variants_edges_node[] | null;
|
||||
onVariantAssign: () => void;
|
||||
onRowClick: (productId: string, variantId: string) => () => void;
|
||||
onVariantUnassign: (id: string) => void;
|
||||
}
|
||||
|
||||
const numberOfColumns = 5;
|
||||
|
||||
const DiscountVariants: React.FC<SaleVariantsProps> = props => {
|
||||
const {
|
||||
variants,
|
||||
disabled,
|
||||
pageInfo,
|
||||
onRowClick,
|
||||
onPreviousPage,
|
||||
onVariantAssign,
|
||||
onVariantUnassign,
|
||||
onNextPage,
|
||||
isChecked,
|
||||
selected,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage(messages.discountVariantsHeader)}
|
||||
toolbar={
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={onVariantAssign}
|
||||
data-test-id="assign-variant"
|
||||
>
|
||||
<FormattedMessage {...messages.discountVariantsButton} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<colgroup>
|
||||
<col />
|
||||
<col className={classes.colProductName} />
|
||||
<col className={classes.colVariantName} />
|
||||
<col className={classes.colType} />
|
||||
<col className={classes.colActions} />
|
||||
</colgroup>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={variants}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colProductName}>
|
||||
<span className={variants?.length > 0 && classes.colNameLabel}>
|
||||
<FormattedMessage
|
||||
{...messages.discountVariantsTableProductHeader}
|
||||
/>
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colVariantName}>
|
||||
<FormattedMessage
|
||||
{...messages.discountVariantsTableVariantHeader}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
<FormattedMessage
|
||||
{...messages.discountVariantsTableProductHeader}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions} />
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
variants,
|
||||
variant => {
|
||||
const isSelected = variant ? isChecked(variant.id) : false;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover={!!variant}
|
||||
key={variant ? variant.id : "skeleton"}
|
||||
onClick={
|
||||
variant && onRowClick(variant.product.id, variant.id)
|
||||
}
|
||||
className={classes.tableRow}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(variant.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCellAvatar
|
||||
className={classes.colProductName}
|
||||
thumbnail={maybe(() => variant.product.thumbnail.url)}
|
||||
>
|
||||
{maybe<React.ReactNode>(
|
||||
() => variant.product.name,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCellAvatar>
|
||||
<TableCell className={classes.colType}>
|
||||
{maybe<React.ReactNode>(() => variant.name, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => variant.product.productType.name,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions}>
|
||||
<IconButton
|
||||
disabled={!variant || disabled}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
onVariantUnassign(variant.id);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon color="primary" />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage {...messages.discountVariantsNotFound} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
DiscountVariants.displayName = "DiscountVariants";
|
||||
export default DiscountVariants;
|
2
src/discounts/components/DiscountVariants/index.ts
Normal file
2
src/discounts/components/DiscountVariants/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./DiscountVariants";
|
||||
export * from "./DiscountVariants";
|
28
src/discounts/components/DiscountVariants/messages.ts
Normal file
28
src/discounts/components/DiscountVariants/messages.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
discountVariantsHeader: {
|
||||
defaultMessage: "Eligible Variants",
|
||||
description: "section header"
|
||||
},
|
||||
discountVariantsButton: {
|
||||
defaultMessage: "Assign variants",
|
||||
description: "button"
|
||||
},
|
||||
discountVariantsTableProductHeader: {
|
||||
defaultMessage: "Product Name",
|
||||
description: "table head"
|
||||
},
|
||||
discountVariantsTableVariantHeader: {
|
||||
defaultMessage: "Variant Name",
|
||||
description: "table head"
|
||||
},
|
||||
discountVariantsTableTypeHeader: {
|
||||
defaultMessage: "Product Type",
|
||||
description: "table head"
|
||||
},
|
||||
discountVariantsNotFound: {
|
||||
defaultMessage: "No variants found",
|
||||
description: "no variants"
|
||||
}
|
||||
});
|
36
src/discounts/components/DiscountVariants/styles.ts
Normal file
36
src/discounts/components/DiscountVariants/styles.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar/Avatar";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
colActions: {
|
||||
"&:last-child": {
|
||||
paddingRight: 0
|
||||
},
|
||||
width: `calc(76px + ${theme.spacing(0.5)})`
|
||||
},
|
||||
colProductName: {
|
||||
paddingLeft: 0,
|
||||
width: "auto",
|
||||
minWidth: 200
|
||||
},
|
||||
colNameLabel: {
|
||||
marginLeft: `calc(${AVATAR_MARGIN}px + ${theme.spacing(3)})`
|
||||
},
|
||||
colVariantName: {
|
||||
width: "auto",
|
||||
minWidth: 150
|
||||
},
|
||||
colType: {
|
||||
width: "auto",
|
||||
minWidth: 150
|
||||
},
|
||||
table: {
|
||||
tableLayout: "fixed"
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
}),
|
||||
{ name: "DiscountVariants" }
|
||||
);
|
|
@ -14,6 +14,7 @@ import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFrag
|
|||
import { sectionNames } from "@saleor/intl";
|
||||
import { Backlink } from "@saleor/macaw-ui";
|
||||
import { validatePrice } from "@saleor/products/utils/validation";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
|
@ -30,6 +31,7 @@ import DiscountCategories from "../DiscountCategories";
|
|||
import DiscountCollections from "../DiscountCollections";
|
||||
import DiscountDates from "../DiscountDates";
|
||||
import DiscountProducts from "../DiscountProducts";
|
||||
import DiscountVariants from "../DiscountVariants";
|
||||
import SaleInfo from "../SaleInfo";
|
||||
import SaleSummary from "../SaleSummary";
|
||||
import SaleType from "../SaleType";
|
||||
|
@ -49,20 +51,27 @@ export interface SaleDetailsPageFormData extends MetadataFormData {
|
|||
export enum SaleDetailsPageTab {
|
||||
categories = "categories",
|
||||
collections = "collections",
|
||||
products = "products"
|
||||
products = "products",
|
||||
variants = "variants"
|
||||
}
|
||||
|
||||
export function saleDetailsPageTab(tab: string): SaleDetailsPageTab {
|
||||
return tab === SaleDetailsPageTab.products
|
||||
? SaleDetailsPageTab.products
|
||||
: tab === SaleDetailsPageTab.collections
|
||||
? SaleDetailsPageTab.collections
|
||||
: SaleDetailsPageTab.categories;
|
||||
: tab === SaleDetailsPageTab.categories
|
||||
? SaleDetailsPageTab.categories
|
||||
: SaleDetailsPageTab.variants;
|
||||
}
|
||||
|
||||
export interface SaleDetailsPageProps
|
||||
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
|
||||
TabListActions<
|
||||
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
|
||||
| "categoryListToolbar"
|
||||
| "collectionListToolbar"
|
||||
| "productListToolbar"
|
||||
| "variantListToolbar"
|
||||
>,
|
||||
ChannelProps {
|
||||
activeTab: SaleDetailsPageTab;
|
||||
|
@ -82,6 +91,9 @@ export interface SaleDetailsPageProps
|
|||
onProductAssign: () => void;
|
||||
onProductUnassign: (id: string) => void;
|
||||
onProductClick: (id: string) => () => void;
|
||||
onVariantAssign: () => void;
|
||||
onVariantUnassign: (id: string) => void;
|
||||
onVariantClick: (productId: string, variantId: string) => () => void;
|
||||
onRemove: () => void;
|
||||
onSubmit: (data: SaleDetailsPageFormData) => void;
|
||||
onTabClick: (index: SaleDetailsPageTab) => void;
|
||||
|
@ -92,6 +104,7 @@ export interface SaleDetailsPageProps
|
|||
const CategoriesTab = Tab(SaleDetailsPageTab.categories);
|
||||
const CollectionsTab = Tab(SaleDetailsPageTab.collections);
|
||||
const ProductsTab = Tab(SaleDetailsPageTab.products);
|
||||
const VariantsTab = Tab(SaleDetailsPageTab.variants);
|
||||
|
||||
const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
||||
activeTab,
|
||||
|
@ -120,9 +133,13 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
onProductAssign,
|
||||
onProductUnassign,
|
||||
onProductClick,
|
||||
onVariantAssign,
|
||||
onVariantUnassign,
|
||||
onVariantClick,
|
||||
categoryListToolbar,
|
||||
collectionListToolbar,
|
||||
productListToolbar,
|
||||
variantListToolbar,
|
||||
isChecked,
|
||||
selected,
|
||||
selectedChannelId,
|
||||
|
@ -239,6 +256,25 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
}
|
||||
)}
|
||||
</ProductsTab>
|
||||
<VariantsTab
|
||||
testId="variants-tab"
|
||||
isActive={activeTab === SaleDetailsPageTab.variants}
|
||||
changeTab={onTabClick}
|
||||
>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Variants ({quantity})",
|
||||
description: "number of variants",
|
||||
id: "saleDetailsPageVariantsQuantity"
|
||||
},
|
||||
{
|
||||
quantity: maybe(
|
||||
() => sale.variants.totalCount.toString(),
|
||||
"…"
|
||||
)
|
||||
}
|
||||
)}
|
||||
</VariantsTab>
|
||||
</TabContainer>
|
||||
<CardSpacer />
|
||||
{activeTab === SaleDetailsPageTab.categories ? (
|
||||
|
@ -273,7 +309,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
toggleAll={toggleAll}
|
||||
toolbar={collectionListToolbar}
|
||||
/>
|
||||
) : (
|
||||
) : activeTab === SaleDetailsPageTab.products ? (
|
||||
<DiscountProducts
|
||||
disabled={disabled}
|
||||
onNextPage={onNextPage}
|
||||
|
@ -282,7 +318,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
onProductUnassign={onProductUnassign}
|
||||
onRowClick={onProductClick}
|
||||
pageInfo={pageInfo}
|
||||
discount={sale}
|
||||
products={mapEdgesToItems(sale?.products)}
|
||||
channelsCount={allChannelsCount}
|
||||
isChecked={isChecked}
|
||||
selected={selected}
|
||||
|
@ -290,6 +326,22 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
toggleAll={toggleAll}
|
||||
toolbar={productListToolbar}
|
||||
/>
|
||||
) : (
|
||||
<DiscountVariants
|
||||
disabled={disabled}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onVariantAssign={onVariantAssign}
|
||||
onVariantUnassign={onVariantUnassign}
|
||||
onRowClick={onVariantClick}
|
||||
pageInfo={pageInfo}
|
||||
variants={mapEdgesToItems(sale?.variants)}
|
||||
isChecked={isChecked}
|
||||
selected={selected}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={variantListToolbar}
|
||||
/>
|
||||
)}
|
||||
<CardSpacer />
|
||||
<DiscountDates
|
||||
|
|
|
@ -20,6 +20,7 @@ import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFrag
|
|||
import { sectionNames } from "@saleor/intl";
|
||||
import { Backlink } from "@saleor/macaw-ui";
|
||||
import { validatePrice } from "@saleor/products/utils/validation";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
|
@ -353,7 +354,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
|||
onProductUnassign={onProductUnassign}
|
||||
onRowClick={onProductClick}
|
||||
pageInfo={pageInfo}
|
||||
discount={voucher}
|
||||
products={mapEdgesToItems(voucher.products)}
|
||||
channelsCount={allChannelsCount}
|
||||
isChecked={isChecked}
|
||||
selected={selected}
|
||||
|
|
|
@ -424,6 +424,147 @@ export const sale: SaleDetails_sale = {
|
|||
},
|
||||
totalCount: 4
|
||||
},
|
||||
variants: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE0",
|
||||
name: "XL",
|
||||
product: {
|
||||
id: "UHJvZHVjdDoxMTg=",
|
||||
name: "White Hoodie",
|
||||
thumbnail: {
|
||||
url: placeholderImage,
|
||||
__typename: "Image"
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6MTQ=",
|
||||
name: "Top (clothing)",
|
||||
__typename: "ProductType"
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Channel-USD",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel"
|
||||
},
|
||||
__typename: "ProductChannelListing"
|
||||
}
|
||||
],
|
||||
__typename: "Product"
|
||||
},
|
||||
__typename: "ProductVariant"
|
||||
},
|
||||
__typename: "ProductVariantCountableEdge"
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjc4",
|
||||
name: "L",
|
||||
product: {
|
||||
id: "UHJvZHVjdDoxMTE=",
|
||||
name: "T-shirt",
|
||||
thumbnail: {
|
||||
url: placeholderImage,
|
||||
__typename: "Image"
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6MTQ=",
|
||||
name: "Top (clothing)",
|
||||
__typename: "ProductType"
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Channel-USD",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel"
|
||||
},
|
||||
__typename: "ProductChannelListing"
|
||||
}
|
||||
],
|
||||
__typename: "Product"
|
||||
},
|
||||
__typename: "ProductVariant"
|
||||
},
|
||||
__typename: "ProductVariantCountableEdge"
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdFZhcmlhbnQ6MjUz",
|
||||
name: "L",
|
||||
product: {
|
||||
id: "UHJvZHVjdDo4OQ==",
|
||||
name: "Code Division T-shirt",
|
||||
thumbnail: {
|
||||
url: placeholderImage,
|
||||
__typename: "Image"
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6MTQ=",
|
||||
name: "Top (clothing)",
|
||||
__typename: "ProductType"
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Channel-USD",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel"
|
||||
},
|
||||
__typename: "ProductChannelListing"
|
||||
},
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDoy",
|
||||
name: "Channel-PLN",
|
||||
currencyCode: "PLN",
|
||||
__typename: "Channel"
|
||||
},
|
||||
__typename: "ProductChannelListing"
|
||||
}
|
||||
],
|
||||
__typename: "Product"
|
||||
},
|
||||
__typename: "ProductVariant"
|
||||
},
|
||||
__typename: "ProductVariantCountableEdge"
|
||||
}
|
||||
],
|
||||
pageInfo: {
|
||||
endCursor: "W251bGwsICIxMTgyMjM1OTEiXQ==",
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: "W251bGwsICIxMDQwNDk0NiJd",
|
||||
__typename: "PageInfo"
|
||||
},
|
||||
totalCount: 3,
|
||||
__typename: "ProductVariantCountableConnection"
|
||||
},
|
||||
startDate: "2019-01-03",
|
||||
type: "PERCENTAGE" as SaleType
|
||||
};
|
||||
|
|
|
@ -43,6 +43,70 @@ export interface SaleCataloguesAdd_saleCataloguesAdd_sale_channelListings {
|
|||
currency: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_thumbnail {
|
||||
__typename: "Image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_channelListings_channel {
|
||||
__typename: "Channel";
|
||||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
isPublished: boolean;
|
||||
publicationDate: any | null;
|
||||
isAvailableForPurchase: boolean | null;
|
||||
availableForPurchase: any | null;
|
||||
visibleInListings: boolean;
|
||||
channel: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_channelListings_channel;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product {
|
||||
__typename: "Product";
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnail: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_thumbnail | null;
|
||||
productType: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_productType;
|
||||
channelListings: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product_channelListings[] | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
product: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node_product;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges {
|
||||
__typename: "ProductVariantCountableEdge";
|
||||
node: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges_node;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_variants {
|
||||
__typename: "ProductVariantCountableConnection";
|
||||
edges: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_edges[];
|
||||
pageInfo: SaleCataloguesAdd_saleCataloguesAdd_sale_variants_pageInfo;
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_products_edges_node_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
|
@ -174,6 +238,7 @@ export interface SaleCataloguesAdd_saleCataloguesAdd_sale {
|
|||
startDate: any;
|
||||
endDate: any | null;
|
||||
channelListings: SaleCataloguesAdd_saleCataloguesAdd_sale_channelListings[] | null;
|
||||
variants: SaleCataloguesAdd_saleCataloguesAdd_sale_variants | null;
|
||||
products: SaleCataloguesAdd_saleCataloguesAdd_sale_products | null;
|
||||
categories: SaleCataloguesAdd_saleCataloguesAdd_sale_categories | null;
|
||||
collections: SaleCataloguesAdd_saleCataloguesAdd_sale_collections | null;
|
||||
|
|
|
@ -43,6 +43,70 @@ export interface SaleCataloguesRemove_saleCataloguesRemove_sale_channelListings
|
|||
currency: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_thumbnail {
|
||||
__typename: "Image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_channelListings_channel {
|
||||
__typename: "Channel";
|
||||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
isPublished: boolean;
|
||||
publicationDate: any | null;
|
||||
isAvailableForPurchase: boolean | null;
|
||||
availableForPurchase: any | null;
|
||||
visibleInListings: boolean;
|
||||
channel: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_channelListings_channel;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product {
|
||||
__typename: "Product";
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnail: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_thumbnail | null;
|
||||
productType: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_productType;
|
||||
channelListings: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product_channelListings[] | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
product: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node_product;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges {
|
||||
__typename: "ProductVariantCountableEdge";
|
||||
node: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges_node;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_variants {
|
||||
__typename: "ProductVariantCountableConnection";
|
||||
edges: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_edges[];
|
||||
pageInfo: SaleCataloguesRemove_saleCataloguesRemove_sale_variants_pageInfo;
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_products_edges_node_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
|
@ -174,6 +238,7 @@ export interface SaleCataloguesRemove_saleCataloguesRemove_sale {
|
|||
startDate: any;
|
||||
endDate: any | null;
|
||||
channelListings: SaleCataloguesRemove_saleCataloguesRemove_sale_channelListings[] | null;
|
||||
variants: SaleCataloguesRemove_saleCataloguesRemove_sale_variants | null;
|
||||
products: SaleCataloguesRemove_saleCataloguesRemove_sale_products | null;
|
||||
categories: SaleCataloguesRemove_saleCataloguesRemove_sale_categories | null;
|
||||
collections: SaleCataloguesRemove_saleCataloguesRemove_sale_collections | null;
|
||||
|
|
|
@ -36,6 +36,70 @@ export interface SaleDetails_sale_channelListings {
|
|||
currency: string;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node_product_thumbnail {
|
||||
__typename: "Image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node_product_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node_product_channelListings_channel {
|
||||
__typename: "Channel";
|
||||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
isPublished: boolean;
|
||||
publicationDate: any | null;
|
||||
isAvailableForPurchase: boolean | null;
|
||||
availableForPurchase: any | null;
|
||||
visibleInListings: boolean;
|
||||
channel: SaleDetails_sale_variants_edges_node_product_channelListings_channel;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node_product {
|
||||
__typename: "Product";
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnail: SaleDetails_sale_variants_edges_node_product_thumbnail | null;
|
||||
productType: SaleDetails_sale_variants_edges_node_product_productType;
|
||||
channelListings: SaleDetails_sale_variants_edges_node_product_channelListings[] | null;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges_node {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
product: SaleDetails_sale_variants_edges_node_product;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_edges {
|
||||
__typename: "ProductVariantCountableEdge";
|
||||
node: SaleDetails_sale_variants_edges_node;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_variants {
|
||||
__typename: "ProductVariantCountableConnection";
|
||||
edges: SaleDetails_sale_variants_edges[];
|
||||
pageInfo: SaleDetails_sale_variants_pageInfo;
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface SaleDetails_sale_products_edges_node_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
|
@ -167,6 +231,7 @@ export interface SaleDetails_sale {
|
|||
startDate: any;
|
||||
endDate: any | null;
|
||||
channelListings: SaleDetails_sale_channelListings[] | null;
|
||||
variants: SaleDetails_sale_variants | null;
|
||||
products: SaleDetails_sale_products | null;
|
||||
categories: SaleDetails_sale_categories | null;
|
||||
collections: SaleDetails_sale_collections | null;
|
||||
|
|
|
@ -53,9 +53,11 @@ export type SaleUrlDialog =
|
|||
| "assign-category"
|
||||
| "assign-collection"
|
||||
| "assign-product"
|
||||
| "assign-variant"
|
||||
| "unassign-category"
|
||||
| "unassign-collection"
|
||||
| "unassign-product"
|
||||
| "unassign-variant"
|
||||
| "remove"
|
||||
| ChannelsAction;
|
||||
export type SaleUrlQueryParams = Pagination &
|
||||
|
|
|
@ -11,6 +11,7 @@ import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
|||
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
|
||||
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
|
||||
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
||||
import AssignVariantDialog from "@saleor/components/AssignVariantDialog";
|
||||
import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config";
|
||||
|
@ -45,7 +46,7 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { productUrl } from "@saleor/products/urls";
|
||||
import { productUrl, productVariantEditPath } from "@saleor/products/urls";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
|
@ -60,6 +61,7 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { createUpdateHandler } from "./handlers";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface SaleDetailsProps {
|
||||
id: string;
|
||||
|
@ -155,9 +157,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
if (data.saleDelete.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Removed sale"
|
||||
})
|
||||
text: intl.formatMessage(messages.saleDetailsSaleDeleteDialog)
|
||||
});
|
||||
navigate(saleListUrl(), true);
|
||||
}
|
||||
|
@ -197,9 +197,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
onChange={channelsToggle}
|
||||
onClose={handleChannelsModalClose}
|
||||
open={isChannelsModalOpen}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Manage Channel Availability"
|
||||
})}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsChannelAvailabilityDialogHeader
|
||||
)}
|
||||
selected={channelListElements.length}
|
||||
confirmButtonState="default"
|
||||
onConfirm={handleChannelsConfirm}
|
||||
|
@ -219,7 +219,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
? maybe(() => data.sale.categories.pageInfo)
|
||||
: params.activeTab === SaleDetailsPageTab.collections
|
||||
? maybe(() => data.sale.collections.pageInfo)
|
||||
: maybe(() => data.sale.products.pageInfo);
|
||||
: params.activeTab === SaleDetailsPageTab.products
|
||||
? maybe(() => data.sale.products.pageInfo)
|
||||
: maybe(() => data.sale.variants.pageInfo);
|
||||
|
||||
const handleCategoriesUnassign = (ids: string[]) =>
|
||||
saleCataloguesRemove({
|
||||
|
@ -254,6 +256,17 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
}
|
||||
});
|
||||
|
||||
const handleVariantsUnassign = (ids: string[]) =>
|
||||
saleCataloguesRemove({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
variants: ids
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
loadNextPage,
|
||||
loadPreviousPage,
|
||||
|
@ -324,6 +337,14 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
}
|
||||
onProductClick={id => () =>
|
||||
navigate(productUrl(id))}
|
||||
onVariantAssign={() => openModal("assign-variant")}
|
||||
onVariantUnassign={variantId =>
|
||||
handleVariantsUnassign([variantId])
|
||||
}
|
||||
onVariantClick={(productId, variantId) => () =>
|
||||
navigate(
|
||||
productVariantEditPath(productId, variantId)
|
||||
)}
|
||||
activeTab={params.activeTab}
|
||||
onBack={() => navigate(saleListUrl())}
|
||||
onTabClick={changeTab}
|
||||
|
@ -340,9 +361,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Unassign"
|
||||
description="unassign category from sale, button"
|
||||
id="saleDetailsUnassignCategory"
|
||||
{...messages.saleDetailsUnassignCategory}
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
|
@ -356,9 +375,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Unassign"
|
||||
description="unassign collection from sale, button"
|
||||
id="saleDetailsUnassignCollection"
|
||||
{...messages.saleDetailsUnassignCollection}
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
|
@ -372,9 +389,21 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Unassign"
|
||||
description="unassign product from sale, button"
|
||||
id="saleDetailsUnassignProduct"
|
||||
{...messages.saleDetailsUnassignProduct}
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
variantListToolbar={
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
openModal("unassign-variant", {
|
||||
ids: listElements
|
||||
})
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
{...messages.saleDetailsUnassignVariant}
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
|
@ -383,6 +412,34 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<AssignVariantDialog
|
||||
confirmButtonState={saleCataloguesAddOpts.status}
|
||||
hasMore={
|
||||
searchProductsOpts.data?.search.pageInfo
|
||||
.hasNextPage
|
||||
}
|
||||
open={params.action === "assign-variant"}
|
||||
onFetch={searchProducts}
|
||||
onFetchMore={loadMoreProducts}
|
||||
loading={searchProductsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={variants =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
variants: variants.map(
|
||||
variant => variant.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
products={mapEdgesToItems(
|
||||
searchProductsOpts?.data?.search
|
||||
)?.filter(suggestedProduct => suggestedProduct.id)}
|
||||
/>
|
||||
<AssignProductDialog
|
||||
confirmButtonState={saleCataloguesAddOpts.status}
|
||||
hasMore={
|
||||
|
@ -400,9 +457,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
products
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -472,10 +527,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
params.action === "unassign-category" &&
|
||||
canOpenBulkActionDialog
|
||||
}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Unassign Categories From Sale",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsUnassignCategoryDialogHeader
|
||||
)}
|
||||
confirmButtonState={saleCataloguesRemoveOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={() =>
|
||||
|
@ -485,8 +539,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
{canOpenBulkActionDialog && (
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Are you sure you want to unassign this category?} other{Are you sure you want to unassign {displayQuantity} categories?}}"
|
||||
description="dialog content"
|
||||
{...messages.saleDetailsUnassignCategoryDialog}
|
||||
values={{
|
||||
counter: params.ids.length,
|
||||
displayQuantity: (
|
||||
|
@ -502,10 +555,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
params.action === "unassign-collection" &&
|
||||
canOpenBulkActionDialog
|
||||
}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Unassign Collections From Sale",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsUnassignCollectionDialogHeader
|
||||
)}
|
||||
confirmButtonState={saleCataloguesRemoveOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={() =>
|
||||
|
@ -515,8 +567,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
{canOpenBulkActionDialog && (
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Are you sure you want to unassign this collection?} other{Are you sure you want to unassign {displayQuantity} collections?}}"
|
||||
description="dialog content"
|
||||
{...messages.saleDetailsUnassignCollectionDialog}
|
||||
values={{
|
||||
counter: params.ids.length,
|
||||
displayQuantity: (
|
||||
|
@ -532,10 +583,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
params.action === "unassign-product" &&
|
||||
canOpenBulkActionDialog
|
||||
}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Unassign Products From Sale",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsUnassignProductDialogHeader
|
||||
)}
|
||||
confirmButtonState={saleCataloguesRemoveOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={() => handleProductsUnassign(params.ids)}
|
||||
|
@ -543,8 +593,33 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
{canOpenBulkActionDialog && (
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="{counter,plural,one{Are you sure you want to unassign this product?} other{Are you sure you want to unassign {displayQuantity} products?}}"
|
||||
description="dialog content"
|
||||
{...messages.saleDetailsUnassignCategoryDialog}
|
||||
values={{
|
||||
counter: params.ids.length,
|
||||
displayQuantity: (
|
||||
<strong>{params.ids.length}</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
)}
|
||||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={
|
||||
params.action === "unassign-variant" &&
|
||||
canOpenBulkActionDialog
|
||||
}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsUnassignVariantDialogHeader
|
||||
)}
|
||||
confirmButtonState={saleCataloguesRemoveOpts.status}
|
||||
onClose={closeModal}
|
||||
onConfirm={() => handleVariantsUnassign(params.ids)}
|
||||
>
|
||||
{canOpenBulkActionDialog && (
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
{...messages.saleDetailsUnassignVariantDialog}
|
||||
values={{
|
||||
counter: params.ids.length,
|
||||
displayQuantity: (
|
||||
|
@ -557,10 +632,9 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Sale",
|
||||
description: "dialog header"
|
||||
})}
|
||||
title={intl.formatMessage(
|
||||
messages.saleDetailsSaleDeleteDialogHeader
|
||||
)}
|
||||
confirmButtonState={saleDeleteOpts.status}
|
||||
onClose={closeModal}
|
||||
variant="delete"
|
||||
|
@ -572,8 +646,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {saleName}?"
|
||||
description="dialog content"
|
||||
{...messages.saleDetailsUnassignDialogDelete}
|
||||
values={{
|
||||
saleName: (
|
||||
<strong>
|
||||
|
|
76
src/discounts/views/SaleDetails/messages.ts
Normal file
76
src/discounts/views/SaleDetails/messages.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
saleDetailsUnassignCategory: {
|
||||
defaultMessage: "Unassign",
|
||||
description: "unassign category from sale, button"
|
||||
},
|
||||
saleDetailsUnassignCollection: {
|
||||
defaultMessage: "Unassign",
|
||||
description: "unassign collection from sale, button"
|
||||
},
|
||||
saleDetailsUnassignProduct: {
|
||||
defaultMessage: "Unassign",
|
||||
description: "unassign product from sale, button"
|
||||
},
|
||||
saleDetailsUnassignVariant: {
|
||||
defaultMessage: "Unassign",
|
||||
description: "unassign variant from sale, button"
|
||||
},
|
||||
saleDetailsUnassignCategoryDialog: {
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to unassign this category?} other{Are you sure you want to unassign {displayQuantity} categories?}}",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsUnassignCollectionDialog: {
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to unassign this collection?} other{Are you sure you want to unassign {displayQuantity} collections?}}",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsUnassignProductDialog: {
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to unassign this product?} other{Are you sure you want to unassign {displayQuantity} products?}}",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsUnassignVariantDialog: {
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to unassign this variant?} other{Are you sure you want to unassign {displayQuantity} variants?}}",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsUnassignDialogDelete: {
|
||||
defaultMessage: "Are you sure you want to delete {saleName}?",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsSaleDelate: {
|
||||
defaultMessage: "Removed sale",
|
||||
description: "sale Details delete button"
|
||||
},
|
||||
saleDetailsUnassignCategoryDialogHeader: {
|
||||
defaultMessage: "Unassign Categories From Sale",
|
||||
description: "dialog header"
|
||||
},
|
||||
saleDetailsUnassignCollectionDialogHeader: {
|
||||
defaultMessage: "Unassign Collection From Sale",
|
||||
description: "dialog header"
|
||||
},
|
||||
saleDetailsUnassignProductDialogHeader: {
|
||||
defaultMessage: "Unassign Product From Sale",
|
||||
description: "dialog header"
|
||||
},
|
||||
saleDetailsUnassignVariantDialogHeader: {
|
||||
defaultMessage: "Unassign Variant From Sale",
|
||||
description: "dialog header"
|
||||
},
|
||||
saleDetailsSaleDeleteDialog: {
|
||||
defaultMessage: "Removed sale",
|
||||
description: "dialog content"
|
||||
},
|
||||
saleDetailsSaleDeleteDialogHeader: {
|
||||
defaultMessage: "Delete Sale",
|
||||
description: "dialog header"
|
||||
},
|
||||
saleDetailsChannelAvailabilityDialogHeader: {
|
||||
defaultMessage: "Manage Channel Availability",
|
||||
description: "channel availability dialog header"
|
||||
}
|
||||
});
|
|
@ -533,9 +533,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
|||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
products
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -32,6 +32,32 @@ export const saleDetailsFragment = gql`
|
|||
${saleFragment}
|
||||
fragment SaleDetailsFragment on Sale {
|
||||
...SaleFragment
|
||||
variants(after: $after, before: $before, first: $first, last: $last) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
product {
|
||||
id
|
||||
name
|
||||
thumbnail {
|
||||
url
|
||||
}
|
||||
productType {
|
||||
id
|
||||
name
|
||||
}
|
||||
channelListings {
|
||||
...ChannelListingProductWithoutPricingFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfoFragment
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
products(after: $after, before: $before, first: $first, last: $last) {
|
||||
edges {
|
||||
node {
|
||||
|
|
|
@ -36,6 +36,70 @@ export interface SaleDetailsFragment_channelListings {
|
|||
currency: string;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node_product_thumbnail {
|
||||
__typename: "Image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node_product_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node_product_channelListings_channel {
|
||||
__typename: "Channel";
|
||||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
isPublished: boolean;
|
||||
publicationDate: any | null;
|
||||
isAvailableForPurchase: boolean | null;
|
||||
availableForPurchase: any | null;
|
||||
visibleInListings: boolean;
|
||||
channel: SaleDetailsFragment_variants_edges_node_product_channelListings_channel;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node_product {
|
||||
__typename: "Product";
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnail: SaleDetailsFragment_variants_edges_node_product_thumbnail | null;
|
||||
productType: SaleDetailsFragment_variants_edges_node_product_productType;
|
||||
channelListings: SaleDetailsFragment_variants_edges_node_product_channelListings[] | null;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges_node {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
product: SaleDetailsFragment_variants_edges_node_product;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_edges {
|
||||
__typename: "ProductVariantCountableEdge";
|
||||
node: SaleDetailsFragment_variants_edges_node;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_variants {
|
||||
__typename: "ProductVariantCountableConnection";
|
||||
edges: SaleDetailsFragment_variants_edges[];
|
||||
pageInfo: SaleDetailsFragment_variants_pageInfo;
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface SaleDetailsFragment_products_edges_node_productType {
|
||||
__typename: "ProductType";
|
||||
id: string;
|
||||
|
@ -167,6 +231,7 @@ export interface SaleDetailsFragment {
|
|||
startDate: any;
|
||||
endDate: any | null;
|
||||
channelListings: SaleDetailsFragment_channelListings[] | null;
|
||||
variants: SaleDetailsFragment_variants | null;
|
||||
products: SaleDetailsFragment_products | null;
|
||||
categories: SaleDetailsFragment_categories | null;
|
||||
collections: SaleDetailsFragment_collections | null;
|
||||
|
|
|
@ -12,11 +12,40 @@ export interface SearchProducts_search_edges_node_thumbnail {
|
|||
url: string;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges_node_variants_channelListings_channel {
|
||||
__typename: "Channel";
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges_node_variants_channelListings_price {
|
||||
__typename: "Money";
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges_node_variants_channelListings {
|
||||
__typename: "ProductVariantChannelListing";
|
||||
channel: SearchProducts_search_edges_node_variants_channelListings_channel;
|
||||
price: SearchProducts_search_edges_node_variants_channelListings_price | null;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges_node_variants {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
sku: string;
|
||||
channelListings: SearchProducts_search_edges_node_variants_channelListings[] | null;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges_node {
|
||||
__typename: "Product";
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnail: SearchProducts_search_edges_node_thumbnail | null;
|
||||
variants: (SearchProducts_search_edges_node_variants | null)[] | null;
|
||||
}
|
||||
|
||||
export interface SearchProducts_search_edges {
|
||||
|
|
55
src/searches/types/SearchVariants.ts
Normal file
55
src/searches/types/SearchVariants.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: SearchVariants
|
||||
// ====================================================
|
||||
|
||||
export interface SearchVariants_search_edges_node_product_thumbnail {
|
||||
__typename: "Image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SearchVariants_search_edges_node_product {
|
||||
__typename: "Product";
|
||||
name: string;
|
||||
thumbnail: SearchVariants_search_edges_node_product_thumbnail | null;
|
||||
}
|
||||
|
||||
export interface SearchVariants_search_edges_node {
|
||||
__typename: "ProductVariant";
|
||||
id: string;
|
||||
name: string;
|
||||
product: SearchVariants_search_edges_node_product;
|
||||
}
|
||||
|
||||
export interface SearchVariants_search_edges {
|
||||
__typename: "ProductVariantCountableEdge";
|
||||
node: SearchVariants_search_edges_node;
|
||||
}
|
||||
|
||||
export interface SearchVariants_search_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
endCursor: string | null;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
}
|
||||
|
||||
export interface SearchVariants_search {
|
||||
__typename: "ProductVariantCountableConnection";
|
||||
edges: SearchVariants_search_edges[];
|
||||
pageInfo: SearchVariants_search_pageInfo;
|
||||
}
|
||||
|
||||
export interface SearchVariants {
|
||||
search: SearchVariants_search | null;
|
||||
}
|
||||
|
||||
export interface SearchVariantsVariables {
|
||||
after?: string | null;
|
||||
first: number;
|
||||
query: string;
|
||||
}
|
|
@ -18,6 +18,23 @@ export const searchProducts = gql`
|
|||
thumbnail {
|
||||
url
|
||||
}
|
||||
variants {
|
||||
id
|
||||
name
|
||||
sku
|
||||
channelListings {
|
||||
channel {
|
||||
id
|
||||
isActive
|
||||
name
|
||||
currencyCode
|
||||
}
|
||||
price {
|
||||
amount
|
||||
currency
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
|
|
|
@ -1873,15 +1873,46 @@ export const products: SearchProducts_search_edges_node[] = [
|
|||
thumbnail: {
|
||||
__typename: "Image",
|
||||
url: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Product",
|
||||
id: "2",
|
||||
name: "Banana Juice",
|
||||
thumbnail: {
|
||||
__typename: "Image",
|
||||
url: ""
|
||||
}
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
__typename: "ProductVariant",
|
||||
id: "UHJvZHVjdFZhcmlhbnQ6MjAz",
|
||||
name: "1l",
|
||||
sku: "43226647",
|
||||
channelListings: [
|
||||
{
|
||||
__typename: "ProductVariantChannelListing",
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
id: "Q2hhbm5lbDox",
|
||||
isActive: true,
|
||||
name: "Channel-USD",
|
||||
currencyCode: "USD"
|
||||
},
|
||||
price: {
|
||||
__typename: "Money",
|
||||
amount: 5,
|
||||
currency: "USD"
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "ProductVariantChannelListing",
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
id: "Q2hhbm5lbDoy",
|
||||
isActive: true,
|
||||
name: "Channel-PLN",
|
||||
currencyCode: "PLN"
|
||||
},
|
||||
price: {
|
||||
__typename: "Money",
|
||||
amount: 20,
|
||||
currency: "PLN"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
|
|
@ -88061,6 +88061,12 @@ exports[`Storyshots Views / Discounts / Sale details collections 1`] = `
|
|||
>
|
||||
Products (4)
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root-id Tab-root-id MuiTypography-body1-id"
|
||||
data-test-id="variants-tab"
|
||||
>
|
||||
Variants (3)
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
|
@ -88155,10 +88161,10 @@ exports[`Storyshots Views / Discounts / Sale details collections 1`] = `
|
|||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCollections-colName-id"
|
||||
scope="col"
|
||||
>
|
||||
Collection name
|
||||
Collection Name
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCollections-textRight-id"
|
||||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCollections-colProducts-id"
|
||||
scope="col"
|
||||
>
|
||||
Products
|
||||
|
@ -89484,6 +89490,12 @@ exports[`Storyshots Views / Discounts / Sale details default 1`] = `
|
|||
>
|
||||
Products (4)
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root-id Tab-root-id MuiTypography-body1-id"
|
||||
data-test-id="variants-tab"
|
||||
>
|
||||
Variants (3)
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
|
@ -89578,7 +89590,7 @@ exports[`Storyshots Views / Discounts / Sale details default 1`] = `
|
|||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colName-id"
|
||||
scope="col"
|
||||
>
|
||||
Category name
|
||||
Category Name
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colProducts-id"
|
||||
|
@ -90912,6 +90924,12 @@ exports[`Storyshots Views / Discounts / Sale details form errors 1`] = `
|
|||
>
|
||||
Products (4)
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root-id Tab-root-id MuiTypography-body1-id"
|
||||
data-test-id="variants-tab"
|
||||
>
|
||||
Variants (3)
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
|
@ -91006,7 +91024,7 @@ exports[`Storyshots Views / Discounts / Sale details form errors 1`] = `
|
|||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colName-id"
|
||||
scope="col"
|
||||
>
|
||||
Category name
|
||||
Category Name
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colProducts-id"
|
||||
|
@ -92363,6 +92381,12 @@ exports[`Storyshots Views / Discounts / Sale details loading 1`] = `
|
|||
>
|
||||
Products (…)
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root-id Tab-root-id MuiTypography-body1-id"
|
||||
data-test-id="variants-tab"
|
||||
>
|
||||
Variants (…)
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
|
@ -92459,7 +92483,7 @@ exports[`Storyshots Views / Discounts / Sale details loading 1`] = `
|
|||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colName-id"
|
||||
scope="col"
|
||||
>
|
||||
Category name
|
||||
Category Name
|
||||
</th>
|
||||
<th
|
||||
class="MuiTableCell-root-id MuiTableCell-head-id DiscountCategories-colProducts-id"
|
||||
|
@ -93778,6 +93802,12 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = `
|
|||
>
|
||||
Products (4)
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root-id Tab-root-id MuiTypography-body1-id"
|
||||
data-test-id="variants-tab"
|
||||
>
|
||||
Variants (3)
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||
import AssignProductDialog, {
|
||||
AssignProductDialogProps
|
||||
} from "@saleor/components/AssignProductDialog";
|
||||
import { fetchMoreProps } from "@saleor/fixtures";
|
||||
import { products } from "@saleor/products/fixtures";
|
||||
import { products } from "@saleor/shipping/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -17,7 +16,7 @@ const props: AssignProductDialogProps = {
|
|||
onFetch: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
open: true,
|
||||
products: products(placeholderImage)
|
||||
products
|
||||
};
|
||||
|
||||
storiesOf("Generics / Assign product", module)
|
||||
|
|
|
@ -36,6 +36,9 @@ const props: SaleDetailsPageProps = {
|
|||
onProductAssign: () => undefined,
|
||||
onProductClick: () => undefined,
|
||||
onProductUnassign: () => undefined,
|
||||
onVariantAssign: () => undefined,
|
||||
onVariantClick: () => undefined,
|
||||
onVariantUnassign: () => undefined,
|
||||
onRemove: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
onTabClick: () => undefined,
|
||||
|
@ -45,6 +48,7 @@ const props: SaleDetailsPageProps = {
|
|||
hasPreviousPage: false
|
||||
},
|
||||
productListToolbar: null,
|
||||
variantListToolbar: null,
|
||||
sale,
|
||||
saveButtonBarState: "default",
|
||||
selectedChannelId: "123",
|
||||
|
|
|
@ -2018,6 +2018,7 @@ export interface CatalogueInput {
|
|||
products?: (string | null)[] | null;
|
||||
categories?: (string | null)[] | null;
|
||||
collections?: (string | null)[] | null;
|
||||
variants?: (string | null)[] | null;
|
||||
}
|
||||
|
||||
export interface CategoryFilterInput {
|
||||
|
@ -2674,6 +2675,7 @@ export interface SaleInput {
|
|||
type?: DiscountValueTypeEnum | null;
|
||||
value?: any | null;
|
||||
products?: (string | null)[] | null;
|
||||
variants?: (string | null)[] | null;
|
||||
categories?: (string | null)[] | null;
|
||||
collections?: (string | null)[] | null;
|
||||
startDate?: any | null;
|
||||
|
|
Loading…
Reference in a new issue