diff --git a/cypress/elements/catalog/product-selectors.js b/cypress/elements/catalog/product-selectors.js index c2ef76ef7..f2f584994 100644 --- a/cypress/elements/catalog/product-selectors.js +++ b/cypress/elements/catalog/product-selectors.js @@ -10,5 +10,6 @@ export const PRODUCTS_SELECTORS = { firstCategoryItem: "#downshift-0-item-0", visibleRadioBtn: "[name='isPublished']", saveBtn: "[data-test='button-bar-confirm']", - confirmationMsg: "[data-test='notification']" + confirmationMsg: "[data-test='notification']", + channelAvailabilityItem: "[data-test='channel-availability-item']" }; diff --git a/cypress/elements/shared/button-selectors.js b/cypress/elements/shared/button-selectors.js new file mode 100644 index 000000000..12b956298 --- /dev/null +++ b/cypress/elements/shared/button-selectors.js @@ -0,0 +1,5 @@ +/* eslint-disable sort-keys */ +export const BUTTON_SELECTORS = { + back: '[data-test="back"]', + submit: '[data-test="submit"]' +}; diff --git a/cypress/integration/products.js b/cypress/integration/products.js index ae2141928..dd8c787a2 100644 --- a/cypress/integration/products.js +++ b/cypress/integration/products.js @@ -1,5 +1,6 @@ import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors"; import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors"; +import { BUTTON_SELECTORS } from "../elements/shared/button-selectors"; // describe("Products", () => { @@ -13,6 +14,8 @@ describe("Products", () => { .click() .get(PRODUCTS_SELECTORS.products) .click() + .get(BUTTON_SELECTORS.submit) + .click() .get(PRODUCTS_SELECTORS.createProductBtn) .click() .get(PRODUCTS_SELECTORS.productNameInput) @@ -33,6 +36,9 @@ describe("Products", () => { .get(PRODUCTS_SELECTORS.categoryItem) .first() .click() + .get(PRODUCTS_SELECTORS.channelAvailabilityItem) + .first() + .click() .get(PRODUCTS_SELECTORS.visibleRadioBtn) .first() .click() diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index d786531cc..9c1bf1f91 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -6,6 +6,9 @@ "configurationMenuAttributes": { "string": "Determine attributes used to create product types" }, + "configurationMenuChannels": { + "string": "Define and manage your sales channels" + }, "configurationMenuNavigation": { "string": "Define how users can navigate through your store" }, @@ -205,10 +208,6 @@ "context": "vat not included in order price", "string": "does not apply" }, - "productExportFieldAvailability": { - "context": "product field", - "string": "Available for purchase" - }, "productExportFieldCategory": { "context": "product field", "string": "Category" @@ -225,10 +224,6 @@ "context": "product field", "string": "Name" }, - "productExportFieldPrice": { - "context": "product field", - "string": "Cost Price" - }, "productExportFieldProductImages": { "context": "product field", "string": "Product Images" @@ -249,10 +244,6 @@ "context": "product field", "string": "Variant Images" }, - "productExportFieldVariantPrice": { - "context": "product field", - "string": "Variant Price" - }, "productExportFieldVariantSku": { "context": "product field", "string": "Export Variant SKU" @@ -265,10 +256,6 @@ "context": "product field", "string": "Visibility" }, - "productStatusLabel": { - "context": "product", - "string": "Published" - }, "productStockHeader": { "context": "product stock, section header", "string": "Inventory" @@ -288,11 +275,8 @@ "productTypeTaxesInputLabel": { "string": "Taxes" }, - "productVariantCreatePricesPriceInputLabel": { - "string": "Price" - }, "productVariantCreatePricesSetPricePlaceholder": { - "context": "variant price", + "context": "input label", "string": "Price" }, "productVariantCreatePricesStockInputLabel": { @@ -310,10 +294,6 @@ "context": "header", "string": "Warehouses" }, - "productVariantPriceOptionalCostPriceField": { - "context": "optional field", - "string": "Optional" - }, "productVariantWarehouseSectionDescription": { "context": "no warehouses info", "string": "There are no warehouses set up for your store. To add stock quantity to the variant please configure a warehouse" @@ -1064,13 +1044,9 @@ "context": "product type", "string": "Type" }, - "src_dot_categories_dot_components_dot_CategoryProductList_dot_2341910657": { - "context": "product", - "string": "Not published" - }, - "src_dot_categories_dot_components_dot_CategoryProductList_dot_3640454975": { - "context": "product status", - "string": "Published" + "src_dot_categories_dot_components_dot_CategoryProductList_dot_3326160357": { + "context": "availability status", + "string": "Availability" }, "src_dot_categories_dot_components_dot_CategoryProductList_dot_636461959": { "context": "product", @@ -1150,6 +1126,128 @@ "context": "dialog title", "string": "Delete categories" }, + "src_dot_channels": { + "context": "channels section name", + "string": "Channels" + }, + "src_dot_channels_dot_components_dot_ChannelDeleteDialog_dot_2257704694": { + "context": "delete channel", + "string": "All order information from this channel need to be moved to a different channel. Please select channel orders need to be moved to:." + }, + "src_dot_channels_dot_components_dot_ChannelDeleteDialog_dot_2342703674": { + "context": "dialog header", + "string": "Select Channel" + }, + "src_dot_channels_dot_components_dot_ChannelDeleteDialog_dot_2405647976": { + "context": "dialog header", + "string": "Delete Channel" + }, + "src_dot_channels_dot_components_dot_ChannelDeleteDialog_dot_777038435": { + "context": "delete channel", + "string": "Deleting channel will delete all product data regarding this channel. Are you sure you want to delete this channel?" + }, + "src_dot_channels_dot_components_dot_ChannelDeleteDialog_dot_863901": { + "context": "currency channel", + "string": "There is no available channel to move order information to. Please create a channel with same currency so that information can be moved to it." + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_1223259680": { + "context": "channel settings", + "string": "Channel Settings" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_1843613796": { + "context": "selected currency", + "string": "Selected Currency" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_2864204643": { + "context": "button", + "string": "Copied" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_3478065224": { + "context": "channel slug", + "string": "Slug" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_3511613983": { + "context": "channel name", + "string": "Channel Name" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_383867403": { + "context": "button", + "string": "Copy" + }, + "src_dot_channels_dot_components_dot_ChannelForm_dot_3873626327": { + "context": "channel currency", + "string": "Currency" + }, + "src_dot_channels_dot_components_dot_ChannelSettingsDialog_dot_1391686013": { + "context": "dialog header", + "string": "Settings" + }, + "src_dot_channels_dot_components_dot_ChannelSettingsDialog_dot_2721512922": { + "context": "channel settings", + "string": "Configure the way information are presented in catalog section of Dashboard." + }, + "src_dot_channels_dot_components_dot_ChannelSettingsDialog_dot_645871430": { + "context": "select label", + "string": "Show prices for" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_1004218338": { + "context": "inactive", + "string": "Inactive" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_1756106276": { + "context": "status", + "string": "Status" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_1782042241": { + "context": "deactivate", + "string": "Deactivate" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_2605883031": { + "context": "channel status title", + "string": "Channel Status" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_3247064221": { + "context": "active", + "string": "Active" + }, + "src_dot_channels_dot_components_dot_ChannelStatus_dot_3865193889": { + "context": "activate", + "string": "Activate" + }, + "src_dot_channels_dot_pages_dot_ChannelsListPage_dot_3511613983": { + "context": "channel name", + "string": "Channel Name" + }, + "src_dot_channels_dot_pages_dot_ChannelsListPage_dot_4097816011": { + "string": "No channels found" + }, + "src_dot_channels_dot_pages_dot_ChannelsListPage_dot_4190792473": { + "context": "table actions", + "string": "Actions" + }, + "src_dot_channels_dot_pages_dot_ChannelsListPage_dot_4224047662": { + "context": "button", + "string": "Create Channel" + }, + "src_dot_channels_dot_views_dot_ChannelCreate_dot_2071399707": { + "context": "currency code select", + "string": "{code} - {countries}" + }, + "src_dot_channels_dot_views_dot_ChannelCreate_dot_4169662653": { + "context": "channel create", + "string": "New Channel" + }, + "src_dot_channels_dot_views_dot_ChannelCreate_dot_4224047662": { + "context": "window title", + "string": "Create Channel" + }, + "src_dot_channels_dot_views_dot_ChannelDetails_dot_1742743610": { + "context": "window title", + "string": "Channel details" + }, + "src_dot_channels_dot_views_dot_ChannelsList_dot_3499322424": { + "string": "Channel deleted" + }, "src_dot_clear": { "context": "button", "string": "Clear" @@ -1158,10 +1256,6 @@ "context": "collections section name", "string": "Collections" }, - "src_dot_collections_dot_components_dot_CollectionCreatePage_dot_2001551496": { - "context": "collection", - "string": "will be visible from {date}" - }, "src_dot_collections_dot_components_dot_CollectionCreatePage_dot_643174786": { "context": "collection label", "string": "Visible" @@ -1177,14 +1271,6 @@ "context": "page header", "string": "Add Collection" }, - "src_dot_collections_dot_components_dot_CollectionDetailsPage_dot_2001551496": { - "context": "collection", - "string": "will be visible from {date}" - }, - "src_dot_collections_dot_components_dot_CollectionDetailsPage_dot_2906897537": { - "context": "switch button", - "string": "Feature on Homepage" - }, "src_dot_collections_dot_components_dot_CollectionDetailsPage_dot_643174786": { "context": "collection label", "string": "Visible" @@ -1208,6 +1294,10 @@ "context": "field is optional", "string": "(Optional)" }, + "src_dot_collections_dot_components_dot_CollectionListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_collections_dot_components_dot_CollectionListPage_dot_1631917001": { "context": "tab name", "string": "All Collections" @@ -1230,10 +1320,6 @@ "src_dot_collections_dot_components_dot_CollectionList_dot_2137803833": { "string": "No collections found" }, - "src_dot_collections_dot_components_dot_CollectionList_dot_2341910657": { - "context": "collection is not published", - "string": "Not published" - }, "src_dot_collections_dot_components_dot_CollectionList_dot_2527742754": { "string": "No. of Products" }, @@ -1244,10 +1330,6 @@ "context": "collection availability", "string": "Availability" }, - "src_dot_collections_dot_components_dot_CollectionList_dot_3640454975": { - "context": "collection is published", - "string": "Published" - }, "src_dot_collections_dot_components_dot_CollectionProducts_dot_1657559629": { "string": "No products found" }, @@ -1255,13 +1337,9 @@ "context": "product type", "string": "Type" }, - "src_dot_collections_dot_components_dot_CollectionProducts_dot_2341910657": { - "context": "product is not published", - "string": "Not published" - }, - "src_dot_collections_dot_components_dot_CollectionProducts_dot_3640454975": { - "context": "product is published", - "string": "Published" + "src_dot_collections_dot_components_dot_CollectionProducts_dot_3326160357": { + "context": "product availability", + "string": "Availability" }, "src_dot_collections_dot_components_dot_CollectionProducts_dot_4114667680": { "context": "products in collection", @@ -1290,6 +1368,9 @@ "src_dot_collections_dot_views_dot_3482612628": { "string": "Deleted product from collection" }, + "src_dot_collections_dot_views_dot_3576236500": { + "string": "Manage Collection Channel Availability" + }, "src_dot_collections_dot_views_dot_3791354625": { "context": "dialog title", "string": "Unassign products from collection" @@ -1313,31 +1394,9 @@ "context": "dialog title", "string": "Delete image" }, - "src_dot_collections_dot_views_dot_CollectionList_dot_1547167026": { - "context": "publish collections", - "string": "Publish" - }, - "src_dot_collections_dot_views_dot_CollectionList_dot_1944626595": { - "string": "{counter,plural,one{Are you sure you want to unpublish this collection?} other{Are you sure you want to unpublish {displayQuantity} collections?}}" - }, - "src_dot_collections_dot_views_dot_CollectionList_dot_2237014112": { - "context": "unpublish collections", - "string": "Unpublish" - }, "src_dot_collections_dot_views_dot_CollectionList_dot_2491832187": { "string": "{counter,plural,one{Are you sure you want to delete this collection?} other{Are you sure you want to delete {displayQuantity} collections?}}" }, - "src_dot_collections_dot_views_dot_CollectionList_dot_2637364047": { - "context": "dialog title", - "string": "Unpublish collections" - }, - "src_dot_collections_dot_views_dot_CollectionList_dot_2756013640": { - "string": "{counter,plural,one{Are you sure you want to publish this collection?} other{Are you sure you want to publish {displayQuantity} collections?}}" - }, - "src_dot_collections_dot_views_dot_CollectionList_dot_2823425739": { - "context": "dialog title", - "string": "Publish collections" - }, "src_dot_collections_dot_views_dot_CollectionList_dot_3817188998": { "context": "dialog title", "string": "Delete collections" @@ -1445,18 +1504,110 @@ "context": "product availability", "string": "Available for purchase" }, + "src_dot_components_dot_AvailabilityCard_dot_2162667201": { + "context": "channel publication date", + "string": "Visible since {date}" + }, + "src_dot_components_dot_AvailabilityCard_dot_2232321263": { + "context": "product publication date label", + "string": "will become published on {date}" + }, "src_dot_components_dot_AvailabilityCard_dot_2938074852": { "context": "product available for purchase date", "string": "will become available on {date}" }, + "src_dot_components_dot_AvailabilityCard_dot_3285520461": { + "context": "channel publication date", + "string": "Will become available on {date}" + }, "src_dot_components_dot_AvailabilityCard_dot_570524410": { "context": "product availability date label", "string": "Set availability date" }, + "src_dot_components_dot_AvailabilityCard_dot_643174786": { + "context": "channel publication status", + "string": "Visible" + }, + "src_dot_components_dot_AvailabilityCard_dot_77815154": { + "context": "channel publication status", + "string": "Hidden" + }, "src_dot_components_dot_AvailabilityCard_dot_825317195": { "context": "product unavailability", "string": "Unavailable for purchase" }, + "src_dot_components_dot_ChannelsAvailabilityContent_dot_1528830621": { + "string": "Select channels you want for {contentType} to be available on" + }, + "src_dot_components_dot_ChannelsAvailabilityContent_dot_1866672276": { + "string": "No Channels found" + }, + "src_dot_components_dot_ChannelsAvailabilityContent_dot_3630122739": { + "string": "Available at all channels" + }, + "src_dot_components_dot_ChannelsAvailabilityContent_dot_3754190292": { + "string": "Channels A to Z" + }, + "src_dot_components_dot_ChannelsAvailabilityContent_dot_4243012684": { + "string": "Search through channels" + }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1043589445": { + "context": "product channel publication status", + "string": "hidden" + }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1484966255": { + "context": "product status title", + "string": "Available in {count}/{allCount}" + }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1702481199": { + "context": "product channel publication date", + "string": "published since {date}" + }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_3285520461": { + "context": "product channel publication date", + "string": "Will become available on {date}" + }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_802058289": { + "context": "product status", + "string": "Available in {count} out of {allCount, plural, one {# channel} other {# channels}}" + }, + "src_dot_components_dot_ChannelsAvailability_dot_1311467573": { + "string": "Show in product listings" + }, + "src_dot_components_dot_ChannelsAvailability_dot_1815688500": { + "context": "date", + "string": "since {date}" + }, + "src_dot_components_dot_ChannelsAvailability_dot_2060790769": { + "context": "publish on date", + "string": "Publish on" + }, + "src_dot_components_dot_ChannelsAvailability_dot_2264302389": { + "string": "Disabling this checkbox will remove product from search and category pages. It will be available on collection pages." + }, + "src_dot_components_dot_ChannelsAvailability_dot_2699516026": { + "context": "available on date", + "string": "Set available on" + }, + "src_dot_components_dot_ChannelsAvailability_dot_292404896": { + "string": "Set publication date" + }, + "src_dot_components_dot_ChannelsAvailability_dot_3326160357": { + "context": "section header", + "string": "Availability" + }, + "src_dot_components_dot_ChannelsAvailability_dot_370220662": { + "context": "channels availability text", + "string": "Available at {selectedChannelsCount} out of {allChannelsCount, plural, one {# channel} other {# channels}}" + }, + "src_dot_components_dot_ChannelsAvailability_dot_4037103586": { + "context": "section header button", + "string": "Manage" + }, + "src_dot_components_dot_ChannelsSelect_dot_4183335632": { + "context": "channel select label", + "string": "Channel:" + }, "src_dot_components_dot_ColumnPicker_dot_1483881697": { "context": "button", "string": "Reset" @@ -1782,6 +1933,9 @@ "context": "button", "string": "Go back to dashboard" }, + "src_dot_components_dot_PriceField_dot_4072537038": { + "string": "Price cannot be lower than 0" + }, "src_dot_components_dot_RadioGroupField_dot_4205644805": { "string": "No results found" }, @@ -1925,6 +2079,9 @@ "src_dot_configuration_dot_3655543906": { "string": "Product Settings" }, + "src_dot_configuration_dot_887806978": { + "string": "Multichannel" + }, "src_dot_confirm": { "context": "button", "string": "Confirm" @@ -2084,10 +2241,6 @@ "context": "customer", "string": "Join Date" }, - "src_dot_customers_dot_components_dot_CustomerListPage_dot_moneySpent": { - "context": "customer", - "string": "Money Spent" - }, "src_dot_customers_dot_components_dot_CustomerListPage_dot_numberOfOrders": { "string": "Number of Orders" }, @@ -2263,16 +2416,12 @@ "context": "button", "string": "Assign products" }, - "src_dot_discounts_dot_components_dot_DiscountProducts_dot_2341910657": { - "context": "product is not published", - "string": "Not published" - }, "src_dot_discounts_dot_components_dot_DiscountProducts_dot_2697405188": { "string": "Product Name" }, - "src_dot_discounts_dot_components_dot_DiscountProducts_dot_3640454975": { - "context": "product is published", - "string": "Published" + "src_dot_discounts_dot_components_dot_DiscountProducts_dot_3326160357": { + "context": "product availability", + "string": "Availability" }, "src_dot_discounts_dot_components_dot_DiscountProducts_dot_4257289053": { "string": "Product Type" @@ -2289,6 +2438,10 @@ "context": "sale name", "string": "Name" }, + "src_dot_discounts_dot_components_dot_SaleListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_discounts_dot_components_dot_SaleListPage_dot_1866913828": { "string": "Search Sale" }, @@ -2378,6 +2531,17 @@ "context": "sale discount", "string": "Discount Value" }, + "src_dot_discounts_dot_components_dot_SaleValue_dot_1615342575": { + "context": "channels sale info", + "string": "Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency" + }, + "src_dot_discounts_dot_components_dot_SaleValue_dot_4097816011": { + "string": "No channels found" + }, + "src_dot_discounts_dot_components_dot_SaleValue_dot_507892781": { + "context": "column title", + "string": "Channel name" + }, "src_dot_discounts_dot_components_dot_VoucherCreatePage_dot_1357216572": { "context": "page header", "string": "Create Voucher" @@ -2438,6 +2602,10 @@ "context": "tab name", "string": "All Vouchers" }, + "src_dot_discounts_dot_components_dot_VoucherListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_discounts_dot_components_dot_VoucherListPage_dot_1930485532": { "string": "Search Voucher" }, @@ -2507,6 +2675,13 @@ "src_dot_discounts_dot_components_dot_VoucherList_dot_79160621": { "string": "No vouchers found" }, + "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_1148029984": { + "context": "column title", + "string": "Value" + }, + "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_1615342575": { + "string": "Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency" + }, "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_2746532349": { "context": "voucher requirements, header", "string": "Minimum Requirements" @@ -2519,6 +2694,13 @@ "context": "voucher requirement", "string": "Minimum quantity of items" }, + "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_4097816011": { + "string": "No channels found" + }, + "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_507892781": { + "context": "column title", + "string": "Channel name" + }, "src_dot_discounts_dot_components_dot_VoucherRequirements_dot_653777456": { "context": "voucher requirement", "string": "Minimal order value" @@ -2559,6 +2741,10 @@ "context": "voucher discount type", "string": "Fixed Amount" }, + "src_dot_discounts_dot_components_dot_VoucherValue_dot_1134347598": { + "context": "column title", + "string": "Price" + }, "src_dot_discounts_dot_components_dot_VoucherValue_dot_1148029984": { "context": "section header", "string": "Value" @@ -2570,12 +2756,22 @@ "context": "voucher application, switch button", "string": "Only once per order" }, + "src_dot_discounts_dot_components_dot_VoucherValue_dot_1615342575": { + "string": "Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency" + }, "src_dot_discounts_dot_components_dot_VoucherValue_dot_1960678372": { "string": "Voucher Specific Information" }, + "src_dot_discounts_dot_components_dot_VoucherValue_dot_4097816011": { + "string": "No channels found" + }, "src_dot_discounts_dot_components_dot_VoucherValue_dot_4189095909": { "string": "If this option is disabled, discount will be counted for every eligible product" }, + "src_dot_discounts_dot_components_dot_VoucherValue_dot_507892781": { + "context": "column title", + "string": "Channel name" + }, "src_dot_discounts_dot_order": { "context": "voucher discount", "string": "Entire order" @@ -2588,70 +2784,50 @@ "context": "voucher discount", "string": "Shipment" }, - "src_dot_discounts_dot_views_dot_1162257691": { - "string": "Deleted voucher" + "src_dot_discounts_dot_views_dot_SaleCreate_dot_3707049729": { + "string": "Successfully created sale" }, - "src_dot_discounts_dot_views_dot_1402402714": { - "context": "dialog header", - "string": "Unassign Collections From Voucher" + "src_dot_discounts_dot_views_dot_SaleCreate_dot_480188715": { + "string": "Manage Sales Channel Availability" }, - "src_dot_discounts_dot_views_dot_1457489953": { + "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_1827854264": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_1827854264": { "context": "dialog header", "string": "Unassign Categories From Sale" }, - "src_dot_discounts_dot_views_dot_1952217501": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_1952217501": { "context": "dialog header", "string": "Unassign Collections From Sale" }, - "src_dot_discounts_dot_views_dot_2072403265": { - "context": "dialog header", - "string": "Unassign Products From Voucher" - }, - "src_dot_discounts_dot_views_dot_2353723060": { + "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_2534378844": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_2534378844": { "string": "Removed sale" }, - "src_dot_discounts_dot_views_dot_2669520431": { - "context": "dialog header", - "string": "Unassign Categories From Voucher" - }, - "src_dot_discounts_dot_views_dot_3215481647": { + "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_3261917848": { - "context": "dialog content", - "string": "Are you sure you want to delete {voucherCode}?" - }, - "src_dot_discounts_dot_views_dot_3395246518": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_3395246518": { "context": "dialog header", "string": "Unassign Products From Sale" }, - "src_dot_discounts_dot_views_dot_3707049729": { - "string": "Successfully created sale" + "src_dot_discounts_dot_views_dot_SaleDetails_dot_3823295269": { + "string": "Manage Channel Availability" }, - "src_dot_discounts_dot_views_dot_506030254": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_506030254": { "context": "dialog header", "string": "Delete Sale" }, - "src_dot_discounts_dot_views_dot_655651329": { - "string": "Successfully created voucher" - }, - "src_dot_discounts_dot_views_dot_767268203": { + "src_dot_discounts_dot_views_dot_SaleDetails_dot_767268203": { "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_895379508": { - "context": "dialog header", - "string": "Delete Voucher" - }, "src_dot_discounts_dot_views_dot_SaleList_dot_2809303671": { "context": "dialog header", "string": "Delete Sales" @@ -2660,6 +2836,50 @@ "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this sale?} other{Are you sure you want to delete {displayQuantity} sales?}}" }, + "src_dot_discounts_dot_views_dot_VoucherCreate_dot_3423943948": { + "string": "Manage Products Channel Availability" + }, + "src_dot_discounts_dot_views_dot_VoucherCreate_dot_655651329": { + "string": "Successfully created voucher" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_dot_1162257691": { + "string": "Deleted voucher" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_dot_1402402714": { + "context": "dialog header", + "string": "Unassign Collections From Voucher" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_dot_2072403265": { + "context": "dialog header", + "string": "Unassign Products From Voucher" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_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_VoucherDetails_dot_2669520431": { + "context": "dialog header", + "string": "Unassign Categories From Voucher" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_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_VoucherDetails_dot_3261917848": { + "context": "dialog content", + "string": "Are you sure you want to delete {voucherCode}?" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_dot_3823295269": { + "string": "Manage Channel Availability" + }, + "src_dot_discounts_dot_views_dot_VoucherDetails_dot_767268203": { + "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_VoucherDetails_dot_895379508": { + "context": "dialog header", + "string": "Delete Voucher" + }, "src_dot_discounts_dot_views_dot_VoucherList_dot_2983742638": { "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this voucher?} other{Are you sure you want to delete {displayQuantity} vouchers?}}" @@ -2696,6 +2916,10 @@ "src_dot_endHour": { "string": "End Hour" }, + "src_dot_exchangeRates": { + "context": "Manage and Update your warehouse information", + "string": "Exchange Rates" + }, "src_dot_firstName": { "string": "First Name" }, @@ -2722,6 +2946,9 @@ "src_dot_home_dot_components_dot_HomeActivityCard_dot_placed": { "string": "Order #{orderId} was placed" }, + "src_dot_home_dot_components_dot_HomeHeader_dot_4019698341": { + "string": "Channel" + }, "src_dot_hooks_dot_3382262667": { "string": "Variant {name} has been set as default." }, @@ -2755,6 +2982,10 @@ "context": "orders section name", "string": "Orders" }, + "src_dot_orders_dot_components_dot_DraftOrderChannelSectionCard_dot_1243773440": { + "context": "section header", + "string": "Sales channel" + }, "src_dot_orders_dot_components_dot_OrderAddressEditDialog_dot_3278396777": { "context": "dialog header", "string": "Edit Shipping Address" @@ -2780,6 +3011,10 @@ "src_dot_orders_dot_components_dot_OrderCannotCancelOrderDialog_dot_775268031": { "string": "There are still fulfillments created for this order. Cancel the fulfillments first before you cancel the order." }, + "src_dot_orders_dot_components_dot_OrderChannelSectionCard_dot_1243773440": { + "context": "section header", + "string": "Sales channel" + }, "src_dot_orders_dot_components_dot_OrderCustomerNote_dot_1505053535": { "string": "No notes from customer" }, @@ -2833,22 +3068,13 @@ "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_1895667608": { "string": "Product" }, - "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_270371448": { - "string": "Product is out of stock" - }, "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_2796503714": { "context": "quantity of ordered products", "string": "Quantity" }, - "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_2963336581": { - "string": "Product is unavailable to purchase" - }, "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_546865199": { "string": "No Products added to Order" }, - "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_59548500": { - "string": "Product is hidden" - }, "src_dot_orders_dot_components_dot_OrderDraftDetailsProducts_dot_878013594": { "context": "total price of ordered products", "string": "Total" @@ -2879,6 +3105,10 @@ "context": "button", "string": "Add products" }, + "src_dot_orders_dot_components_dot_OrderDraftListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_orders_dot_components_dot_OrderDraftListPage_dot_2826235371": { "context": "button", "string": "Create order" @@ -3203,6 +3433,10 @@ "context": "generate invoice button", "string": "Generate" }, + "src_dot_orders_dot_components_dot_OrderListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_orders_dot_components_dot_OrderListPage_dot_2826235371": { "context": "button", "string": "Create order" @@ -4016,10 +4250,6 @@ "context": "product organization, header", "string": "Organization" }, - "src_dot_products_dot_components_dot_ProductCreatePage_dot_2232321263": { - "context": "product publication date label", - "string": "will become published on {date}" - }, "src_dot_products_dot_components_dot_ProductCreatePage_dot_2341910657": { "context": "product label", "string": "Not published" @@ -4047,6 +4277,9 @@ "context": "there are more elements of list that are hidden", "string": "and {number} more" }, + "src_dot_products_dot_components_dot_ProductExportDialog_dot_1583816707": { + "string": "Add all channels" + }, "src_dot_products_dot_components_dot_ProductExportDialog_dot_1890035856": { "context": "informations about product organization, header", "string": "Product Organization" @@ -4157,6 +4390,10 @@ "context": "product price", "string": "Price" }, + "src_dot_products_dot_components_dot_ProductListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_products_dot_components_dot_ProductListPage_dot_1542417144": { "context": "button", "string": "Create Product" @@ -4172,10 +4409,6 @@ "src_dot_products_dot_components_dot_ProductListPage_dot_3550330425": { "string": "Search Products..." }, - "src_dot_products_dot_components_dot_ProductListPage_dot_3640454975": { - "context": "product status", - "string": "Published" - }, "src_dot_products_dot_components_dot_ProductListPage_dot_821159718": { "context": "tab name", "string": "All Products" @@ -4222,17 +4455,13 @@ "context": "product type", "string": "Type" }, - "src_dot_products_dot_components_dot_ProductList_dot_2341910657": { - "context": "product status", - "string": "Not published" - }, "src_dot_products_dot_components_dot_ProductList_dot_2754779425": { "context": "product type", "string": "Configurable" }, - "src_dot_products_dot_components_dot_ProductList_dot_3640454975": { - "context": "product status", - "string": "Published" + "src_dot_products_dot_components_dot_ProductList_dot_3326160357": { + "context": "product channels", + "string": "Availability" }, "src_dot_products_dot_components_dot_ProductList_dot_636461959": { "context": "product", @@ -4263,14 +4492,6 @@ "src_dot_products_dot_components_dot_ProductOrganization_dot_4257289053": { "string": "Product Type" }, - "src_dot_products_dot_components_dot_ProductPricing_dot_1099355007": { - "context": "product pricing", - "string": "Pricing" - }, - "src_dot_products_dot_components_dot_ProductPricing_dot_1134347598": { - "context": "product price", - "string": "Price" - }, "src_dot_products_dot_components_dot_ProductShipping_dot_1325966144": { "context": "product shipping", "string": "Shipping" @@ -4317,10 +4538,6 @@ "context": "checkbox", "string": "Override the product type's tax rate" }, - "src_dot_products_dot_components_dot_ProductUpdatePage_dot_2232321263": { - "context": "product publication date label", - "string": "will become published on {date}" - }, "src_dot_products_dot_components_dot_ProductUpdatePage_dot_2341910657": { "context": "product label", "string": "Not published" @@ -4341,7 +4558,7 @@ "string": "Delete Variant" }, "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1134347598": { - "context": "variant price", + "context": "variant price, header", "string": "Price" }, "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1346828628": { @@ -4367,6 +4584,10 @@ "context": "product attribute values, page title", "string": "Choose Values" }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2330516954": { + "context": "variant channel price", + "string": "{channel} Price" + }, "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2478977538": { "context": "attribute values, variant creation step", "string": "Select Values" @@ -4382,6 +4603,9 @@ "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2783195765": { "string": "Apply single price to all SKUs" }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2877822536": { + "string": "Skip pricing for now" + }, "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3387090508": { "string": "Apply unique stock by attribute to each SKU" }, @@ -4480,8 +4704,24 @@ "src_dot_products_dot_components_dot_ProductVariantPrice_dot_1134347598": { "string": "Price" }, - "src_dot_products_dot_components_dot_ProductVariantPrice_dot_819659341": { - "string": "Cost price" + "src_dot_products_dot_components_dot_ProductVariantPrice_dot_1387754199": { + "context": "tabel column header", + "string": "Selling Price" + }, + "src_dot_products_dot_components_dot_ProductVariantPrice_dot_2051669917": { + "context": "tabel column header", + "string": "Cost Price" + }, + "src_dot_products_dot_components_dot_ProductVariantPrice_dot_3211447524": { + "context": "info text", + "string": "Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency" + }, + "src_dot_products_dot_components_dot_ProductVariantPrice_dot_3511613983": { + "context": "tabel column header", + "string": "Channel Name" + }, + "src_dot_products_dot_components_dot_ProductVariantPrice_dot_4097816011": { + "string": "No channels found" }, "src_dot_products_dot_components_dot_ProductVariantSetDefault_dot_3683859003": { "context": "set variant as default, button", @@ -4549,14 +4789,6 @@ "context": "product variant name", "string": "Variant" }, - "src_dot_products_dot_views_dot_1542417144": { - "context": "window title", - "string": "Create Product" - }, - "src_dot_products_dot_views_dot_1591632382": { - "context": "page header", - "string": "New Product" - }, "src_dot_products_dot_views_dot_1731766393": { "context": "dialog header", "string": "Delete Image" @@ -4571,13 +4803,24 @@ "src_dot_products_dot_views_dot_2880386427": { "string": "Are you sure you want to delete this image?" }, - "src_dot_products_dot_views_dot_2899821092": { - "string": "Product created" - }, "src_dot_products_dot_views_dot_3989383405": { "context": "window title", "string": "Create variant" }, + "src_dot_products_dot_views_dot_ProductCreate_dot_1542417144": { + "context": "window title", + "string": "Create Product" + }, + "src_dot_products_dot_views_dot_ProductCreate_dot_1591632382": { + "context": "page header", + "string": "New Product" + }, + "src_dot_products_dot_views_dot_ProductCreate_dot_2899821092": { + "string": "Product created" + }, + "src_dot_products_dot_views_dot_ProductCreate_dot_3423943948": { + "string": "Manage Products Channel Availability" + }, "src_dot_products_dot_views_dot_ProductList_dot_1204353135": { "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this product?} other{Are you sure you want to delete {displayQuantity} products?}}" @@ -4586,30 +4829,6 @@ "context": "waiting for export to end, header", "string": "Exporting CSV" }, - "src_dot_products_dot_views_dot_ProductList_dot_1547167026": { - "context": "publish product, button", - "string": "Publish" - }, - "src_dot_products_dot_views_dot_ProductList_dot_2036051956": { - "context": "dialog content", - "string": "{counter,plural,one{Are you sure you want to unpublish this product?} other{Are you sure you want to unpublish {displayQuantity} products?}}" - }, - "src_dot_products_dot_views_dot_ProductList_dot_2237014112": { - "context": "unpublish product, button", - "string": "Unpublish" - }, - "src_dot_products_dot_views_dot_ProductList_dot_2946646245": { - "context": "dialog header", - "string": "Publish Products" - }, - "src_dot_products_dot_views_dot_ProductList_dot_3362608461": { - "context": "dialog header", - "string": "Unpublish Products" - }, - "src_dot_products_dot_views_dot_ProductList_dot_3518737068": { - "context": "dialog content", - "string": "{counter,plural,one{Are you sure you want to publish this product?} other{Are you sure you want to publish {displayQuantity} products?}}" - }, "src_dot_products_dot_views_dot_ProductList_dot_400629795": { "context": "dialog header", "string": "Delete Products" @@ -4629,13 +4848,12 @@ "context": "delete product", "string": "Are you sure you want to delete {name}?" }, + "src_dot_products_dot_views_dot_ProductUpdate_dot_3423943948": { + "string": "Manage Products Channel Availability" + }, "src_dot_products_dot_views_dot_ProductUpdate_dot_4108890645": { "string": "Product removed" }, - "src_dot_products_dot_views_dot_ProductUpdate_dot_671489548": { - "context": "snackbar text", - "string": "Product availability updated" - }, "src_dot_products_dot_views_dot_ProductUpdate_dot_879305849": { "context": "dialog header", "string": "Delete Product" @@ -4703,6 +4921,78 @@ "context": "shipping section name", "string": "Shipping Methods" }, + "src_dot_shipping_dot_components_dot_DeleteShippingRateDialog_dot_1502359905": { + "context": "dialog header", + "string": "Delete Shipping Method" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_1063111824": { + "context": "max price in channel", + "string": "Max. value" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_1449127001": { + "string": "Min Value" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_1615342575": { + "context": "channels discount info", + "string": "Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_2946165345": { + "context": "card title", + "string": "Order value" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_4226393146": { + "context": "price rates info", + "string": "This rate will apply to all orders of all prices" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_507892781": { + "context": "channel name", + "string": "Channel name" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_58751738": { + "string": "Max Value" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_666501915": { + "context": "min price in channel", + "string": "Min. value" + }, + "src_dot_shipping_dot_components_dot_OrderValue_dot_882649212": { + "context": "checkbox label", + "string": "There are no value limits" + }, + "src_dot_shipping_dot_components_dot_OrderWeight_dot_1064401942": { + "context": "info text", + "string": "This rate will apply to all orders" + }, + "src_dot_shipping_dot_components_dot_OrderWeight_dot_2935375344": { + "string": "Min. Order Weight" + }, + "src_dot_shipping_dot_components_dot_OrderWeight_dot_3721863048": { + "context": "card title", + "string": "Order weight" + }, + "src_dot_shipping_dot_components_dot_OrderWeight_dot_882649212": { + "context": "checkbox label", + "string": "There are no value limits" + }, + "src_dot_shipping_dot_components_dot_OrderWeight_dot_959089677": { + "string": "Max. Order Weight" + }, + "src_dot_shipping_dot_components_dot_PricingCard_dot_1099355007": { + "context": "pricing card title", + "string": "Pricing" + }, + "src_dot_shipping_dot_components_dot_PricingCard_dot_1134347598": { + "context": "column title", + "string": "Price" + }, + "src_dot_shipping_dot_components_dot_PricingCard_dot_3211447524": { + "context": "info text", + "string": "Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency" + }, + "src_dot_shipping_dot_components_dot_PricingCard_dot_507892781": { + "context": "column title", + "string": "Channel name" + }, "src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_2863708228": { "string": "This unit will be used as default shipping weight" }, @@ -4774,86 +5064,16 @@ "src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_1109610983": { "string": "Shipping Zone Name" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1337705349": { - "string": "Minimal Order Value" + "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_1161979494": { + "context": "page title", + "string": "Price Rate Create" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1388947267": { - "string": "This rate will apply to all orders of all weights" + "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_1325966144": { + "string": "Shipping" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1397795758": { - "context": "add weight based shipping method, dialog header", - "string": "Add Weight Rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1403365734": { - "context": "shipping method price", - "string": "Rate Price" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1486599614": { - "string": "This will be shown to customers at checkout" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1542600502": { - "context": "button", - "string": "Update rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_16061680": { - "context": "button", - "string": "Create rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_1878009504": { - "string": "Minimal Order Weight" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_2215090771": { - "context": "shipping method, switch button", - "string": "This is free shipping" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_2324036635": { - "context": "order weight range", - "string": "Weight range" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_2533614652": { - "context": "order price range", - "string": "Value range" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_2892088870": { - "context": "dialog header", - "string": "Add Price Rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_3213611593": { - "context": "shipping method", - "string": "Rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_3463567334": { - "string": "Maximal Order Value" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_3659741519": { - "string": "Maximal Order Weight" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_382595300": { - "context": "shipping method name", - "string": "Rate Name" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_3934114933": { - "context": "dialog header", - "string": "Edit Price Rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_4152709923": { - "context": "edit weight based shipping method, dialog header", - "string": "Edit Weight Rate" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_4226393146": { - "string": "This rate will apply to all orders of all prices" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_882649212": { - "context": "shipping method has no value limits", - "string": "There are no value limits" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_price": { - "context": "error message", - "string": "Maximum price cannot be lower than minimum" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_weight": { - "context": "error message", - "string": "Maximum weight cannot be lower than minimum" + "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_3538551526": { + "context": "page title", + "string": "Weight Rate Create" }, "src_dot_shipping_dot_components_dot_ShippingZoneRates_dot_1134347598": { "context": "shipping method price", @@ -4905,6 +5125,10 @@ "context": "header", "string": "Shipping" }, + "src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1391686013": { + "context": "button", + "string": "Settings" + }, "src_dot_shipping_dot_components_dot_ShippingZonesList_dot_120574110": { "context": "sort shipping methods by zone, section header", "string": "Shipping By Zone" @@ -4923,6 +5147,10 @@ "src_dot_shipping_dot_components_dot_ShippingZonesList_dot_655374584": { "string": "No shipping zones found" }, + "src_dot_shipping_dot_price": { + "context": "error message", + "string": "Maximum price cannot be lower than minimum" + }, "src_dot_shipping_dot_views_dot_1005071028": { "string": "Are you sure you want to delete {shippingZoneName} shipping zone?" }, @@ -4938,6 +5166,12 @@ "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this shipping zone?} other{Are you sure you want to delete {displayQuantity} shipping zones?}}" }, + "src_dot_shipping_dot_views_dot_PriceRatesCreate_dot_3823295269": { + "string": "Manage Channel Availability" + }, + "src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_3823295269": { + "string": "Manage Channel Availability" + }, "src_dot_shipping_dot_views_dot_ShippingZoneDetails_dot_1010705153": { "context": "dialog header", "string": "Delete Shipping Zone" @@ -4946,14 +5180,20 @@ "context": "unassign country", "string": "Are you sure you want to delete {countryName} from this shipping zone?" }, - "src_dot_shipping_dot_views_dot_ShippingZoneDetails_dot_1502359905": { - "context": "dialog header", - "string": "Delete Shipping Method" - }, "src_dot_shipping_dot_views_dot_ShippingZoneDetails_dot_3133838427": { "context": "unassign country, dialog header", "string": "Delete from Shipping Zone" }, + "src_dot_shipping_dot_views_dot_WeightRatesCreate_dot_3014453080": { + "string": "Manage Channels Availability" + }, + "src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_3014453080": { + "string": "Manage Channels Availability" + }, + "src_dot_shipping_dot_weight": { + "context": "error message", + "string": "Maximum weight cannot be lower than minimum" + }, "src_dot_show": { "context": "button", "string": "Show" @@ -5421,15 +5661,6 @@ "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_1163855804": { "string": "Languages" }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_1866913828": { - "string": "Search Sale" - }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_1930485532": { - "string": "Search Voucher" - }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_2105464697": { - "string": "Search Product" - }, "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_222873645": { "string": "Collections" }, @@ -5437,26 +5668,17 @@ "context": "header", "string": "Translations to {language}" }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_2559018090": { - "string": "Search Page" - }, "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_2968663655": { "string": "Products" }, "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_3287512325": { "string": "Pages" }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_3420445375": { - "string": "Search Product Type" - }, "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_3583204912": { "string": "Categories" }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_3841025483": { - "string": "Search Category" - }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_4057224233": { - "string": "Search Collection" + "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_4153345096": { + "string": "Attributes" }, "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_487083593": { "string": "Sales" @@ -5464,9 +5686,6 @@ "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_749185240": { "string": "Vouchers" }, - "src_dot_translations_dot_components_dot_TranslationsEntitiesListPage_dot_765385638": { - "string": "Product Types" - }, "src_dot_translations_dot_components_dot_TranslationsEntitiesList_dot_1136143456": { "context": "translation progress", "string": "{current} of {max}" @@ -5510,24 +5729,16 @@ "src_dot_translations_dot_components_dot_TranslationsPagesPage_dot_432157284": { "string": "Page Title" }, - "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_1281101905": { - "context": "header", - "string": "Translation Product Type \"{productTypeName}\" - {languageCode}" - }, "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_1567737068": { "context": "attribute values", "string": "Value {number}" }, - "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_2510190956": { - "context": "header", - "string": "Product Attribute ({attributeName})" - }, "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_2642976392": { "string": "Attribute Name" }, - "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_3538502409": { + "src_dot_translations_dot_components_dot_TranslationsProductTypesPage_dot_884093025": { "context": "header", - "string": "Variant Attribute ({attributeName})" + "string": "Translation Attribute \"{attribute}\" - {languageCode}" }, "src_dot_translations_dot_components_dot_TranslationsProductsPage_dot_1406947243": { "string": "Search Engine Description" @@ -5629,6 +5840,18 @@ "context": "error message", "string": "Only pre-authorized payments can be captured" }, + "src_dot_utils_dot_errors_dot_channelAlreadyExist": { + "string": "This channel has already been created" + }, + "src_dot_utils_dot_errors_dot_channelSameCurrency": { + "string": "Currency in both channels must be the same" + }, + "src_dot_utils_dot_errors_dot_channelUnique": { + "string": "Slug must be unique" + }, + "src_dot_utils_dot_errors_dot_duplicated": { + "string": "The same object cannot be in both lists" + }, "src_dot_utils_dot_errors_dot_duplicatedInputItem": { "string": "Cannot add and remove group the same time" }, @@ -5658,6 +5881,10 @@ "src_dot_utils_dot_errors_dot_invalidUrlFormat": { "string": "Url has invalid format" }, + "src_dot_utils_dot_errors_dot_lessThanMin": { + "context": "error message", + "string": "Max value cannot be less than min value" + }, "src_dot_utils_dot_errors_dot_misconfigured": { "string": "Plugin is misconfigured and cannot be activated" }, @@ -5706,6 +5933,9 @@ "src_dot_utils_dot_errors_dot_permissionOutOfScope": { "string": "Those permissions are out of your scope" }, + "src_dot_utils_dot_errors_dot_priceInvalid": { + "string": "Product price cannot be lower than 0." + }, "src_dot_utils_dot_errors_dot_shippingNotApplicable": { "context": "error message", "string": "Shipping method is not valid for chosen shipping address" diff --git a/package-lock.json b/package-lock.json index 99cf8bf48..a0b5a68f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9496,6 +9496,15 @@ "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", "dev": true }, + "currency-codes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/currency-codes/-/currency-codes-2.1.0.tgz", + "integrity": "sha512-aASwFNP8VjZ0y0PWlSW7c9N/isYTLxK6OCbm7aVuQMk7dWO2zgup9KGiFQgeL9OGL5P/ulvCHcjQizmuEeZXtw==", + "requires": { + "first-match": "~0.0.1", + "nub": "~0.0.0" + } + }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -12260,6 +12269,11 @@ "resolved": "https://registry.npmjs.org/find-with-regex/-/find-with-regex-1.1.3.tgz", "integrity": "sha512-zkEVQ1H3PIQL/19ADKt1lCQU4QGM3OneiderUcFgn5EgTm/TnoUh7HxPAwP8w/vXxWSLC6KtpbDQpypJ5+majw==" }, + "first-match": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/first-match/-/first-match-0.0.1.tgz", + "integrity": "sha1-pg7GQnAPD0NyNOu37D84JHblQv0=" + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -12566,8 +12580,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -12585,13 +12598,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -12604,18 +12615,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -12718,8 +12726,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -12729,7 +12736,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -12742,20 +12748,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -12772,7 +12775,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -12845,8 +12847,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -12856,7 +12857,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -12932,8 +12932,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -12963,7 +12962,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -12981,7 +12979,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -13020,13 +13017,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -17570,6 +17565,11 @@ "boolbase": "~1.0.0" } }, + "nub": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/nub/-/nub-0.0.0.tgz", + "integrity": "sha1-s2m9Mr3eZq9ZYFw7BSC8IZ3MwE8=" + }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", diff --git a/package.json b/package.json index f9f472a16..7a7279721 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "apollo-upload-client": "^9.1.0", "classnames": "^2.2.6", "crc-32": "^1.2.0", + "currency-codes": "^2.1.0", "downshift": "^1.31.16", "draft-js": "^0.10.5", "draftail": "^1.2.1", diff --git a/schema.graphql b/schema.graphql index 10b947e2e..7daa390b2 100644 --- a/schema.graphql +++ b/schema.graphql @@ -469,6 +469,7 @@ input AttributeFilterInput { ids: [ID] inCollection: ID inCategory: ID + channel: String } input AttributeInput { @@ -662,6 +663,7 @@ type BulkProductError { attributes: [ID!] index: Int warehouses: [ID!] + channels: [ID!] } type BulkStockError { @@ -691,7 +693,7 @@ type Category implements Node & ObjectWithMetadata { privateMetadata: [MetadataItem]! metadata: [MetadataItem]! ancestors(before: String, after: String, first: Int, last: Int): CategoryCountableConnection - products(before: String, after: String, first: Int, last: Int): ProductCountableConnection + products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection url: String @deprecated(reason: "This field will be removed after 2020-07-31.") children(before: String, after: String, first: Int, last: Int): CategoryCountableConnection backgroundImage(size: Int): Image @@ -750,6 +752,7 @@ enum CategorySortField { input CategorySortingInput { direction: OrderDirection! + channel: String field: CategorySortField! } @@ -786,14 +789,86 @@ type CategoryUpdate { category: Category } +type Channel implements Node { + id: ID! + name: String! + isActive: Boolean! + slug: String! + currencyCode: String! +} + +type ChannelActivate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + channel: Channel + channelErrors: [ChannelError!]! +} + +type ChannelCreate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + channelErrors: [ChannelError!]! + channel: Channel +} + +input ChannelCreateInput { + isActive: Boolean + name: String! + slug: String! + currencyCode: String! +} + +type ChannelDeactivate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + channel: Channel + channelErrors: [ChannelError!]! +} + +type ChannelDelete { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + channelErrors: [ChannelError!]! + channel: Channel +} + +input ChannelDeleteInput { + targetChannel: ID! +} + +type ChannelError { + field: String + message: String + code: ChannelErrorCode! +} + +enum ChannelErrorCode { + ALREADY_EXISTS + GRAPHQL_ERROR + INVALID + NOT_FOUND + REQUIRED + UNIQUE + CHANNEL_TARGET_ID_MUST_BE_DIFFERENT + CHANNELS_CURRENCY_MUST_BE_THE_SAME +} + +type ChannelUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + channelErrors: [ChannelError!]! + channel: Channel +} + +input ChannelUpdateInput { + isActive: Boolean + name: String + slug: String +} + type Checkout implements Node & ObjectWithMetadata { created: DateTime! lastChange: DateTime! user: User quantity: Int! + channel: Channel! billingAddress: Address shippingAddress: Address - shippingMethod: ShippingMethod note: String! discount: Money discountName: String @@ -809,6 +884,7 @@ type Checkout implements Node & ObjectWithMetadata { isShippingRequired: Boolean! lines: [CheckoutLine] shippingPrice: TaxedMoney + shippingMethod: ShippingMethod subtotalPrice: TaxedMoney token: UUID! totalPrice: TaxedMoney @@ -853,6 +929,7 @@ type CheckoutCreate { } input CheckoutCreateInput { + channel: String lines: [CheckoutLineInput]! email: String shippingAddress: AddressInput @@ -905,6 +982,8 @@ enum CheckoutErrorCode { UNIQUE VOUCHER_NOT_APPLICABLE ZERO_QUANTITY + MISSING_CHANNEL_SLUG + CHANNEL_INACTIVE } type CheckoutLine implements Node { @@ -986,20 +1065,19 @@ type Collection implements Node & ObjectWithMetadata { name: String! description: String! descriptionJson: JSONString! - publicationDate: Date - isPublished: Boolean! slug: String! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! products(filter: ProductFilterInput, sortBy: ProductOrder, before: String, after: String, first: Int, last: Int): ProductCountableConnection backgroundImage(size: Int): Image translation(languageCode: LanguageCodeEnum!): CollectionTranslation + channelListings: [CollectionChannelListing!] } type CollectionAddProducts { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! } type CollectionBulkDelete { @@ -1008,10 +1086,30 @@ type CollectionBulkDelete { productErrors: [ProductError!]! } -type CollectionBulkPublish { +type CollectionChannelListing implements Node { + publicationDate: Date + isPublished: Boolean! + id: ID! + channel: Channel! +} + +type CollectionChannelListingError { + field: String + message: String + code: ProductErrorCode! + attributes: [ID!] + channels: [ID!] +} + +type CollectionChannelListingUpdate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - count: Int! - productErrors: [ProductError!]! + collection: Collection + collectionChannelListingErrors: [CollectionChannelListingError!]! +} + +input CollectionChannelListingUpdateInput { + addChannels: [PublishableChannelListingInput!] + removeChannels: [ID!] } type CollectionCountableConnection { @@ -1027,7 +1125,7 @@ type CollectionCountableEdge { type CollectionCreate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! collection: Collection } @@ -1046,14 +1144,32 @@ input CollectionCreateInput { type CollectionDelete { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! collection: Collection } +type CollectionError { + field: String + message: String + products: [ID!] + code: CollectionErrorCode! +} + +enum CollectionErrorCode { + DUPLICATED_INPUT_ITEM + GRAPHQL_ERROR + INVALID + NOT_FOUND + REQUIRED + UNIQUE + CANNOT_MANAGE_PRODUCT_WITHOUT_VARIANT +} + input CollectionFilterInput { published: CollectionPublished search: String ids: [ID] + channel: String } input CollectionInput { @@ -1076,13 +1192,13 @@ enum CollectionPublished { type CollectionRemoveProducts { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! } type CollectionReorderProducts { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! } enum CollectionSortField { @@ -1094,6 +1210,7 @@ enum CollectionSortField { input CollectionSortingInput { direction: OrderDirection! + channel: String field: CollectionSortField! } @@ -1126,7 +1243,7 @@ type CollectionTranslation implements Node { type CollectionUpdate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - productErrors: [ProductError!]! + collectionErrors: [CollectionError!]! collection: Collection } @@ -1147,8 +1264,8 @@ enum ConfigurationTypeFieldEnum { STRING BOOLEAN SECRET - SECRETMULTILINE PASSWORD + SECRETMULTILINE } type ConfirmAccount { @@ -1486,7 +1603,6 @@ enum CustomerEventsEnum { input CustomerFilterInput { dateJoined: DateRangeInput - moneySpent: PriceRangeInput numberOfOrders: IntRangeInput placedOrders: DateRangeInput search: String @@ -1620,7 +1736,9 @@ input DigitalContentUrlCreateInput { type DiscountError { field: String message: String + products: [ID!] code: DiscountErrorCode! + channels: [ID!] } enum DiscountErrorCode { @@ -1630,6 +1748,8 @@ enum DiscountErrorCode { NOT_FOUND REQUIRED UNIQUE + CANNOT_MANAGE_PRODUCT_WITHOUT_VARIANT + DUPLICATED_INPUT_ITEM } enum DiscountStatusEnum { @@ -1676,6 +1796,7 @@ input DraftOrderCreateInput { shippingMethod: ID voucher: ID customerNote: String + channel: ID lines: [OrderLineCreateInput] } @@ -1694,6 +1815,7 @@ input DraftOrderInput { shippingMethod: ID voucher: ID customerNote: String + channel: ID } type DraftOrderLineDelete { @@ -1809,6 +1931,7 @@ input ExportFileSortingInput { input ExportInfoInput { attributes: [ID!] warehouses: [ID!] + channels: [ID!] fields: [ProductFieldEnum!] } @@ -1837,6 +1960,12 @@ enum FileTypesEnum { XLSX } +type FileUpload { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + uploadedFile: UploadedFile + uploadErrors: [UploadError!]! +} + type Fulfillment implements Node & ObjectWithMetadata { id: ID! fulfillmentOrder: Int! @@ -1993,12 +2122,6 @@ type GroupCountableEdge { cursor: String! } -type HomepageCollectionUpdate { - errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - shop: Shop - shopErrors: [ShopError!]! -} - type Image { url: String! alt: String @@ -2423,12 +2546,12 @@ type Mutation { staffNotificationRecipientCreate(input: StaffNotificationRecipientInput!): StaffNotificationRecipientCreate staffNotificationRecipientUpdate(id: ID!, input: StaffNotificationRecipientInput!): StaffNotificationRecipientUpdate staffNotificationRecipientDelete(id: ID!): StaffNotificationRecipientDelete - homepageCollectionUpdate(collection: ID): HomepageCollectionUpdate shopDomainUpdate(input: SiteDomainInput): ShopDomainUpdate shopSettingsUpdate(input: ShopSettingsInput!): ShopSettingsUpdate shopFetchTaxRates: ShopFetchTaxRates shopSettingsTranslate(input: ShopSettingsTranslationInput!, languageCode: LanguageCodeEnum!): ShopSettingsTranslate shopAddressUpdate(input: AddressInput): ShopAddressUpdate + shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate shippingPriceDelete(id: ID!): ShippingPriceDelete shippingPriceBulkDelete(ids: [ID]!): ShippingPriceBulkDelete @@ -2461,17 +2584,16 @@ type Mutation { collectionDelete(id: ID!): CollectionDelete collectionReorderProducts(collectionId: ID!, moves: [MoveProductInput]!): CollectionReorderProducts collectionBulkDelete(ids: [ID]!): CollectionBulkDelete - collectionBulkPublish(ids: [ID]!, isPublished: Boolean!): CollectionBulkPublish collectionRemoveProducts(collectionId: ID!, products: [ID]!): CollectionRemoveProducts collectionUpdate(id: ID!, input: CollectionInput!): CollectionUpdate collectionTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): CollectionTranslate + collectionChannelListingUpdate(id: ID!, input: CollectionChannelListingUpdateInput!): CollectionChannelListingUpdate productCreate(input: ProductCreateInput!): ProductCreate productDelete(id: ID!): ProductDelete productBulkDelete(ids: [ID]!): ProductBulkDelete - productBulkPublish(ids: [ID]!, isPublished: Boolean!): ProductBulkPublish productUpdate(id: ID!, input: ProductInput!): ProductUpdate productTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): ProductTranslate - productSetAvailabilityForPurchase(isAvailable: Boolean!, productId: ID!, startDate: Date): ProductSetAvailabilityForPurchase + productChannelListingUpdate(id: ID!, input: ProductChannelListingUpdateInput!): ProductChannelListingUpdate productImageCreate(input: ProductImageCreateInput!): ProductImageCreate productVariantReorder(moves: [ReorderInput]!, productId: ID!): ProductVariantReorder productImageDelete(id: ID!): ProductImageDelete @@ -2497,11 +2619,13 @@ type Mutation { productVariantUpdate(id: ID!, input: ProductVariantInput!): ProductVariantUpdate productVariantSetDefault(productId: ID!, variantId: ID!): ProductVariantSetDefault productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate + productVariantChannelListingUpdate(id: ID!, input: [ProductVariantChannelListingAddInput!]!): ProductVariantChannelListingUpdate variantImageAssign(imageId: ID!, variantId: ID!): VariantImageAssign variantImageUnassign(imageId: ID!, variantId: ID!): VariantImageUnassign paymentCapture(amount: PositiveDecimal, paymentId: ID!): PaymentCapture paymentRefund(amount: PositiveDecimal, paymentId: ID!): PaymentRefund paymentVoid(paymentId: ID!): PaymentVoid + paymentInitialize(gateway: String!, paymentData: JSONString): PaymentInitialize pageCreate(input: PageInput!): PageCreate pageDelete(id: ID!): PageDelete pageBulkDelete(ids: [ID]!): PageBulkDelete @@ -2562,6 +2686,7 @@ type Mutation { saleCataloguesAdd(id: ID!, input: CatalogueInput!): SaleAddCatalogues saleCataloguesRemove(id: ID!, input: CatalogueInput!): SaleRemoveCatalogues saleTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): SaleTranslate + saleChannelListingUpdate(id: ID!, input: SaleChannelListingInput!): SaleChannelListingUpdate voucherCreate(input: VoucherInput!): VoucherCreate voucherDelete(id: ID!): VoucherDelete voucherBulkDelete(ids: [ID]!): VoucherBulkDelete @@ -2569,7 +2694,9 @@ type Mutation { voucherCataloguesAdd(id: ID!, input: CatalogueInput!): VoucherAddCatalogues voucherCataloguesRemove(id: ID!, input: CatalogueInput!): VoucherRemoveCatalogues voucherTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): VoucherTranslate + voucherChannelListingUpdate(id: ID!, input: VoucherChannelListingInput!): VoucherChannelListingUpdate exportProducts(input: ExportProductsInput!): ExportProducts + fileUpload(file: Upload!): FileUpload checkoutAddPromoCode(checkoutId: ID!, promoCode: String!): CheckoutAddPromoCode checkoutBillingAddressUpdate(billingAddress: AddressInput!, checkoutId: ID!): CheckoutBillingAddressUpdate checkoutComplete(checkoutId: ID!, paymentData: JSONString, redirectUrl: String, storeSource: Boolean = false): CheckoutComplete @@ -2584,6 +2711,11 @@ type Mutation { checkoutPaymentCreate(checkoutId: ID!, input: PaymentInput!): CheckoutPaymentCreate checkoutShippingAddressUpdate(checkoutId: ID!, shippingAddress: AddressInput!): CheckoutShippingAddressUpdate checkoutShippingMethodUpdate(checkoutId: ID, shippingMethodId: ID!): CheckoutShippingMethodUpdate + channelCreate(input: ChannelCreateInput!): ChannelCreate + channelUpdate(id: ID!, input: ChannelUpdateInput!): ChannelUpdate + channelDelete(id: ID!, input: ChannelDeleteInput!): ChannelDelete + channelActivate(id: ID!): ChannelActivate + channelDeactivate(id: ID!): ChannelDeactivate appCreate(input: AppInput!): AppCreate appUpdate(id: ID!, input: AppInput!): AppUpdate appDelete(id: ID!): AppDelete @@ -2668,6 +2800,7 @@ type Order implements Node & ObjectWithMetadata { shippingAddress: Address shippingMethod: ShippingMethod shippingMethodName: String + channel: Channel! shippingPrice: TaxedMoney token: String! voucher: Voucher @@ -2766,6 +2899,7 @@ type OrderError { code: OrderErrorCode! warehouse: ID orderLine: ID + variants: [ID!] } enum OrderErrorCode { @@ -2794,6 +2928,8 @@ enum OrderErrorCode { ZERO_QUANTITY INSUFFICIENT_STOCK DUPLICATED_INPUT_ITEM + NOT_AVAILABLE_IN_CHANNEL + CHANNEL_INACTIVE } type OrderEvent implements Node { @@ -2951,7 +3087,6 @@ enum OrderSortField { CUSTOMER PAYMENT FULFILLMENT_STATUS - TOTAL } input OrderSortingInput { @@ -3226,6 +3361,18 @@ type PaymentGateway { currencies: [String]! } +type PaymentInitialize { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + initializedPayment: PaymentInitialized + paymentErrors: [PaymentError!]! +} + +type PaymentInitialized { + gateway: String! + name: String! + data: JSONString +} + input PaymentInput { gateway: String! token: String @@ -3260,6 +3407,7 @@ enum PermissionEnum { MANAGE_USERS MANAGE_STAFF MANAGE_APPS + MANAGE_CHANNELS MANAGE_DISCOUNTS MANAGE_PLUGINS MANAGE_GIFT_CARD @@ -3412,33 +3560,29 @@ type Product implements Node & ObjectWithMetadata { name: String! description: String! descriptionJson: JSONString! - publicationDate: Date - isPublished: Boolean! productType: ProductType! slug: String! category: Category updatedAt: DateTime chargeTaxes: Boolean! weight: Weight - availableForPurchase: Date - visibleInListings: Boolean! defaultVariant: ProductVariant + rating: Float privateMetadata: [MetadataItem]! metadata: [MetadataItem]! url: String! @deprecated(reason: "This field will be removed after 2020-07-31.") thumbnail(size: Int): Image pricing: ProductPricingInfo isAvailable: Boolean - minimalVariantPrice: Money taxType: TaxType attributes: [SelectedAttribute!]! - purchaseCost: MoneyRange - margin: Margin + channelListings: [ProductChannelListing!] imageById(id: ID): ProductImage variants: [ProductVariant] images: [ProductImage] collections: [Collection] translation(languageCode: LanguageCodeEnum!): ProductTranslation + availableForPurchase: Date isAvailableForPurchase: Boolean } @@ -3448,10 +3592,45 @@ type ProductBulkDelete { productErrors: [ProductError!]! } -type ProductBulkPublish { +type ProductChannelListing implements Node { + id: ID! + publicationDate: Date + isPublished: Boolean! + channel: Channel! + visibleInListings: Boolean! + availableForPurchase: Date + discountedPrice: Money + purchaseCost: MoneyRange + margin: Margin + isAvailableForPurchase: Boolean +} + +input ProductChannelListingAddInput { + channelId: ID! + isPublished: Boolean + publicationDate: Date + visibleInListings: Boolean + isAvailableForPurchase: Boolean + availableForPurchaseDate: Date +} + +type ProductChannelListingError { + field: String + message: String + code: ProductErrorCode! + attributes: [ID!] + channels: [ID!] +} + +type ProductChannelListingUpdate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - count: Int! - productErrors: [ProductError!]! + product: Product + productChannelListingErrors: [ProductChannelListingError!]! +} + +input ProductChannelListingUpdateInput { + addChannels: [ProductChannelListingAddInput!] + removeChannels: [ID!] } type ProductCountableConnection { @@ -3473,24 +3652,18 @@ type ProductCreate { input ProductCreateInput { attributes: [AttributeValueInput] - publicationDate: Date category: ID chargeTaxes: Boolean collections: [ID] description: String descriptionJson: JSONString - isPublished: Boolean name: String slug: String taxCode: String seo: SeoInput weight: WeightScalar - sku: String - trackInventory: Boolean - basePrice: PositiveDecimal - visibleInListings: Boolean + rating: Float productType: ID! - stocks: [StockInput!] } type ProductDelete { @@ -3514,12 +3687,15 @@ enum ProductErrorCode { DUPLICATED_INPUT_ITEM GRAPHQL_ERROR INVALID + PRODUCT_WITHOUT_CATEGORY NOT_PRODUCTS_IMAGE NOT_PRODUCTS_VARIANT NOT_FOUND REQUIRED UNIQUE VARIANT_NO_DIGITAL_CONTENT + CANNOT_MANAGE_PRODUCT_WITHOUT_VARIANT + PRODUCT_NOT_ASSIGNED_TO_CHANNEL } enum ProductFieldEnum { @@ -3528,15 +3704,11 @@ enum ProductFieldEnum { PRODUCT_TYPE CATEGORY VISIBLE - AVAILABLE_FOR_PURCHASE - SEARCHABLE PRODUCT_WEIGHT COLLECTIONS CHARGE_TAXES PRODUCT_IMAGES VARIANT_SKU - VARIANT_PRICE - COST_PRICE VARIANT_WEIGHT VARIANT_IMAGES } @@ -3555,6 +3727,7 @@ input ProductFilterInput { minimalPrice: PriceRangeInput productTypes: [ID] ids: [ID] + channel: String } type ProductImage implements Node { @@ -3610,26 +3783,22 @@ input ProductImageUpdateInput { input ProductInput { attributes: [AttributeValueInput] - publicationDate: Date category: ID chargeTaxes: Boolean collections: [ID] description: String descriptionJson: JSONString - isPublished: Boolean name: String slug: String taxCode: String seo: SeoInput weight: WeightScalar - sku: String - trackInventory: Boolean - basePrice: PositiveDecimal - visibleInListings: Boolean + rating: Float } input ProductOrder { direction: OrderDirection! + channel: String attributeId: ID field: ProductOrderField } @@ -3642,6 +3811,8 @@ enum ProductOrderField { TYPE PUBLISHED PUBLICATION_DATE + COLLECTION + RATING } type ProductPricingInfo { @@ -3653,12 +3824,6 @@ type ProductPricingInfo { priceRangeLocalCurrency: TaxedMoneyRange } -type ProductSetAvailabilityForPurchase { - errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") - product: Product - productErrors: [ProductError!]! -} - input ProductStockFilterInput { warehouseIds: [ID!] quantity: IntRangeInput @@ -3701,7 +3866,7 @@ type ProductType implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - products(before: String, after: String, first: Int, last: Int): ProductCountableConnection + products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "Use the top-level `products` query with the `productTypes` filter.") taxRate: TaxRateType taxType: TaxType variantAttributes: [Attribute] @@ -3808,7 +3973,7 @@ type ProductVariant implements Node & ObjectWithMetadata { quantity: Int! @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") quantityAllocated: Int @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") stockQuantity: Int! @deprecated(reason: "Use the quantityAvailable field instead. This field will be removed after 2020-07-31.") - price: Money + channelListings: [ProductVariantChannelListing!] pricing: VariantPricingInfo isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") attributes: [SelectedAttribute!]! @@ -3832,12 +3997,11 @@ type ProductVariantBulkCreate { input ProductVariantBulkCreateInput { attributes: [AttributeValueInput]! - costPrice: PositiveDecimal - price: PositiveDecimal sku: String! trackInventory: Boolean weight: WeightScalar stocks: [StockInput!] + channelListings: [ProductVariantChannelListingAddInput!] } type ProductVariantBulkDelete { @@ -3846,6 +4010,26 @@ type ProductVariantBulkDelete { productErrors: [ProductError!]! } +type ProductVariantChannelListing implements Node { + id: ID! + channel: Channel! + price: Money + costPrice: Money + margin: Int +} + +input ProductVariantChannelListingAddInput { + channelId: ID! + price: PositiveDecimal! + costPrice: PositiveDecimal +} + +type ProductVariantChannelListingUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + variant: ProductVariant + productChannelListingErrors: [ProductChannelListingError!]! +} + type ProductVariantCountableConnection { pageInfo: PageInfo! edges: [ProductVariantCountableEdge!]! @@ -3865,8 +4049,6 @@ type ProductVariantCreate { input ProductVariantCreateInput { attributes: [AttributeValueInput]! - costPrice: PositiveDecimal - price: PositiveDecimal sku: String trackInventory: Boolean weight: WeightScalar @@ -3887,8 +4069,6 @@ input ProductVariantFilterInput { input ProductVariantInput { attributes: [AttributeValueInput] - costPrice: PositiveDecimal - price: PositiveDecimal sku: String trackInventory: Boolean weight: WeightScalar @@ -3949,6 +4129,12 @@ type ProductVariantUpdate { productVariant: ProductVariant } +input PublishableChannelListingInput { + channelId: ID! + isPublished: Boolean + publicationDate: Date +} + type Query { webhook(id: ID!): Webhook webhookEvents: [WebhookEvent] @@ -3960,52 +4146,54 @@ type Query { stock(id: ID!): Stock stocks(filter: StockFilterInput, before: String, after: String, first: Int, last: Int): StockCountableConnection shop: Shop! - shippingZone(id: ID!): ShippingZone - shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection + shippingZone(id: ID!, channel: String): ShippingZone + shippingZones(channel: String, before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection digitalContent(id: ID!): DigitalContent digitalContents(before: String, after: String, first: Int, last: Int): DigitalContentCountableConnection attributes(filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection attribute(id: ID!): Attribute categories(filter: CategoryFilterInput, sortBy: CategorySortingInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection category(id: ID, slug: String): Category - collection(id: ID, slug: String): Collection - collections(filter: CollectionFilterInput, sortBy: CollectionSortingInput, before: String, after: String, first: Int, last: Int): CollectionCountableConnection - product(id: ID, slug: String): Product - products(filter: ProductFilterInput, sortBy: ProductOrder, stockAvailability: StockAvailability, before: String, after: String, first: Int, last: Int): ProductCountableConnection + collection(id: ID, slug: String, channel: String): Collection + collections(filter: CollectionFilterInput, sortBy: CollectionSortingInput, channel: String, before: String, after: String, first: Int, last: Int): CollectionCountableConnection + product(id: ID, slug: String, channel: String): Product + products(filter: ProductFilterInput, sortBy: ProductOrder, stockAvailability: StockAvailability, channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection productType(id: ID!): ProductType productTypes(filter: ProductTypeFilterInput, sortBy: ProductTypeSortingInput, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection - productVariant(id: ID, sku: String): ProductVariant - productVariants(ids: [ID], filter: ProductVariantFilterInput, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection - reportProductSales(period: ReportingPeriod!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection + productVariant(id: ID, sku: String, channel: String): ProductVariant + productVariants(ids: [ID], channel: String, filter: ProductVariantFilterInput, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection + reportProductSales(period: ReportingPeriod!, channel: String!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection payment(id: ID!): Payment payments(before: String, after: String, first: Int, last: Int): PaymentCountableConnection page(id: ID, slug: String): Page pages(sortBy: PageSortingInput, filter: PageFilterInput, before: String, after: String, first: Int, last: Int): PageCountableConnection homepageEvents(before: String, after: String, first: Int, last: Int): OrderEventCountableConnection order(id: ID!): Order - orders(sortBy: OrderSortingInput, filter: OrderFilterInput, created: ReportingPeriod, status: OrderStatusFilter, before: String, after: String, first: Int, last: Int): OrderCountableConnection + orders(sortBy: OrderSortingInput, filter: OrderFilterInput, created: ReportingPeriod, status: OrderStatusFilter, channel: String, before: String, after: String, first: Int, last: Int): OrderCountableConnection draftOrders(sortBy: OrderSortingInput, filter: OrderDraftFilterInput, created: ReportingPeriod, before: String, after: String, first: Int, last: Int): OrderCountableConnection - ordersTotal(period: ReportingPeriod): TaxedMoney + ordersTotal(period: ReportingPeriod, channel: String): TaxedMoney orderByToken(token: UUID!): Order - menu(id: ID, name: String, slug: String): Menu - menus(sortBy: MenuSortingInput, filter: MenuFilterInput, before: String, after: String, first: Int, last: Int): MenuCountableConnection - menuItem(id: ID!): MenuItem - menuItems(sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection + menu(channel: String, id: ID, name: String, slug: String): Menu + menus(channel: String, sortBy: MenuSortingInput, filter: MenuFilterInput, before: String, after: String, first: Int, last: Int): MenuCountableConnection + menuItem(id: ID!, channel: String): MenuItem + menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection giftCard(id: ID!): GiftCard giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection plugin(id: ID!): Plugin plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection - sale(id: ID!): Sale - sales(filter: SaleFilterInput, sortBy: SaleSortingInput, query: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection - voucher(id: ID!): Voucher - vouchers(filter: VoucherFilterInput, sortBy: VoucherSortingInput, query: String, before: String, after: String, first: Int, last: Int): VoucherCountableConnection + sale(id: ID!, channel: String): Sale + sales(filter: SaleFilterInput, sortBy: SaleSortingInput, query: String, channel: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection + voucher(id: ID!, channel: String): Voucher + vouchers(filter: VoucherFilterInput, sortBy: VoucherSortingInput, query: String, channel: String, before: String, after: String, first: Int, last: Int): VoucherCountableConnection exportFile(id: ID!): ExportFile exportFiles(filter: ExportFileFilterInput, sortBy: ExportFileSortingInput, before: String, after: String, first: Int, last: Int): ExportFileCountableConnection taxTypes: [TaxType] - checkout(token: UUID): Checkout - checkouts(before: String, after: String, first: Int, last: Int): CheckoutCountableConnection + checkout(token: UUID, channel: String): Checkout + checkouts(channel: String, before: String, after: String, first: Int, last: Int): CheckoutCountableConnection checkoutLine(id: ID): CheckoutLine checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection + channel(id: ID): Channel + channels: [Channel!] appsInstallations: [AppInstallation!]! apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection app(id: ID!): App @@ -4058,13 +4246,15 @@ type Sale implements Node { id: ID! name: String! type: SaleType! - value: Float! startDate: DateTime! endDate: DateTime 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 translation(languageCode: LanguageCodeEnum!): SaleTranslation + channelListings: [SaleChannelListing!] + discountValue: Float + currency: String } type SaleAddCatalogues { @@ -4079,6 +4269,29 @@ type SaleBulkDelete { discountErrors: [DiscountError!]! } +type SaleChannelListing implements Node { + id: ID! + channel: Channel! + discountValue: Float! + currency: String! +} + +input SaleChannelListingAddInput { + channelId: ID! + discountValue: PositiveDecimal! +} + +input SaleChannelListingInput { + addChannels: [SaleChannelListingAddInput!] + removeChannels: [ID!] +} + +type SaleChannelListingUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + sale: Sale + discountErrors: [DiscountError!]! +} + type SaleCountableConnection { pageInfo: PageInfo! edges: [SaleCountableEdge!]! @@ -4136,6 +4349,7 @@ enum SaleSortField { input SaleSortingInput { direction: OrderDirection! + channel: String field: SaleSortField! } @@ -4193,6 +4407,7 @@ type ShippingError { message: String code: ShippingErrorCode! warehouses: [ID!] + channels: [ID!] } enum ShippingErrorCode { @@ -4209,15 +4424,42 @@ enum ShippingErrorCode { type ShippingMethod implements Node & ObjectWithMetadata { id: ID! name: String! - price: Money - minimumOrderPrice: Money - maximumOrderPrice: Money minimumOrderWeight: Weight maximumOrderWeight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! type: ShippingMethodTypeEnum translation(languageCode: LanguageCodeEnum!): ShippingMethodTranslation + channelListings: [ShippingMethodChannelListing!] + price: Money + maximumOrderPrice: Money + minimumOrderPrice: Money +} + +type ShippingMethodChannelListing implements Node { + id: ID! + channel: Channel! + minimumOrderPrice: Money + maximumOrderPrice: Money + price: Money +} + +input ShippingMethodChannelListingAddInput { + channelId: ID! + price: PositiveDecimal + minimumOrderPrice: PositiveDecimal + maximumOrderPrice: PositiveDecimal +} + +input ShippingMethodChannelListingInput { + addChannels: [ShippingMethodChannelListingAddInput!] + removeChannels: [ID!] +} + +type ShippingMethodChannelListingUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + shippingMethod: ShippingMethod + shippingErrors: [ShippingError!]! } type ShippingMethodTranslatableContent implements Node { @@ -4247,8 +4489,8 @@ type ShippingPriceBulkDelete { type ShippingPriceCreate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingZone: ShippingZone - shippingErrors: [ShippingError!]! shippingMethod: ShippingMethod + shippingErrors: [ShippingError!]! } type ShippingPriceDelete { @@ -4260,9 +4502,6 @@ type ShippingPriceDelete { input ShippingPriceInput { name: String - price: PositiveDecimal - minimumOrderPrice: PositiveDecimal - maximumOrderPrice: PositiveDecimal minimumOrderWeight: WeightScalar maximumOrderWeight: WeightScalar type: ShippingMethodTypeEnum @@ -4278,8 +4517,8 @@ type ShippingPriceTranslate { type ShippingPriceUpdate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingZone: ShippingZone - shippingErrors: [ShippingError!]! shippingMethod: ShippingMethod + shippingErrors: [ShippingError!]! } type ShippingZone implements Node & ObjectWithMetadata { @@ -4356,7 +4595,6 @@ type Shop { defaultMailSenderAddress: String description: String domain: Domain! - homepageCollection: Collection @deprecated(reason: "Use the `collection` query with the `slug` parameter. This field will be removed in Saleor 3.0") languages: [LanguageDisplay]! name: String! navigation: Navigation @deprecated(reason: "Fetch menus using the `menu` query with `slug` parameter.") @@ -4748,6 +4986,21 @@ type UpdatePrivateMetadata { scalar Upload +type UploadError { + field: String + message: String + code: UploadErrorCode! +} + +enum UploadErrorCode { + GRAPHQL_ERROR +} + +type UploadedFile { + url: String! + contentType: String! +} + type User implements Node & ObjectWithMetadata { id: ID! lastLogin: DateTime @@ -4882,14 +5135,16 @@ type Voucher implements Node { applyOncePerOrder: Boolean! applyOncePerCustomer: Boolean! discountValueType: DiscountValueTypeEnum! - discountValue: Float! - minSpent: Money minCheckoutItemsQuantity: Int 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 countries: [CountryDisplay] translation(languageCode: LanguageCodeEnum!): VoucherTranslation + discountValue: Float + currency: String + minSpent: Money + channelListings: [VoucherChannelListing!] } type VoucherAddCatalogues { @@ -4904,6 +5159,31 @@ type VoucherBulkDelete { discountErrors: [DiscountError!]! } +type VoucherChannelListing implements Node { + id: ID! + channel: Channel! + discountValue: Float! + currency: String! + minSpent: Money +} + +input VoucherChannelListingAddInput { + channelId: ID! + discountValue: PositiveDecimal + minAmountSpent: PositiveDecimal +} + +input VoucherChannelListingInput { + addChannels: [VoucherChannelListingAddInput!] + removeChannels: [ID!] +} + +type VoucherChannelListingUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + voucher: Voucher + discountErrors: [DiscountError!]! +} + type VoucherCountableConnection { pageInfo: PageInfo! edges: [VoucherCountableEdge!]! @@ -4948,11 +5228,9 @@ input VoucherInput { startDate: DateTime endDate: DateTime discountValueType: DiscountValueTypeEnum - discountValue: PositiveDecimal products: [ID] collections: [ID] categories: [ID] - minAmountSpent: PositiveDecimal minCheckoutItemsQuantity: Int countries: [String] applyOncePerOrder: Boolean @@ -4978,6 +5256,7 @@ enum VoucherSortField { input VoucherSortingInput { direction: OrderDirection! + channel: String field: VoucherSortField! } diff --git a/src/apps/views/AppsList/AppsList.tsx b/src/apps/views/AppsList/AppsList.tsx index 40c91f855..0f086e824 100644 --- a/src/apps/views/AppsList/AppsList.tsx +++ b/src/apps/views/AppsList/AppsList.tsx @@ -62,7 +62,7 @@ export const AppsList: React.FC = ({ params }) => { const navigate = useNavigator(); const { updateListSettings, settings } = useListSettings(ListViews.APPS_LIST); const paginate = usePaginator(); - const paginationState = createPaginationState(settings.rowNumber, params); + const paginationState = createPaginationState(settings?.rowNumber, params); const queryVariables = { sort: { direction: OrderDirection.DESC, diff --git a/src/attributes/components/AttributeListPage/AttributeListPage.tsx b/src/attributes/components/AttributeListPage/AttributeListPage.tsx index 9de531647..4bcbe0051 100644 --- a/src/attributes/components/AttributeListPage/AttributeListPage.tsx +++ b/src/attributes/components/AttributeListPage/AttributeListPage.tsx @@ -35,7 +35,6 @@ export interface AttributeListPageProps } const AttributeListPage: React.FC = ({ - currencySymbol, filterOpts, initialSearch, onAdd, @@ -73,7 +72,6 @@ const AttributeListPage: React.FC = ({ defaultMessage: "All Attributes", description: "tab name" })} - currencySymbol={currencySymbol} currentTab={currentTab} filterStructure={structure} initialSearch={initialSearch} diff --git a/src/attributes/views/AttributeList/AttributeList.tsx b/src/attributes/views/AttributeList/AttributeList.tsx index d71bd2907..1ca96d473 100644 --- a/src/attributes/views/AttributeList/AttributeList.tsx +++ b/src/attributes/views/AttributeList/AttributeList.tsx @@ -19,7 +19,6 @@ import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; @@ -52,7 +51,6 @@ const AttributeList: React.FC = ({ params }) => { const navigate = useNavigator(); const paginate = usePaginator(); const notify = useNotifier(); - const shop = useShop(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); @@ -145,13 +143,11 @@ const AttributeList: React.FC = ({ params }) => { ); const handleSort = createSortHandler(navigate, attributeListUrl, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); return ( <> data.attributes.edges.map(edge => edge.node))} - currencySymbol={currencySymbol} currentTab={currentTab} disabled={loading || attributeBulkDeleteOpts.loading} filterOpts={getFilterOpts(params)} diff --git a/src/categories/components/CategoryProductList/CategoryProductList.tsx b/src/categories/components/CategoryProductList/CategoryProductList.tsx index 3959c3bb6..bd40db659 100644 --- a/src/categories/components/CategoryProductList/CategoryProductList.tsx +++ b/src/categories/components/CategoryProductList/CategoryProductList.tsx @@ -3,11 +3,11 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; +import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown"; import Checkbox from "@saleor/components/Checkbox"; import Money from "@saleor/components/Money"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; import TableCellAvatar, { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar"; @@ -16,12 +16,9 @@ import TablePagination from "@saleor/components/TablePagination"; import { maybe, renderCollection } from "@saleor/misc"; import { ListActions, ListProps } from "@saleor/types"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedMessage } from "react-intl"; -import { - CategoryDetails_category_products_edges_node, - CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted -} from "../../types/CategoryDetails"; +import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; const useStyles = makeStyles( theme => ({ @@ -74,11 +71,14 @@ const useStyles = makeStyles( ); interface CategoryProductListProps extends ListProps, ListActions { + channelsCount: number; + selectedChannel: string; products: CategoryDetails_category_products_edges_node[]; } export const CategoryProductList: React.FC = props => { const { + channelsCount, disabled, isChecked, pageInfo, @@ -89,59 +89,14 @@ export const CategoryProductList: React.FC = props => toolbar, onNextPage, onPreviousPage, - onRowClick + onRowClick, + selectedChannel } = props; const classes = useStyles(props); - const intl = useIntl(); const numberOfColumns = 5; - const getProductPrice = ( - priceRangeUndiscounted: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted - ) => { - if (!priceRangeUndiscounted) { - return null; - } - - const { start, stop } = priceRangeUndiscounted; - const { - gross: { amount: startAmount } - } = start; - const { - gross: { amount: stopAmount } - } = stop; - - if (startAmount === stopAmount) { - return ( - - ); - } else { - return ( - <> - - {" - "} - - - ); - } - }; - return (
@@ -173,8 +128,8 @@ export const CategoryProductList: React.FC = props => @@ -202,6 +157,9 @@ export const CategoryProductList: React.FC = props => products, product => { const isSelected = product ? isChecked(product.id) : false; + const channel = product?.channelListings.find( + listing => listing.channel.id === selectedChannel + ); return ( = props => )} - {product && - maybe(() => product.isAvailable !== undefined) ? ( - ) : ( )} - {product?.pricing?.priceRangeUndiscounted ? ( - getProductPrice(product?.pricing?.priceRangeUndiscounted) + {product?.channelListings ? ( + ) : ( )} diff --git a/src/categories/components/CategoryProducts/CategoryProducts.tsx b/src/categories/components/CategoryProducts/CategoryProducts.tsx index 06ace2040..6059953b2 100644 --- a/src/categories/components/CategoryProducts/CategoryProducts.tsx +++ b/src/categories/components/CategoryProducts/CategoryProducts.tsx @@ -1,6 +1,11 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { makeStyles } from "@material-ui/core/styles"; import CardTitle from "@saleor/components/CardTitle"; +import { ChannelsSelect } from "@saleor/components/ChannelsSelect"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -10,10 +15,23 @@ import CategoryProductList from "../CategoryProductList"; interface CategoryProductsProps extends PageListProps, ListActions { products: CategoryDetails_category_products_edges_node[]; + channelChoices: SingleAutocompleteChoiceType[]; + channelsCount: number; categoryName: string; } +const useStyles = makeStyles( + theme => ({ + channelsSelectContainer: { + paddingTop: theme.spacing(2) + } + }), + { name: "CategoryProducts" } +); + export const CategoryProducts: React.FC = ({ + channelChoices, + channelsCount, products, disabled, pageInfo, @@ -29,6 +47,11 @@ export const CategoryProducts: React.FC = ({ toolbar }) => { const intl = useIntl(); + const classes = useStyles({}); + + const [channelChoice, setChannelChoice] = useStateFromProps( + channelChoices?.length ? channelChoices[0]?.value : "" + ); return ( @@ -49,7 +72,16 @@ export const CategoryProducts: React.FC = ({ } /> + + + void; onSubmit: (data: CategoryUpdateData) => SubmitPromise; onImageUpload(file: File); @@ -66,6 +69,8 @@ const ProductsTab = Tab(CategoryPageTab.products); export const CategoryUpdatePage: React.FC = ({ changeTab, + channelChoices, + channelsCount, currentTab, category, disabled, @@ -198,6 +203,8 @@ export const CategoryUpdatePage: React.FC = ({ )} {currentTab === CategoryPageTab.products && ( ( ); export const categoryDetails = gql` - ${fragmentMoney} + ${channelListingProductFragment} ${categoryFragment} ${categoryDetailsFragment} ${pageInfoFragment} @@ -81,7 +81,6 @@ export const categoryDetails = gql` node { id name - isAvailable thumbnail { url } @@ -89,19 +88,8 @@ export const categoryDetails = gql` id name } - pricing { - priceRangeUndiscounted { - start { - gross { - ...Money - } - } - stop { - gross { - ...Money - } - } - } + channelListings { + ...ChannelListingProductFragment } } } diff --git a/src/categories/types/CategoryDetails.ts b/src/categories/types/CategoryDetails.ts index d6ce553eb..c7b149d4b 100644 --- a/src/categories/types/CategoryDetails.ts +++ b/src/categories/types/CategoryDetails.ts @@ -85,47 +85,37 @@ export interface CategoryDetails_category_products_edges_node_productType { name: string; } -export interface CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_start_gross { +export interface CategoryDetails_category_products_edges_node_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_start_gross; +export interface CategoryDetails_category_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; } -export interface CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_start | null; - stop: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted_stop | null; -} - -export interface CategoryDetails_category_products_edges_node_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: CategoryDetails_category_products_edges_node_pricing_priceRangeUndiscounted | null; +export interface CategoryDetails_category_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: CategoryDetails_category_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: CategoryDetails_category_products_edges_node_channelListings_channel; } export interface CategoryDetails_category_products_edges_node { __typename: "Product"; id: string; name: string; - isAvailable: boolean | null; thumbnail: CategoryDetails_category_products_edges_node_thumbnail | null; productType: CategoryDetails_category_products_edges_node_productType; - pricing: CategoryDetails_category_products_edges_node_pricing | null; + channelListings: CategoryDetails_category_products_edges_node_channelListings[] | null; } export interface CategoryDetails_category_products_edges { diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index 5efdaafd6..c7323d452 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -1,6 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import { useChannelsList } from "@saleor/channels/queries"; import ActionDialog from "@saleor/components/ActionDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; @@ -78,6 +79,13 @@ export const CategoryDetails: React.FC = ({ variables: { ...paginationState, id } }); + const { data: channelsData } = useChannelsList({}); + + const channelChoices = channelsData?.channels?.map(channel => ({ + label: channel.name, + value: channel.id + })); + const category = data?.category; if (category === null) { @@ -205,6 +213,8 @@ export const CategoryDetails: React.FC = ({ <> data.category.name)} /> data.category)} diff --git a/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx new file mode 100644 index 000000000..efcd253e8 --- /dev/null +++ b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx @@ -0,0 +1,27 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { channelsList } from "../../fixtures"; +import ChannelDeleteDialog, { + ChannelDeleteDialogProps +} from "./ChannelDeleteDialog"; + +const props: ChannelDeleteDialogProps = { + channelsChoices: channelsList.map(channel => ({ + label: channel.name, + value: channel.id + })), + confirmButtonState: "default", + onBack: () => undefined, + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Views / Channels / Delete channel", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("without channels to choose", () => ( + + )); diff --git a/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.tsx b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.tsx new file mode 100644 index 000000000..58c228100 --- /dev/null +++ b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.tsx @@ -0,0 +1,96 @@ +import Typography from "@material-ui/core/Typography"; +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import { + Choices, + SingleSelectField +} from "@saleor/components/SingleSelectField"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { buttonMessages } from "@saleor/intl"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "../styles"; + +export interface ChannelDeleteDialogProps { + channelsChoices: Choices; + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + onBack: () => void; + onClose: () => void; + onConfirm: (targetChannelId: string) => void; +} + +const ChannelDeleteDialog: React.FC = ({ + channelsChoices = [], + confirmButtonState, + open, + onBack, + onClose, + onConfirm +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + const [choice, setChoice] = useStateFromProps( + !!channelsChoices.length ? channelsChoices[0].value : "" + ); + const hasChannels = !!channelsChoices?.length; + + return ( + (hasChannels ? onConfirm(choice) : onBack())} + title={intl.formatMessage({ + defaultMessage: "Delete Channel", + description: "dialog header" + })} + confirmButtonLabel={intl.formatMessage( + hasChannels ? buttonMessages.delete : buttonMessages.ok + )} + variant={hasChannels ? "delete" : "default"} + > +
+ {hasChannels ? ( + <> + + + +
+ setChoice(e.target.value)} + /> +
+ + + + + ) : ( + + + + )} +
+
+ ); +}; +ChannelDeleteDialog.displayName = "ChannelDeleteDialog"; +export default ChannelDeleteDialog; diff --git a/src/channels/components/ChannelDeleteDialog/index.ts b/src/channels/components/ChannelDeleteDialog/index.ts new file mode 100644 index 000000000..aa8f71dd5 --- /dev/null +++ b/src/channels/components/ChannelDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelDeleteDialog"; +export { default } from "./ChannelDeleteDialog"; diff --git a/src/channels/components/ChannelForm/ChannelForm.stories.tsx b/src/channels/components/ChannelForm/ChannelForm.stories.tsx new file mode 100644 index 000000000..27219435a --- /dev/null +++ b/src/channels/components/ChannelForm/ChannelForm.stories.tsx @@ -0,0 +1,25 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { channelCreateErrors } from "../../fixtures"; +import ChannelForm, { ChannelFormProps } from "./ChannelForm"; + +const props: ChannelFormProps = { + data: { + currencyCode: "euro", + name: "Test", + slug: "test" + }, + disabled: false, + errors: [], + onChange: () => undefined +}; + +storiesOf("Views / Channels / Channel form", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("disabled", () => ) + .add("with errors", () => ( + + )); diff --git a/src/channels/components/ChannelForm/ChannelForm.tsx b/src/channels/components/ChannelForm/ChannelForm.tsx new file mode 100644 index 000000000..07fca80a3 --- /dev/null +++ b/src/channels/components/ChannelForm/ChannelForm.tsx @@ -0,0 +1,162 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import InputAdornment from "@material-ui/core/InputAdornment"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import CardSpacer from "@saleor/components/CardSpacer"; +import CardTitle from "@saleor/components/CardTitle"; +import FormSpacer from "@saleor/components/FormSpacer"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; +import { ChannelErrorFragment } from "@saleor/fragments/types/ChannelErrorFragment"; +import useClipboard from "@saleor/hooks/useClipboard"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import { FormChange } from "@saleor/hooks/useForm"; +import { commonMessages } from "@saleor/intl"; +import { getFormErrors } from "@saleor/utils/errors"; +import getChannelsErrorMessage from "@saleor/utils/errors/channels"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "../styles"; + +export interface FormData { + name: string; + currencyCode: string; + slug: string; +} + +export interface ChannelFormProps { + data: FormData; + disabled: boolean; + currencyCodes?: SingleAutocompleteChoiceType[]; + errors: ChannelErrorFragment[]; + selectedCurrencyCode?: string; + onChange: FormChange; + onCurrencyCodeChange?: (event: ChangeEvent) => void; +} + +export const ChannelForm: React.FC = ({ + currencyCodes, + data, + disabled, + errors, + selectedCurrencyCode, + onChange, + onCurrencyCodeChange +}) => { + const intl = useIntl(); + const [copied, copy] = useClipboard(); + const formErrors = getFormErrors( + ["name", "slug", "currencyCode"], + errors + ); + const classes = useStyles({}); + + return ( + <> + + + + + + copy(data.slug)} + > + {copied ? ( + + ) : ( + + )} + + ) + }} + /> + + + + + + + + {!!currencyCodes ? ( + + ) : ( + <> + + + + {data.currencyCode} + + )} + + + + ); +}; + +ChannelForm.displayName = "ChannelForm"; +export default ChannelForm; diff --git a/src/channels/components/ChannelForm/index.ts b/src/channels/components/ChannelForm/index.ts new file mode 100644 index 000000000..05586bb7a --- /dev/null +++ b/src/channels/components/ChannelForm/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelForm"; +export { default } from "./ChannelForm"; diff --git a/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.stories.tsx b/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.stories.tsx new file mode 100644 index 000000000..e85b9c563 --- /dev/null +++ b/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.stories.tsx @@ -0,0 +1,26 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { channelsList } from "../../fixtures"; +import ChannelSettingsDialog, { + ChannelSettingsDialogProps +} from "./ChannelSettingsDialog"; + +const channelsChoices = channelsList.map(channel => ({ + label: channel.name, + value: channel.id +})); + +const props: ChannelSettingsDialogProps = { + channelsChoices, + confirmButtonState: "default", + defaultChoice: channelsChoices[0]?.value, + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Views / Channels / Settings dialog", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.tsx b/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.tsx new file mode 100644 index 000000000..3e524a1f0 --- /dev/null +++ b/src/channels/components/ChannelSettingsDialog/ChannelSettingsDialog.tsx @@ -0,0 +1,71 @@ +import Typography from "@material-ui/core/Typography"; +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import { + Choices, + SingleSelectField +} from "@saleor/components/SingleSelectField"; +import React, { useState } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "../styles"; + +export interface ChannelSettingsDialogProps { + channelsChoices: Choices; + confirmButtonState: ConfirmButtonTransitionState; + defaultChoice: string; + open: boolean; + onClose: () => void; + onConfirm: (choice: string) => void; +} + +const ChannelSettingsDialog: React.FC = ({ + channelsChoices = [], + confirmButtonState, + defaultChoice, + open, + onClose, + onConfirm +}) => { + const classes = useStyles({}); + const intl = useIntl(); + const [choice, setChoice] = useState( + defaultChoice || (!!channelsChoices.length ? channelsChoices[0].value : "") + ); + + return ( + onConfirm(choice)} + title={intl.formatMessage({ + defaultMessage: "Settings", + description: "dialog header" + })} + > +
+ + + +
+ setChoice(e.target.value)} + /> +
+
+
+ ); +}; +ChannelSettingsDialog.displayName = "ChannelSettingsDialog"; +export default ChannelSettingsDialog; diff --git a/src/channels/components/ChannelSettingsDialog/index.ts b/src/channels/components/ChannelSettingsDialog/index.ts new file mode 100644 index 000000000..2800c3c09 --- /dev/null +++ b/src/channels/components/ChannelSettingsDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelSettingsDialog"; +export { default } from "./ChannelSettingsDialog"; diff --git a/src/channels/components/ChannelStatus/ChannelStatus.stories.tsx b/src/channels/components/ChannelStatus/ChannelStatus.stories.tsx new file mode 100644 index 000000000..8e6558635 --- /dev/null +++ b/src/channels/components/ChannelStatus/ChannelStatus.stories.tsx @@ -0,0 +1,16 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ChannelStatus, { ChannelStatusProps } from "./ChannelStatus"; + +const props: ChannelStatusProps = { + disabled: false, + isActive: false, + updateChannelStatus: () => undefined +}; + +storiesOf("Views / Channels / Channel status", module) + .addDecorator(Decorator) + .add("inactive", () => ) + .add("active", () => ); diff --git a/src/channels/components/ChannelStatus/ChannelStatus.tsx b/src/channels/components/ChannelStatus/ChannelStatus.tsx new file mode 100644 index 000000000..b2b5cc7ac --- /dev/null +++ b/src/channels/components/ChannelStatus/ChannelStatus.tsx @@ -0,0 +1,71 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import CardTitle from "@saleor/components/CardTitle"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "../styles"; + +export interface ChannelStatusProps { + isActive: boolean; + disabled: boolean; + updateChannelStatus: () => void; +} + +export const ChannelStatus: React.FC = ({ + disabled, + isActive, + updateChannelStatus +}) => { + const intl = useIntl(); + const classes = useStyles({}); + + return ( + + + + + + + + {isActive ? ( + + ) : ( + + )} + + + + + ); +}; + +ChannelStatus.displayName = "ChannelStatus"; +export default ChannelStatus; diff --git a/src/channels/components/ChannelStatus/index.ts b/src/channels/components/ChannelStatus/index.ts new file mode 100644 index 000000000..2f74c7a69 --- /dev/null +++ b/src/channels/components/ChannelStatus/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelStatus"; +export { default } from "./ChannelStatus"; diff --git a/src/channels/components/styles.ts b/src/channels/components/styles.ts new file mode 100644 index 000000000..b58f28de2 --- /dev/null +++ b/src/channels/components/styles.ts @@ -0,0 +1,27 @@ +import makeStyles from "@material-ui/core/styles/makeStyles"; + +export const useStyles = makeStyles( + theme => ({ + activeBtn: { + marginLeft: theme.spacing(-1), + marginTop: theme.spacing(1.5) + }, + copyBtn: { + color: theme.palette.primary.main, + fontSize: 14, + fontWeight: 500, + textTransform: "uppercase" + }, + currencyTitle: { + marginBottom: theme.spacing(1) + }, + label: { + color: theme.palette.text.secondary + }, + select: { + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2) + } + }), + { name: "ChannelComponents" } +); diff --git a/src/channels/fixtures.ts b/src/channels/fixtures.ts new file mode 100644 index 000000000..b37ce6b86 --- /dev/null +++ b/src/channels/fixtures.ts @@ -0,0 +1,164 @@ +import { ChannelErrorFragment } from "@saleor/fragments/types/ChannelErrorFragment"; +import { ProductDetails_product_channelListings } from "@saleor/products/types/ProductDetails"; +import { ChannelErrorCode } from "@saleor/types/globalTypes"; + +import { Channel_channel } from "./types/Channel"; +import { Channels_channels } from "./types/Channels"; + +export const channelCreateErrors: ChannelErrorFragment[] = [ + { + __typename: "ChannelError", + code: ChannelErrorCode.UNIQUE, + field: "slug", + message: "Channel with this Slug already exists." + } +]; + +export const channelsList: Channels_channels[] = [ + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbm5lcDoy", + isActive: true, + name: "Test", + slug: "test" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbm7lbDoy213", + isActive: true, + name: "Channel", + slug: "channel" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbn5lbDoytr", + isActive: true, + name: "Channel test", + slug: "channeltest" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbm5lbDo5bot", + isActive: true, + name: "Channel USD", + slug: "channel-usd" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbm7lbDoyr0tr", + isActive: true, + name: "Channel", + slug: "channel2" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbn5lbDoyya", + isActive: true, + name: "Channel test", + slug: "channeltest4" + }, + { + __typename: "Channel", + currencyCode: "euro", + id: "Q2hhbm5lbDo5w0z", + isActive: true, + name: "Channel USD", + slug: "channel-usd1" + } +]; + +export const channel: Channel_channel = { + __typename: "Channel", + currencyCode: "zl", + id: "Q2hhbm5lbDov78", + isActive: true, + name: "Test", + slug: "test" +}; + +export const productChannels: ProductDetails_product_channelListings[] = [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 5, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 0, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "125", + name: "Channel3" + }, + discountedPrice: { + __typename: "Money", + amount: 8, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: null, + visibleInListings: true + } +]; + +export const productPriceChannels = [ + { + costPrice: "5", + id: "123", + name: "Channel1", + sellingPrice: "10" + }, + { + costPrice: "15", + id: "124", + name: "Channel2", + sellingPrice: "20" + }, + { + costPrice: "15", + id: "125", + name: "Channel3", + sellingPrice: "100" + } +]; diff --git a/src/channels/index.tsx b/src/channels/index.tsx new file mode 100644 index 000000000..209f41531 --- /dev/null +++ b/src/channels/index.tsx @@ -0,0 +1,47 @@ +import { sectionNames } from "@saleor/intl"; +import { asSortParams } from "@saleor/utils/sort"; +import { parse as parseQs } from "qs"; +import React from "react"; +import { useIntl } from "react-intl"; +import { Route, RouteComponentProps, Switch } from "react-router-dom"; + +import { WindowTitle } from "../components/WindowTitle"; +import { + channelAddPath, + channelPath, + channelsListPath, + ChannelsListUrlQueryParams, + ChannelsListUrlSortField +} from "./urls"; +import ChannelCreateComponent from "./views/ChannelCreate"; +import ChannelDetailsComponent from "./views/ChannelDetails"; +import ChannelsListComponent from "./views/ChannelsList"; + +const ChannelDetails: React.FC> = ({ + match +}) => ; + +const ChannelsList: React.FC = ({ location }) => { + const qs = parseQs(location.search.substr(1)); + const params: ChannelsListUrlQueryParams = asSortParams( + qs, + ChannelsListUrlSortField + ); + return ; +}; + +export const ChannelsSection: React.FC<{}> = () => { + const intl = useIntl(); + + return ( + <> + + + + + + + + ); +}; +export default ChannelsSection; diff --git a/src/channels/mutations.ts b/src/channels/mutations.ts new file mode 100644 index 000000000..5f80ebc06 --- /dev/null +++ b/src/channels/mutations.ts @@ -0,0 +1,117 @@ +import { + channelDetailsFragment, + channelErrorFragment +} from "@saleor/fragments/channels"; +import makeMutation from "@saleor/hooks/makeMutation"; +import gql from "graphql-tag"; + +import { + ChannelActivate, + ChannelActivateVariables +} from "./types/ChannelActivate"; +import { ChannelCreate, ChannelCreateVariables } from "./types/ChannelCreate"; +import { + ChannelDeactivate, + ChannelDeactivateVariables +} from "./types/ChannelDeactivate"; +import { ChannelDelete, ChannelDeleteVariables } from "./types/ChannelDelete"; +import { ChannelUpdate, ChannelUpdateVariables } from "./types/ChannelUpdate"; + +export const channelCreateMutation = gql` + ${channelErrorFragment} + ${channelDetailsFragment} + mutation ChannelCreate($input: ChannelCreateInput!) { + channelCreate(input: $input) { + channel { + ...ChannelDetailsFragment + } + errors: channelErrors { + ...ChannelErrorFragment + } + } + } +`; + +export const channelUpdateMutation = gql` + ${channelErrorFragment} + ${channelDetailsFragment} + mutation ChannelUpdate($id: ID!, $input: ChannelUpdateInput!) { + channelUpdate(id: $id, input: $input) { + channel { + ...ChannelDetailsFragment + } + errors: channelErrors { + ...ChannelErrorFragment + } + } + } +`; + +export const channelDeleteMutation = gql` + ${channelErrorFragment} + ${channelDetailsFragment} + mutation ChannelDelete($id: ID!, $input: ChannelDeleteInput!) { + channelDelete(id: $id, input: $input) { + channel { + ...ChannelDetailsFragment + } + errors: channelErrors { + ...ChannelErrorFragment + } + } + } +`; + +export const channelActivateMutation = gql` + ${channelErrorFragment} + ${channelDetailsFragment} + mutation ChannelActivate($id: ID!) { + channelActivate(id: $id) { + channel { + ...ChannelDetailsFragment + } + errors: channelErrors { + ...ChannelErrorFragment + } + } + } +`; + +export const channelDeactivateMutation = gql` + ${channelErrorFragment} + ${channelDetailsFragment} + mutation ChannelDeactivate($id: ID!) { + channelDeactivate(id: $id) { + channel { + ...ChannelDetailsFragment + } + errors: channelErrors { + ...ChannelErrorFragment + } + } + } +`; + +export const useChannelCreateMutation = makeMutation< + ChannelCreate, + ChannelCreateVariables +>(channelCreateMutation); + +export const useChannelUpdateMutation = makeMutation< + ChannelUpdate, + ChannelUpdateVariables +>(channelUpdateMutation); + +export const useChannelDeleteMutation = makeMutation< + ChannelDelete, + ChannelDeleteVariables +>(channelDeleteMutation); + +export const useChannelActivateMutation = makeMutation< + ChannelActivate, + ChannelActivateVariables +>(channelActivateMutation); +export const useChannelDeactivateMutation = makeMutation< + ChannelDeactivate, + ChannelDeactivateVariables +>(channelDeactivateMutation); diff --git a/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.stories.tsx b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.stories.tsx new file mode 100644 index 000000000..16726830c --- /dev/null +++ b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.stories.tsx @@ -0,0 +1,41 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { channel, channelCreateErrors } from "../../fixtures"; +import ChannelDetailsPage, { + ChannelDetailsPageProps +} from "./ChannelDetailsPage"; + +const props: ChannelDetailsPageProps = { + currencyCodes: [ + { label: "USD", value: "USD" }, + { label: "PLN", value: "PLN" } + ], + disabled: false, + disabledStatus: false, + errors: [], + onBack: () => undefined, + onSubmit: () => undefined, + saveButtonBarState: "default", + updateChannelStatus: () => undefined +}; + +storiesOf("Views / Channels / Channel details", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("disabled", () => ) + .add("loading", () => ( + + )) + .add("with data", () => ) + .add("without editable currency code", () => ( + + )) + .add("with errors", () => ( + + )); diff --git a/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx new file mode 100644 index 000000000..377382f02 --- /dev/null +++ b/src/channels/pages/ChannelDetailsPage/ChannelDetailsPage.tsx @@ -0,0 +1,93 @@ +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Form from "@saleor/components/Form"; +import Grid from "@saleor/components/Grid"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { ChannelErrorFragment } from "@saleor/fragments/types/ChannelErrorFragment"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import React from "react"; + +import { ChannelForm, FormData } from "../../components/ChannelForm"; +import { ChannelStatus } from "../../components/ChannelStatus/ChannelStatus"; +import { Channel_channel } from "../../types/Channel"; + +export interface ChannelDetailsPageProps { + channel?: Channel_channel; + currencyCodes?: SingleAutocompleteChoiceType[]; + disabled: boolean; + disabledStatus?: boolean; + errors: ChannelErrorFragment[]; + saveButtonBarState: ConfirmButtonTransitionState; + onBack?: () => void; + onSubmit?: (data: FormData) => void; + updateChannelStatus?: () => void; +} + +const initialData: FormData = { + currencyCode: "", + name: "", + slug: "" +}; + +export const ChannelDetailsPage: React.FC = ({ + channel, + currencyCodes, + disabled, + disabledStatus, + errors, + onBack, + onSubmit, + saveButtonBarState, + updateChannelStatus +}) => { + const [selectedCurrencyCode, setSelectedCurrencyCode] = React.useState(""); + + return ( +
+ {({ change, data, hasChanged, submit }) => { + const handleCurrencyCodeSelect = createSingleAutocompleteSelectHandler( + change, + setSelectedCurrencyCode, + currencyCodes + ); + const formDisabled = !data.name || !data.slug || !data.currencyCode; + + return ( + <> + +
+ +
+ {!!updateChannelStatus && ( +
+ +
+ )} +
+ + + ); + }} + + ); +}; + +ChannelDetailsPage.displayName = "ChannelDetailsPage"; +export default ChannelDetailsPage; diff --git a/src/channels/pages/ChannelDetailsPage/index.ts b/src/channels/pages/ChannelDetailsPage/index.ts new file mode 100644 index 000000000..ff215d458 --- /dev/null +++ b/src/channels/pages/ChannelDetailsPage/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelDetailsPage"; +export { default } from "./ChannelDetailsPage"; diff --git a/src/channels/pages/ChannelsListPage/ChannelsListPage.stories.tsx b/src/channels/pages/ChannelsListPage/ChannelsListPage.stories.tsx new file mode 100644 index 000000000..d5050322b --- /dev/null +++ b/src/channels/pages/ChannelsListPage/ChannelsListPage.stories.tsx @@ -0,0 +1,19 @@ +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { channelsList } from "../../fixtures"; +import ChannelsListPage, { ChannelsListPageProps } from "./ChannelsListPage"; + +const props: ChannelsListPageProps = { + channelsList, + navigateToChannelCreate: () => undefined, + onBack: () => undefined, + onRemove: () => undefined, + onRowClick: () => undefined +}; + +storiesOf("Views / Channels / Channels list", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("empty", () => ); diff --git a/src/channels/pages/ChannelsListPage/ChannelsListPage.tsx b/src/channels/pages/ChannelsListPage/ChannelsListPage.tsx new file mode 100644 index 000000000..06dafa9ec --- /dev/null +++ b/src/channels/pages/ChannelsListPage/ChannelsListPage.tsx @@ -0,0 +1,126 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import IconButton from "@material-ui/core/IconButton"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableHead from "@material-ui/core/TableHead"; +import TableRow from "@material-ui/core/TableRow"; +import DeleteIcon from "@material-ui/icons/Delete"; +import AppHeader from "@saleor/components/AppHeader"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import TableCellHeader from "@saleor/components/TableCellHeader"; +import { sectionNames } from "@saleor/intl"; +import { renderCollection, stopPropagation } from "@saleor/misc"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { Channels_channels } from "../../types/Channels"; +import { useStyles } from "./styles"; + +export interface ChannelsListPageProps { + channelsList: Channels_channels[] | undefined; + navigateToChannelCreate: () => void; + onBack: () => void; + onRowClick: (id: string) => () => void; + onRemove: (id: string) => void; +} + +const numberOfColumns = 2; + +export const ChannelsListPage: React.FC = ({ + channelsList, + navigateToChannelCreate, + onBack, + onRemove, + onRowClick +}) => { + const intl = useIntl(); + const classes = useStyles({}); + + return ( + + + {intl.formatMessage(sectionNames.configuration)} + + + + + + + + + + + + + + + + + + {renderCollection( + channelsList, + channel => ( + + + + {channel?.name || } + + + + {channelsList?.length > 1 && ( + onRemove(channel.id)) + : undefined + } + > + + + )} + + + ), + () => ( + + + + + + ) + )} + + + + + ); +}; + +ChannelsListPage.displayName = "ChannelsListPage"; +export default ChannelsListPage; diff --git a/src/channels/pages/ChannelsListPage/index.ts b/src/channels/pages/ChannelsListPage/index.ts new file mode 100644 index 000000000..4e47acdf6 --- /dev/null +++ b/src/channels/pages/ChannelsListPage/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsListPage"; +export { default } from "./ChannelsListPage"; diff --git a/src/channels/pages/ChannelsListPage/styles.ts b/src/channels/pages/ChannelsListPage/styles.ts new file mode 100644 index 000000000..1e8c98126 --- /dev/null +++ b/src/channels/pages/ChannelsListPage/styles.ts @@ -0,0 +1,37 @@ +import makeStyles from "@material-ui/core/styles/makeStyles"; + +export const useStyles = makeStyles( + theme => ({ + [theme.breakpoints.up("lg")]: { + colName: { + "&&": { + width: "auto" + } + } + }, + colAction: { + "&&": { + paddingRight: theme.spacing(1) + }, + textAlign: "right", + width: 140 + }, + colName: { + paddingLeft: 0, + width: 250 + }, + colRight: { + textAlign: "right" + }, + columnPicker: { + marginRight: theme.spacing(3) + }, + table: { + tableLayout: "fixed" + }, + tableRow: { + cursor: "pointer" + } + }), + { name: "ChannelsListPage" } +); diff --git a/src/channels/queries.ts b/src/channels/queries.ts new file mode 100644 index 000000000..c373d0d20 --- /dev/null +++ b/src/channels/queries.ts @@ -0,0 +1,29 @@ +import { channelDetailsFragment } from "@saleor/fragments/channels"; +import makeQuery from "@saleor/hooks/makeQuery"; +import gql from "graphql-tag"; + +import { Channel, ChannelVariables } from "./types/Channel"; +import { Channels } from "./types/Channels"; + +export const channelsList = gql` + ${channelDetailsFragment} + query Channels { + channels { + ...ChannelDetailsFragment + } + } +`; + +export const channelDetails = gql` + ${channelDetailsFragment} + query Channel($id: ID!) { + channel(id: $id) { + ...ChannelDetailsFragment + } + } +`; + +export const useChannelsList = makeQuery(channelsList); +export const useChannelDetails = makeQuery( + channelDetails +); diff --git a/src/channels/types/Channel.ts b/src/channels/types/Channel.ts new file mode 100644 index 000000000..e0cbc55c6 --- /dev/null +++ b/src/channels/types/Channel.ts @@ -0,0 +1,24 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: Channel +// ==================================================== + +export interface Channel_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface Channel { + channel: Channel_channel | null; +} + +export interface ChannelVariables { + id: string; +} diff --git a/src/channels/types/ChannelActivate.ts b/src/channels/types/ChannelActivate.ts new file mode 100644 index 000000000..d011227a9 --- /dev/null +++ b/src/channels/types/ChannelActivate.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ChannelActivate +// ==================================================== + +export interface ChannelActivate_channelActivate_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface ChannelActivate_channelActivate_errors { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} + +export interface ChannelActivate_channelActivate { + __typename: "ChannelActivate"; + channel: ChannelActivate_channelActivate_channel | null; + errors: ChannelActivate_channelActivate_errors[]; +} + +export interface ChannelActivate { + channelActivate: ChannelActivate_channelActivate | null; +} + +export interface ChannelActivateVariables { + id: string; +} diff --git a/src/channels/types/ChannelCreate.ts b/src/channels/types/ChannelCreate.ts new file mode 100644 index 000000000..5437a1d29 --- /dev/null +++ b/src/channels/types/ChannelCreate.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelCreateInput, ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ChannelCreate +// ==================================================== + +export interface ChannelCreate_channelCreate_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface ChannelCreate_channelCreate_errors { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} + +export interface ChannelCreate_channelCreate { + __typename: "ChannelCreate"; + channel: ChannelCreate_channelCreate_channel | null; + errors: ChannelCreate_channelCreate_errors[]; +} + +export interface ChannelCreate { + channelCreate: ChannelCreate_channelCreate | null; +} + +export interface ChannelCreateVariables { + input: ChannelCreateInput; +} diff --git a/src/channels/types/ChannelDeactivate.ts b/src/channels/types/ChannelDeactivate.ts new file mode 100644 index 000000000..d4bee0c93 --- /dev/null +++ b/src/channels/types/ChannelDeactivate.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ChannelDeactivate +// ==================================================== + +export interface ChannelDeactivate_channelDeactivate_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface ChannelDeactivate_channelDeactivate_errors { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} + +export interface ChannelDeactivate_channelDeactivate { + __typename: "ChannelDeactivate"; + channel: ChannelDeactivate_channelDeactivate_channel | null; + errors: ChannelDeactivate_channelDeactivate_errors[]; +} + +export interface ChannelDeactivate { + channelDeactivate: ChannelDeactivate_channelDeactivate | null; +} + +export interface ChannelDeactivateVariables { + id: string; +} diff --git a/src/channels/types/ChannelDelete.ts b/src/channels/types/ChannelDelete.ts new file mode 100644 index 000000000..7dbcef156 --- /dev/null +++ b/src/channels/types/ChannelDelete.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelDeleteInput, ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ChannelDelete +// ==================================================== + +export interface ChannelDelete_channelDelete_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface ChannelDelete_channelDelete_errors { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} + +export interface ChannelDelete_channelDelete { + __typename: "ChannelDelete"; + channel: ChannelDelete_channelDelete_channel | null; + errors: ChannelDelete_channelDelete_errors[]; +} + +export interface ChannelDelete { + channelDelete: ChannelDelete_channelDelete | null; +} + +export interface ChannelDeleteVariables { + id: string; + input: ChannelDeleteInput; +} diff --git a/src/channels/types/ChannelDetailsFragment.ts b/src/channels/types/ChannelDetailsFragment.ts new file mode 100644 index 000000000..930833427 --- /dev/null +++ b/src/channels/types/ChannelDetailsFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelDetailsFragment +// ==================================================== + +export interface ChannelDetailsFragment { + __typename: "Channel"; + id: string; + name: string; + slug: string; + currencyCode: string; +} diff --git a/src/channels/types/ChannelErrorFragment.ts b/src/channels/types/ChannelErrorFragment.ts new file mode 100644 index 000000000..5e647c72c --- /dev/null +++ b/src/channels/types/ChannelErrorFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ChannelErrorFragment +// ==================================================== + +export interface ChannelErrorFragment { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} diff --git a/src/channels/types/ChannelUpdate.ts b/src/channels/types/ChannelUpdate.ts new file mode 100644 index 000000000..26ef58efd --- /dev/null +++ b/src/channels/types/ChannelUpdate.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelUpdateInput, ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ChannelUpdate +// ==================================================== + +export interface ChannelUpdate_channelUpdate_channel { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface ChannelUpdate_channelUpdate_errors { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} + +export interface ChannelUpdate_channelUpdate { + __typename: "ChannelUpdate"; + channel: ChannelUpdate_channelUpdate_channel | null; + errors: ChannelUpdate_channelUpdate_errors[]; +} + +export interface ChannelUpdate { + channelUpdate: ChannelUpdate_channelUpdate | null; +} + +export interface ChannelUpdateVariables { + id: string; + input: ChannelUpdateInput; +} diff --git a/src/channels/types/Channels.ts b/src/channels/types/Channels.ts new file mode 100644 index 000000000..eb551cf26 --- /dev/null +++ b/src/channels/types/Channels.ts @@ -0,0 +1,20 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: Channels +// ==================================================== + +export interface Channels_channels { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} + +export interface Channels { + channels: Channels_channels[] | null; +} diff --git a/src/channels/urls.ts b/src/channels/urls.ts new file mode 100644 index 000000000..9dab6f4ce --- /dev/null +++ b/src/channels/urls.ts @@ -0,0 +1,33 @@ +import { stringify as stringifyQs } from "qs"; +import urlJoin from "url-join"; + +import { Dialog, Filters, SingleAction, Sort } from "../types"; + +export enum ChannelsListUrlFiltersEnum { + query = "query" +} +export enum ChannelsListUrlSortField { + name = "name" +} +export type ChannelsListUrlSort = Sort; +export type ChannelsListUrlFilters = Filters; +export type ChannelsListUrlDialog = "remove"; +export type ChannelsListUrlQueryParams = Dialog & + ChannelsListUrlFilters & + ChannelsListUrlSort & + SingleAction; + +export const channelsSection = "/channels/"; + +export const channelsListPath = channelsSection; + +export const channelsListUrl = (params?: ChannelsListUrlQueryParams) => + channelsListPath + "?" + stringifyQs(params); + +export const channelAddPath = urlJoin(channelsSection, "add"); +export const channelAddUrl = channelAddPath; + +export const channelPath = (id: string) => urlJoin(channelsSection, id); + +export const channelUrl = (id: string, params?: ChannelsListUrlQueryParams) => + channelPath(encodeURIComponent(id)) + "?" + stringifyQs(params); diff --git a/src/channels/utils.ts b/src/channels/utils.ts new file mode 100644 index 000000000..c2ec5a30f --- /dev/null +++ b/src/channels/utils.ts @@ -0,0 +1,306 @@ +import { Channels_channels } from "@saleor/channels/types/Channels"; +import { CollectionDetails_collection } from "@saleor/collections/types/CollectionDetails"; +import { SaleDetails_sale } from "@saleor/discounts/types/SaleDetails"; +import { VoucherDetails_voucher } from "@saleor/discounts/types/VoucherDetails"; +import { RequireOnlyOne } from "@saleor/misc"; +import { ProductDetails_product } from "@saleor/products/types/ProductDetails"; +import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails"; +import { ShippingZone_shippingZone_shippingMethods_channelListings } from "@saleor/shipping/types/ShippingZone"; +import { uniqBy } from "lodash"; + +export interface Channel { + id: string; + name: string; +} + +export interface ChannelData { + id: string; + isPublished: boolean; + name: string; + publicationDate: string | null; + currency: string; + price: string; + costPrice: string; + availableForPurchase: string; + isAvailableForPurchase: boolean; + visibleInListings: boolean; +} + +export interface ChannelPriceData { + id: string; + name: string; + currency: string; + price: string; + costPrice?: string; +} + +export interface IChannelPriceArgs { + price: string; + costPrice: string; +} +export type ChannelPriceArgs = RequireOnlyOne< + IChannelPriceArgs, + "price" | "costPrice" +>; + +export interface ChannelVoucherData { + id: string; + name: string; + discountValue: string; + currency: string; + minSpent: string; +} + +export interface ChannelSaleData { + id: string; + name: string; + discountValue: string; + currency: string; +} + +export interface ChannelCollectionData { + id: string; + isPublished: boolean; + name: string; + publicationDate: string | null; +} + +export const createCollectionChannels = (data?: Channels_channels[]) => + data?.map(channel => ({ + id: channel.id, + isPublished: false, + name: channel.name, + publicationDate: null + })); + +export const createVoucherChannels = (data?: Channels_channels[]) => + data?.map(channel => ({ + currency: channel.currencyCode, + discountValue: "", + id: channel.id, + minSpent: "", + name: channel.name + })); + +export const createSaleChannels = (data?: Channels_channels[]) => + data?.map(channel => ({ + currency: channel.currencyCode, + discountValue: "", + id: channel.id, + name: channel.name + })); + +export const createVariantChannels = ( + data?: ProductVariantDetails_productVariant +): ChannelPriceData[] => { + if (data) { + const productChannels = data?.product.channelListings.map(listing => ({ + costPrice: "", + currency: listing.channel.currencyCode, + id: listing.channel.id, + name: listing.channel.name, + price: "" + })); + const variantChannels = data?.channelListings.map(listing => ({ + costPrice: listing.costPrice?.amount.toString() || "", + currency: listing.channel.currencyCode, + id: listing.channel.id, + name: listing.channel.name, + price: listing.price.amount.toString() + })); + return uniqBy([...variantChannels, ...productChannels], obj => obj.id); + } + return []; +}; + +export const createChannelsDataWithSaleDiscountPrice = ( + saleData?: SaleDetails_sale, + data?: Channels_channels[] +): ChannelSaleData[] => { + if (data && saleData?.channelListings) { + const dataArr = createSaleChannels(data); + + const saleDataArr = createChannelsDataFromSale(saleData); + return uniqBy([...saleDataArr, ...dataArr], obj => obj.id); + } + return []; +}; + +export const createChannelsDataWithDiscountPrice = ( + voucherData?: VoucherDetails_voucher, + data?: Channels_channels[] +): ChannelVoucherData[] => { + if (data && voucherData?.channelListings) { + const dataArr = createVoucherChannels(data); + + const voucherDataArr = createChannelsDataFromVoucher(voucherData); + return uniqBy([...voucherDataArr, ...dataArr], obj => obj.id); + } + return []; +}; + +export const createChannelsData = (data?: Channels_channels[]): ChannelData[] => + data?.map(channel => ({ + availableForPurchase: null, + costPrice: "", + currency: channel.currencyCode, + id: channel.id, + isAvailableForPurchase: false, + isPublished: false, + name: channel.name, + price: "", + publicationDate: null, + visibleInListings: false + })) || []; + +export const createChannelsDataWithPrice = ( + productData?: ProductDetails_product, + data?: Channels_channels[] +): ChannelData[] => { + if (data && productData?.channelListings) { + const dataArr = createChannelsData(data); + + const productDataArr = createChannelsDataFromProduct(productData); + return uniqBy([...productDataArr, ...dataArr], obj => obj.id); + } + return []; +}; + +export const createShippingChannels = ( + data?: Channels_channels[] +): ChannelShippingData[] => + data?.map(channel => ({ + currency: channel.currencyCode, + id: channel.id, + maxValue: "", + minValue: "", + name: channel.name, + price: "" + })) || []; + +export const createShippingChannelsFromRate = ( + data?: ShippingZone_shippingZone_shippingMethods_channelListings[] +): ChannelShippingData[] => + data?.map(channelData => ({ + currency: channelData.channel.currencyCode, + id: channelData.channel.id, + maxValue: channelData.maximumOrderPrice + ? channelData.maximumOrderPrice.amount.toString() + : "", + minValue: channelData.minimumOrderPrice + ? channelData.minimumOrderPrice.amount.toString() + : "", + name: channelData.channel.name, + price: channelData.price ? channelData.price.amount.toString() : "" + })) || []; + +export const createCollectionChannelsData = ( + collectionData?: CollectionDetails_collection +) => { + if (collectionData?.channelListings) { + const collectionDataArr = collectionData?.channelListings.map(listing => ({ + id: listing.channel.id, + isPublished: listing.isPublished, + name: listing.channel.name, + publicationDate: listing.publicationDate + })); + return collectionDataArr; + } +}; + +export interface ChannelShippingData { + currency: string; + id: string; + minValue: string; + name: string; + maxValue: string; + price: string; +} + +export const createChannelsDataFromVoucher = ( + voucherData?: VoucherDetails_voucher +) => + voucherData?.channelListings?.map(option => ({ + currency: option.channel.currencyCode || option?.minSpent?.currency || "", + discountValue: option.discountValue.toString() || "", + id: option.channel.id, + minSpent: option?.minSpent?.amount.toString() || "", + name: option.channel.name + })) || []; + +export const createChannelsDataFromSale = (saleData?: SaleDetails_sale) => + saleData?.channelListings?.map(option => ({ + currency: option.channel.currencyCode || "", + discountValue: option.discountValue.toString() || "", + id: option.channel.id, + name: option.channel.name + })) || []; + +export const createChannelsDataFromProduct = ( + productData?: ProductDetails_product +) => + productData?.channelListings?.map(option => { + const variantChannel = productData.variants[0]?.channelListings.find( + listing => listing.channel.id === option.channel.id + ); + const price = variantChannel?.price; + const costPrice = variantChannel?.costPrice; + return { + availableForPurchase: option?.availableForPurchase, + costPrice: costPrice ? costPrice.amount.toString() : "", + currency: price ? price.currency : "", + id: option.channel.id, + isAvailableForPurchase: !!option?.isAvailableForPurchase, + isPublished: option.isPublished, + name: option.channel.name, + price: price ? price.amount.toString() : "", + publicationDate: option.publicationDate, + visibleInListings: !!option.visibleInListings + }; + }) || []; + +export const createSortedChannelsDataFromProduct = ( + productData?: ProductDetails_product +) => + createChannelsDataFromProduct(productData).sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedChannelsData = (data?: Channels_channels[]) => + createChannelsData(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedShippingChannels = (data?: Channels_channels[]) => + createShippingChannels(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedShippingChannelsFromRate = ( + data?: ShippingZone_shippingZone_shippingMethods_channelListings[] +) => + createShippingChannelsFromRate(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedVoucherData = (data?: Channels_channels[]) => + createVoucherChannels(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedSaleData = (data?: Channels_channels[]) => + createSaleChannels(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedChannelsDataFromVoucher = ( + data?: VoucherDetails_voucher +) => + createChannelsDataFromVoucher(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + +export const createSortedChannelsDataFromSale = (data?: SaleDetails_sale) => + createChannelsDataFromSale(data)?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); diff --git a/src/channels/views/ChannelCreate/ChannelCreate.tsx b/src/channels/views/ChannelCreate/ChannelCreate.tsx new file mode 100644 index 000000000..24f89d2ca --- /dev/null +++ b/src/channels/views/ChannelCreate/ChannelCreate.tsx @@ -0,0 +1,92 @@ +import AppHeader from "@saleor/components/AppHeader"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { commonMessages } from "@saleor/intl"; +import { sectionNames } from "@saleor/intl"; +import currencyCodes from "currency-codes"; +import React from "react"; +import { useIntl } from "react-intl"; + +import { ChannelCreateInput } from "../../../types/globalTypes"; +import { useChannelCreateMutation } from "../../mutations"; +import ChannelDetailsPage from "../../pages/ChannelDetailsPage"; +import { ChannelCreate } from "../../types/ChannelCreate"; +import { channelPath, channelsListUrl } from "../../urls"; + +export const ChannelCreateView = ({}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const handleBack = () => navigate(channelsListUrl()); + + const onSubmit = (data: ChannelCreate) => { + if (!data.channelCreate.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate(channelPath(data.channelCreate.channel.id)); + } + }; + + const [createChannel, createChannelOpts] = useChannelCreateMutation({ + onCompleted: onSubmit + }); + + const handleSubmit = (data: ChannelCreateInput) => + createChannel({ + variables: { + input: { ...data, currencyCode: data.currencyCode.toUpperCase() } + } + }); + + const currencyCodeChoices = currencyCodes.data.map(currencyData => ({ + label: intl.formatMessage( + { + defaultMessage: "{code} - {countries}", + description: "currency code select" + }, + { + code: currencyData.code, + countries: currencyData.countries.join(",") + } + ), + value: currencyData.code + })); + + return ( + <> + + + + {intl.formatMessage(sectionNames.channels)} + + + + + + ); +}; + +export default ChannelCreateView; diff --git a/src/channels/views/ChannelCreate/index.ts b/src/channels/views/ChannelCreate/index.ts new file mode 100644 index 000000000..9439b1261 --- /dev/null +++ b/src/channels/views/ChannelCreate/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelCreate"; +export { default } from "./ChannelCreate"; diff --git a/src/channels/views/ChannelDetails/ChannelDetails.tsx b/src/channels/views/ChannelDetails/ChannelDetails.tsx new file mode 100644 index 000000000..f214252cf --- /dev/null +++ b/src/channels/views/ChannelDetails/ChannelDetails.tsx @@ -0,0 +1,125 @@ +import AppHeader from "@saleor/components/AppHeader"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { ChannelErrorFragment } from "@saleor/fragments/types/ChannelErrorFragment"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { commonMessages } from "@saleor/intl"; +import { sectionNames } from "@saleor/intl"; +import getChannelsErrorMessage from "@saleor/utils/errors/channels"; +import React from "react"; +import { useIntl } from "react-intl"; + +import { ChannelUpdateInput } from "../../../types/globalTypes"; +import { + useChannelActivateMutation, + useChannelDeactivateMutation, + useChannelUpdateMutation +} from "../../mutations"; +import ChannelDetailsPage from "../../pages/ChannelDetailsPage"; +import { useChannelDetails } from "../../queries"; +import { ChannelUpdate } from "../../types/ChannelUpdate"; +import { channelsListUrl } from "../../urls"; + +interface ChannelDetailsProps { + id: string; +} + +export const ChannelDetails: React.FC = ({ id }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const handleBack = () => navigate(channelsListUrl()); + + const onSubmit = (data: ChannelUpdate) => { + if (!data.channelUpdate.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + handleBack(); + } + }; + + const handleError = (error: ChannelErrorFragment) => { + notify({ + status: "error", + text: getChannelsErrorMessage(error, intl) + }); + }; + + const { data, loading } = useChannelDetails({ + displayLoader: true, + variables: { id } + }); + + const [updateChannel, updateChannelOpts] = useChannelUpdateMutation({ + onCompleted: onSubmit + }); + + const [activateChannel, activateChannelOpts] = useChannelActivateMutation({ + onCompleted: data => { + const errors = data.channelActivate.errors; + if (errors.length) { + errors.forEach(error => handleError(error)); + } + } + }); + + const [ + deactivateChannel, + deactivateChannelOpts + ] = useChannelDeactivateMutation({ + onCompleted: data => { + const errors = data.channelDeactivate.errors; + if (errors.length) { + errors.forEach(error => handleError(error)); + } + } + }); + + const handleSubmit = (data: ChannelUpdateInput) => + updateChannel({ + variables: { + id, + input: { name: data.name, slug: data.slug } + } + }); + + return ( + <> + + + + {intl.formatMessage(sectionNames.channels)} + + + + data?.channel?.isActive + ? deactivateChannel({ variables: { id } }) + : activateChannel({ variables: { id } }) + } + saveButtonBarState={updateChannelOpts.status} + /> + + + ); +}; + +export default ChannelDetails; diff --git a/src/channels/views/ChannelDetails/index.ts b/src/channels/views/ChannelDetails/index.ts new file mode 100644 index 000000000..93fb759fa --- /dev/null +++ b/src/channels/views/ChannelDetails/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelDetails"; +export { default } from "./ChannelDetails"; diff --git a/src/channels/views/ChannelsList/ChannelsList.tsx b/src/channels/views/ChannelsList/ChannelsList.tsx new file mode 100644 index 000000000..52b7fcf87 --- /dev/null +++ b/src/channels/views/ChannelsList/ChannelsList.tsx @@ -0,0 +1,116 @@ +import { configurationMenuUrl } from "@saleor/configuration"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import getChannelsErrorMessage from "@saleor/utils/errors/channels"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import React from "react"; +import { useIntl } from "react-intl"; + +import ChannelDeleteDialog from "../../components/ChannelDeleteDialog"; +import { useChannelDeleteMutation } from "../../mutations"; +import ChannelsListPage from "../../pages/ChannelsListPage"; +import { useChannelsList } from "../../queries"; +import { ChannelDelete } from "../../types/ChannelDelete"; +import { + channelAddUrl, + channelsListUrl, + ChannelsListUrlDialog, + ChannelsListUrlQueryParams, + channelUrl +} from "../../urls"; + +interface ChannelsListProps { + params: ChannelsListUrlQueryParams; +} + +export const ChannelsList: React.FC = ({ params }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data, refetch } = useChannelsList({ displayLoader: true }); + + const selectedChannel = data?.channels.find( + channel => channel.id === params?.id + ); + + const [openModal, closeModal] = createDialogActionHandlers< + ChannelsListUrlDialog, + ChannelsListUrlQueryParams + >(navigate, channelsListUrl, params); + + const onCompleted = (data: ChannelDelete) => { + const errors = data.channelDelete.errors; + if (errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage({ + defaultMessage: "Channel deleted" + }) + }); + refetch(); + closeModal(); + } else { + errors.map(error => + notify({ + status: "error", + text: getChannelsErrorMessage(error, intl) + }) + ); + } + }; + + const [deleteChannel, deleteChannelOpts] = useChannelDeleteMutation({ + onCompleted + }); + + const channelsChoices = params.id + ? data?.channels + ?.filter( + channel => + channel.id !== params.id && + channel.currencyCode === selectedChannel.currencyCode + ) + .map(channel => ({ + label: channel.name, + value: channel.id + })) + : []; + + const navigateToChannelCreate = () => navigate(channelAddUrl); + + const handleRemoveConfirm = (id: string) => + deleteChannel({ + variables: { id: params.id, input: { targetChannel: id } } + }); + + return ( + <> + navigate(configurationMenuUrl)} + onRowClick={id => () => navigate(channelUrl(id))} + onRemove={id => + openModal("remove", { + id + }) + } + /> + + {!!selectedChannel && ( + navigate(channelsListUrl())} + onClose={closeModal} + onConfirm={handleRemoveConfirm} + /> + )} + + ); +}; + +ChannelsList.displayName = "ChannelsList"; +export default ChannelsList; diff --git a/src/channels/views/ChannelsList/index.ts b/src/channels/views/ChannelsList/index.ts new file mode 100644 index 000000000..1a78a4520 --- /dev/null +++ b/src/channels/views/ChannelsList/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsList"; +export { default } from "./ChannelsList"; diff --git a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx index b4fb94166..1c344628d 100644 --- a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx +++ b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx @@ -1,4 +1,6 @@ +import { ChannelCollectionData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import { AvailabilityCard } from "@saleor/components/AvailabilityCard"; import { CardSpacer } from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { Container } from "@saleor/components/Container"; @@ -7,9 +9,8 @@ import Metadata from "@saleor/components/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SeoForm from "@saleor/components/SeoForm"; -import VisibilityCard from "@saleor/components/VisibilityCard"; -import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; -import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import { CollectionChannelListingErrorFragment } from "@saleor/fragments/types/CollectionChannelListingErrorFragment"; +import { CollectionErrorFragment } from "@saleor/fragments/types/CollectionErrorFragment"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; import React from "react"; @@ -20,25 +21,38 @@ import { CollectionImage } from "../CollectionImage/CollectionImage"; import CollectionCreateForm, { CollectionCreateData } from "./form"; export interface CollectionCreatePageProps { + channelsCount: number; + channelsErrors: CollectionChannelListingErrorFragment[]; + currentChannels: ChannelCollectionData[]; disabled: boolean; - errors: ProductErrorFragment[]; + errors: CollectionErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; onSubmit: (data: CollectionCreateData) => SubmitPromise; + onChannelsChange: (data: ChannelCollectionData[]) => void; + openChannelsModal: () => void; } const CollectionCreatePage: React.FC = ({ + channelsCount, + channelsErrors, + currentChannels = [], disabled, errors, saveButtonBarState, onBack, + onChannelsChange, + openChannelsModal, onSubmit }: CollectionCreatePageProps) => { const intl = useIntl(); - const localizeDate = useDateLocalize(); return ( - + {({ change, data, handlers, hasChanged, submit }) => ( @@ -115,30 +129,25 @@ const CollectionCreatePage: React.FC = ({
-
diff --git a/src/collections/components/CollectionCreatePage/form.tsx b/src/collections/components/CollectionCreatePage/form.tsx index 9857022ee..f76021470 100644 --- a/src/collections/components/CollectionCreatePage/form.tsx +++ b/src/collections/components/CollectionCreatePage/form.tsx @@ -1,4 +1,6 @@ import { OutputData } from "@editorjs/editorjs"; +import { ChannelCollectionData } from "@saleor/channels/utils"; +import { createChannelsChangeHandler } from "@saleor/collections/utils"; import { MetadataFormData } from "@saleor/components/Metadata"; import { RichTextEditorChange } from "@saleor/components/RichTextEditor"; import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm"; @@ -13,10 +15,9 @@ export interface CollectionCreateFormData extends MetadataFormData { value: string; }; backgroundImageAlt: string; + channelListings: ChannelCollectionData[]; name: string; slug: string; - publicationDate: string; - isPublished: boolean; seoDescription: string; seoTitle: string; } @@ -27,6 +28,10 @@ export interface CollectionCreateData extends CollectionCreateFormData { interface CollectionCreateHandlers { changeMetadata: FormChange; changeDescription: RichTextEditorChange; + changeChannels: ( + id: string, + data: Omit + ) => void; } export interface UseCollectionCreateFormResult { change: FormChange; @@ -37,11 +42,15 @@ export interface UseCollectionCreateFormResult { } export interface CollectionCreateFormProps { + currentChannels: ChannelCollectionData[]; + setChannels: (data: ChannelCollectionData[]) => void; children: (props: UseCollectionCreateFormResult) => React.ReactNode; onSubmit: (data: CollectionCreateData) => SubmitPromise; } function useCollectionCreateForm( + currentChannels: ChannelCollectionData[], + setChannels: (data: ChannelCollectionData[]) => void, onSubmit: (data: CollectionCreateData) => SubmitPromise ): UseCollectionCreateFormResult { const [changed, setChanged] = React.useState(false); @@ -53,11 +62,10 @@ function useCollectionCreateForm( value: null }, backgroundImageAlt: "", - isPublished: false, + channelListings: currentChannels, metadata: [], name: "", privateMetadata: [], - publicationDate: "", seoDescription: "", seoTitle: "", slug: "" @@ -83,12 +91,19 @@ function useCollectionCreateForm( description: description.current }); + const handleChannelChange = createChannelsChangeHandler( + currentChannels, + setChannels, + triggerChange + ); + const submit = () => handleFormSubmit(getData(), onSubmit, setChanged); return { change: handleChange, data: getData(), handlers: { + changeChannels: handleChannelChange, changeDescription, changeMetadata }, @@ -98,10 +113,12 @@ function useCollectionCreateForm( } const CollectionCreateForm: React.FC = ({ + currentChannels, + setChannels, children, onSubmit }) => { - const props = useCollectionCreateForm(onSubmit); + const props = useCollectionCreateForm(currentChannels, setChannels, onSubmit); return
{children(props)}
; }; diff --git a/src/collections/components/CollectionDetails/CollectionDetails.tsx b/src/collections/components/CollectionDetails/CollectionDetails.tsx index 90187bcd0..7e3db7e73 100644 --- a/src/collections/components/CollectionDetails/CollectionDetails.tsx +++ b/src/collections/components/CollectionDetails/CollectionDetails.tsx @@ -7,7 +7,7 @@ import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor, { RichTextEditorChange } from "@saleor/components/RichTextEditor"; -import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; +import { CollectionErrorFragment } from "@saleor/fragments/types/CollectionErrorFragment"; import { commonMessages } from "@saleor/intl"; import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; import React from "react"; @@ -19,7 +19,7 @@ export interface CollectionDetailsProps { name: string; }; disabled: boolean; - errors: ProductErrorFragment[]; + errors: CollectionErrorFragment[]; onChange: (event: React.ChangeEvent) => void; onDescriptionChange: RichTextEditorChange; } diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index 34d214dfc..6db58a13c 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -1,24 +1,21 @@ +import { ChannelCollectionData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; +import { AvailabilityCard } from "@saleor/components/AvailabilityCard"; import { CardSpacer } from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { Container } from "@saleor/components/Container"; -import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; -import FormSpacer from "@saleor/components/FormSpacer"; import Grid from "@saleor/components/Grid"; -import Hr from "@saleor/components/Hr"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SeoForm from "@saleor/components/SeoForm"; -import VisibilityCard from "@saleor/components/VisibilityCard"; -import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; -import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import { CollectionChannelListingErrorFragment } from "@saleor/fragments/types/CollectionChannelListingErrorFragment"; +import { CollectionErrorFragment } from "@saleor/fragments/types/CollectionErrorFragment"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; import React from "react"; import { useIntl } from "react-intl"; -import { maybe } from "../../../misc"; import { ListActions, PageListProps } from "../../../types"; import { CollectionDetails_collection } from "../../types/CollectionDetails"; import CollectionDetails from "../CollectionDetails/CollectionDetails"; @@ -27,38 +24,50 @@ import CollectionProducts from "../CollectionProducts/CollectionProducts"; import CollectionUpdateForm, { CollectionUpdateData } from "./form"; export interface CollectionDetailsPageProps extends PageListProps, ListActions { + channelsCount: number; + channelsErrors: CollectionChannelListingErrorFragment[]; collection: CollectionDetails_collection; - errors: ProductErrorFragment[]; - isFeatured: boolean; + currentChannels: ChannelCollectionData[]; + errors: CollectionErrorFragment[]; + hasChannelChanged: boolean; saveButtonBarState: ConfirmButtonTransitionState; + selectedChannel: string; onBack: () => void; onCollectionRemove: () => void; onImageDelete: () => void; onImageUpload: (file: File) => void; onProductUnassign: (id: string, event: React.MouseEvent) => void; onSubmit: (data: CollectionUpdateData) => SubmitPromise; + onChannelsChange: (data: ChannelCollectionData[]) => void; + openChannelsModal: () => void; } const CollectionDetailsPage: React.FC = ({ + channelsCount, + channelsErrors, collection, + currentChannels = [], disabled, errors, - isFeatured, + hasChannelChanged, saveButtonBarState, + selectedChannel, onBack, onCollectionRemove, onImageDelete, onImageUpload, onSubmit, + onChannelsChange, + openChannelsModal, ...collectionProductsProps }: CollectionDetailsPageProps) => { const intl = useIntl(); - const localizeDate = useDateLocalize(); return ( {({ change, data, handlers, hasChanged, submit }) => ( @@ -66,7 +75,7 @@ const CollectionDetailsPage: React.FC = ({ {intl.formatMessage(sectionNames.collections)} - collection.name)} /> +
= ({ collection.backgroundImage)} + image={collection?.backgroundImage} onImageDelete={onImageDelete} onImageUpload={onImageUpload} onChange={change} @@ -89,6 +98,8 @@ const CollectionDetailsPage: React.FC = ({ @@ -105,55 +116,38 @@ const CollectionDetailsPage: React.FC = ({ slug={data.slug} slugPlaceholder={data.name} title={data.seoTitle} - titlePlaceholder={maybe(() => collection.name)} + titlePlaceholder={collection?.name} onChange={change} />
- - -
- -
+ errors={channelsErrors} + selectedChannelsCount={data.channelListings.length} + allChannelsCount={channelsCount} + channels={data.channelListings} + disabled={disabled} + onChange={handlers.changeChannels} + openModal={openChannelsModal} + />
+ ) => void; } export interface UseCollectionUpdateFormResult { change: FormChange; @@ -40,26 +43,26 @@ export interface UseCollectionUpdateFormResult { export interface CollectionUpdateFormProps { children: (props: UseCollectionUpdateFormResult) => React.ReactNode; collection: CollectionDetails_collection; - isFeatured: boolean; + currentChannels: ChannelCollectionData[]; + setChannels: (data: ChannelCollectionData[]) => void; onSubmit: (data: CollectionUpdateData) => Promise; } function useCollectionUpdateForm( collection: CollectionDetails_collection, - onSubmit: (data: CollectionUpdateData) => Promise, - isFeatured: boolean + currentChannels: ChannelCollectionData[], + setChannels: (data: ChannelCollectionData[]) => void, + onSubmit: (data: CollectionUpdateData) => Promise ): UseCollectionUpdateFormResult { const [changed, setChanged] = React.useState(false); const triggerChange = () => setChanged(true); const form = useForm({ backgroundImageAlt: collection?.backgroundImage?.alt || "", - isFeatured, - isPublished: !!collection?.isPublished, + channelListings: currentChannels, metadata: collection?.metadata?.map(mapMetadataItemToInput), name: collection?.name || "", privateMetadata: collection?.privateMetadata?.map(mapMetadataItemToInput), - publicationDate: collection?.publicationDate || "", seoDescription: collection?.seoDescription || "", seoTitle: collection?.seoTitle || "", slug: collection?.slug || "" @@ -89,16 +92,22 @@ function useCollectionUpdateForm( const getSubmitData = (): CollectionUpdateData => ({ ...getData(), - ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified), - ...getPublicationData(form.data) + ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified) }); + const handleChannelChange = createChannelsChangeHandler( + currentChannels, + setChannels, + triggerChange + ); + const submit = () => handleFormSubmit(getSubmitData(), onSubmit, setChanged); return { change: handleChange, data: getData(), handlers: { + changeChannels: handleChannelChange, changeDescription, changeMetadata }, @@ -108,12 +117,18 @@ function useCollectionUpdateForm( } const CollectionUpdateForm: React.FC = ({ - children, collection, - isFeatured, + currentChannels, + setChannels, + children, onSubmit }) => { - const props = useCollectionUpdateForm(collection, onSubmit, isFeatured); + const props = useCollectionUpdateForm( + collection, + currentChannels, + setChannels, + onSubmit + ); return
{children(props)}
; }; diff --git a/src/collections/components/CollectionList/CollectionList.tsx b/src/collections/components/CollectionList/CollectionList.tsx index a80b9cd50..3d0937d29 100644 --- a/src/collections/components/CollectionList/CollectionList.tsx +++ b/src/collections/components/CollectionList/CollectionList.tsx @@ -4,10 +4,10 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import { CollectionListUrlSortField } from "@saleor/collections/urls"; +import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown"; import Checkbox from "@saleor/components/Checkbox"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; import TableCellHeader from "@saleor/components/TableCellHeader"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; @@ -15,7 +15,7 @@ import { maybe, renderCollection } from "@saleor/misc"; import { ListActions, ListProps, SortPage } from "@saleor/types"; import { getArrowDirection } from "@saleor/utils/sort"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedMessage } from "react-intl"; import { CollectionList_collections_edges_node } from "../../types/CollectionList"; @@ -49,12 +49,15 @@ interface CollectionListProps ListActions, SortPage { collections: CollectionList_collections_edges_node[]; + channelsCount: number; + selectedChannel: string; } const numberOfColumns = 4; const CollectionList: React.FC = props => { const { + channelsCount, collections, disabled, settings, @@ -67,13 +70,13 @@ const CollectionList: React.FC = props => { pageInfo, isChecked, selected, + selectedChannel, toggle, toggleAll, toolbar } = props; const classes = useStyles(props); - const intl = useIntl(); return ( @@ -143,6 +146,9 @@ const CollectionList: React.FC = props => { collections, collection => { const isSelected = collection ? isChecked(collection.id) : false; + const channel = collection?.channelListings.find( + listing => listing.channel.id === selectedChannel + ); return ( = props => { collection.isPublished)} + data-test="availability" + data-test-availability={!!collection?.channelListings?.length} > - {maybe( - () => ( - - ), + {collection && !collection?.channelListings?.length ? ( + "-" + ) : collection?.channelListings !== undefined ? ( + + ) : ( )} diff --git a/src/collections/components/CollectionListPage/CollectionListPage.tsx b/src/collections/components/CollectionListPage/CollectionListPage.tsx index 0a709ef05..b841695b3 100644 --- a/src/collections/components/CollectionListPage/CollectionListPage.tsx +++ b/src/collections/components/CollectionListPage/CollectionListPage.tsx @@ -1,14 +1,16 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; +import makeStyles from "@material-ui/core/styles/makeStyles"; import { CollectionListUrlSortField } from "@saleor/collections/urls"; +import CardMenu from "@saleor/components/CardMenu"; import { Container } from "@saleor/components/Container"; -import FilterBar from "@saleor/components/FilterBar"; import PageHeader from "@saleor/components/PageHeader"; +import SearchBar from "@saleor/components/SearchBar"; import { sectionNames } from "@saleor/intl"; import { - FilterPageProps, ListActions, PageListProps, + SearchPageProps, SortPage, TabPageProps } from "@saleor/types"; @@ -17,44 +19,64 @@ import { FormattedMessage, useIntl } from "react-intl"; import { CollectionList_collections_edges_node } from "../../types/CollectionList"; import CollectionList from "../CollectionList/CollectionList"; -import { - CollectionFilterKeys, - CollectionListFilterOpts, - createFilterStructure -} from "./filters"; export interface CollectionListPageProps extends PageListProps, ListActions, - FilterPageProps, + SearchPageProps, SortPage, TabPageProps { collections: CollectionList_collections_edges_node[]; + channelsCount: number; + selectedChannel: string; + onSettingsOpen?: () => void; } +const useStyles = makeStyles( + theme => ({ + settings: { + marginRight: theme.spacing(2) + } + }), + { name: "CollectionListPage" } +); + const CollectionListPage: React.FC = ({ - currencySymbol, + channelsCount, currentTab, disabled, - filterOpts, initialSearch, onAdd, onAll, - onFilterChange, onSearchChange, + onSettingsOpen, onTabChange, onTabDelete, onTabSave, + selectedChannel, tabs, ...listProps }) => { const intl = useIntl(); - - const structure = createFilterStructure(intl, filterOpts); + const classes = useStyles({}); return ( + {!!onSettingsOpen && ( + + )} - - + ); diff --git a/src/collections/components/CollectionProducts/CollectionProducts.tsx b/src/collections/components/CollectionProducts/CollectionProducts.tsx index d5dd384d9..23f77ac07 100644 --- a/src/collections/components/CollectionProducts/CollectionProducts.tsx +++ b/src/collections/components/CollectionProducts/CollectionProducts.tsx @@ -8,10 +8,10 @@ import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; import CardTitle from "@saleor/components/CardTitle"; +import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown"; import Checkbox from "@saleor/components/Checkbox"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; import TableCellAvatar, { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar"; @@ -57,6 +57,8 @@ const useStyles = makeStyles( export interface CollectionProductsProps extends PageListProps, ListActions { collection: CollectionDetails_collection; + channelsCount: number; + selectedChannel: string; onProductUnassign: (id: string, event: React.MouseEvent) => void; } @@ -64,6 +66,7 @@ const numberOfColumns = 5; const CollectionProducts: React.FC = props => { const { + channelsCount, collection, disabled, onAdd, @@ -72,6 +75,7 @@ const CollectionProducts: React.FC = props => { onProductUnassign, onRowClick, pageInfo, + selectedChannel, isChecked, selected, toggle, @@ -139,8 +143,8 @@ const CollectionProducts: React.FC = props => { @@ -149,9 +153,9 @@ const CollectionProducts: React.FC = props => { pageInfo.hasNextPage)} + hasNextPage={pageInfo?.hasNextPage} onNextPage={onNextPage} - hasPreviousPage={maybe(() => pageInfo.hasPreviousPage)} + hasPreviousPage={pageInfo?.hasPreviousPage} onPreviousPage={onPreviousPage} /> @@ -161,6 +165,10 @@ const CollectionProducts: React.FC = props => { maybe(() => collection.products.edges.map(edge => edge.node)), product => { const isSelected = product ? isChecked(product.id) : false; + const channel = + product?.channelListings.find( + listing => listing.channel.id === selectedChannel + ) || product?.channelListings[0]; return ( = props => { )} - - {maybe( - () => ( - - ), + + {product && !product?.channelListings?.length ? ( + "-" + ) : product?.channelListings !== undefined ? ( + + ) : ( )} diff --git a/src/collections/fixtures.ts b/src/collections/fixtures.ts index 4e515428f..6a6b8436e 100644 --- a/src/collections/fixtures.ts +++ b/src/collections/fixtures.ts @@ -7,8 +7,19 @@ const content = richTextEditorFixtures.richTextEditor; export const collections: CollectionList_collections_edges_node[] = [ { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "123", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjox", - isPublished: true, name: "Summer collection", products: { __typename: "ProductCountableConnection", @@ -17,8 +28,19 @@ export const collections: CollectionList_collections_edges_node[] = [ }, { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "124", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjoy", - isPublished: true, name: "Winter sale", products: { __typename: "ProductCountableConnection", @@ -27,8 +49,19 @@ export const collections: CollectionList_collections_edges_node[] = [ }, { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "125", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjoz", - isPublished: true, name: "Vintage vibes", products: { __typename: "ProductCountableConnection", @@ -37,8 +70,19 @@ export const collections: CollectionList_collections_edges_node[] = [ }, { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "126", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjoa", - isPublished: true, name: "Merry Christmas", products: { __typename: "ProductCountableConnection", @@ -47,8 +91,19 @@ export const collections: CollectionList_collections_edges_node[] = [ }, { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "127", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjob", - isPublished: true, name: "80s Miami", products: { __typename: "ProductCountableConnection", @@ -57,8 +112,19 @@ export const collections: CollectionList_collections_edges_node[] = [ }, { __typename: "Collection", + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "128", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], id: "Q29sbGVjdGlvbjoc", - isPublished: true, name: "Yellow Submarine 2019", products: { __typename: "ProductCountableConnection", @@ -79,9 +145,20 @@ export const collection: ( alt: "Alt text", url: placeholderCollectionImage }, + channelListings: [ + { + __typename: "CollectionChannelListing", + channel: { + __typename: "Channel", + id: "223", + name: "Channel" + }, + isPublished: false, + publicationDate: null + } + ], descriptionJson: JSON.stringify(content), id: "Q29sbGVjdGlvbjox", - isPublished: true, metadata: [ { __typename: "MetadataItem", @@ -96,11 +173,49 @@ export const collection: ( edges: [ { __typename: "ProductCountableEdge", - cursor: "YXJyYXljb25uZWN0aW9uOjA=", node: { __typename: "Product", + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDoxNw==", - isPublished: true, name: "Murray Inc", productType: { __typename: "ProductType", @@ -112,11 +227,49 @@ export const collection: ( }, { __typename: "ProductCountableEdge", - cursor: "YXJyYXljb25uZWN0aW9uOjE=", node: { __typename: "Product", + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDoyNw==", - isPublished: true, name: "Williams-Taylor", productType: { __typename: "ProductType", @@ -128,11 +281,49 @@ export const collection: ( }, { __typename: "ProductCountableEdge", - cursor: "YXJyYXljb25uZWN0aW9uOjI=", node: { __typename: "Product", + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDoyOQ==", - isPublished: true, name: "Hebert-Sherman", productType: { __typename: "ProductType", @@ -144,11 +335,49 @@ export const collection: ( }, { __typename: "ProductCountableEdge", - cursor: "YXJyYXljb25uZWN0aW9uOjM=", node: { __typename: "Product", + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDo1Mw==", - isPublished: true, name: "Estes, Johnson and Graham", productType: { __typename: "ProductType", @@ -167,7 +396,6 @@ export const collection: ( startCursor: "" } }, - publicationDate: "2018-08-25T18:45:54.125Z", seoDescription: "", seoTitle: "", slug: "summer-collection" diff --git a/src/collections/mutations.ts b/src/collections/mutations.ts index bf5fafa7c..bbf5ba059 100644 --- a/src/collections/mutations.ts +++ b/src/collections/mutations.ts @@ -1,10 +1,15 @@ +import { + CollectionChannelListingUpdate, + CollectionChannelListingUpdateVariables +} from "@saleor/collections/types/CollectionChannelListingUpdate"; import { collectionDetailsFragment, collectionProductFragment } from "@saleor/fragments/collections"; import { - productErrorFragment, - shopErrorFragment + collectionChannelListingErrorFragment, + collectionsErrorFragment, + productErrorFragment } from "@saleor/fragments/errors"; import makeMutation from "@saleor/hooks/makeMutation"; import gql from "graphql-tag"; @@ -17,18 +22,10 @@ import { CollectionBulkDelete, CollectionBulkDeleteVariables } from "./types/CollectionBulkDelete"; -import { - CollectionBulkPublish, - CollectionBulkPublishVariables -} from "./types/CollectionBulkPublish"; import { CollectionUpdate, CollectionUpdateVariables } from "./types/CollectionUpdate"; -import { - CollectionUpdateWithHomepage, - CollectionUpdateWithHomepageVariables -} from "./types/CollectionUpdateWithHomepage"; import { CreateCollection, CreateCollectionVariables @@ -44,14 +41,14 @@ import { const collectionUpdate = gql` ${collectionDetailsFragment} - ${productErrorFragment} + ${collectionsErrorFragment} mutation CollectionUpdate($id: ID!, $input: CollectionInput!) { collectionUpdate(id: $id, input: $input) { collection { ...CollectionDetailsFragment } - errors: productErrors { - ...ProductErrorFragment + errors: collectionErrors { + ...CollectionErrorFragment } } } @@ -61,43 +58,9 @@ export const useCollectionUpdateMutation = makeMutation< CollectionUpdateVariables >(collectionUpdate); -const collectionUpdateWithHomepage = gql` - ${collectionDetailsFragment} - ${productErrorFragment} - ${shopErrorFragment} - mutation CollectionUpdateWithHomepage( - $id: ID! - $input: CollectionInput! - $homepageId: ID - ) { - homepageCollectionUpdate(collection: $homepageId) { - errors: shopErrors { - ...ShopErrorFragment - } - shop { - homepageCollection { - id - } - } - } - collectionUpdate(id: $id, input: $input) { - collection { - ...CollectionDetailsFragment - } - errors: productErrors { - ...ProductErrorFragment - } - } - } -`; -export const useCollectionUpdateWithHomepageMutation = makeMutation< - CollectionUpdateWithHomepage, - CollectionUpdateWithHomepageVariables ->(collectionUpdateWithHomepage); - const assignCollectionProduct = gql` ${collectionProductFragment} - ${productErrorFragment} + ${collectionsErrorFragment} mutation CollectionAssignProduct( $collectionId: ID! $productIds: [ID!]! @@ -123,8 +86,8 @@ const assignCollectionProduct = gql` } } } - errors: productErrors { - ...ProductErrorFragment + errors: collectionErrors { + ...CollectionErrorFragment } } } @@ -136,14 +99,14 @@ export const useCollectionAssignProductMutation = makeMutation< const createCollection = gql` ${collectionDetailsFragment} - ${productErrorFragment} + ${collectionsErrorFragment} mutation CreateCollection($input: CollectionCreateInput!) { collectionCreate(input: $input) { collection { ...CollectionDetailsFragment } - errors: productErrors { - ...ProductErrorFragment + errors: collectionErrors { + ...CollectionErrorFragment } } } @@ -154,11 +117,11 @@ export const useCollectionCreateMutation = makeMutation< >(createCollection); const removeCollection = gql` - ${productErrorFragment} + ${collectionsErrorFragment} mutation RemoveCollection($id: ID!) { collectionDelete(id: $id) { - errors: productErrors { - ...ProductErrorFragment + errors: collectionErrors { + ...CollectionErrorFragment } } } @@ -169,7 +132,7 @@ export const useCollectionRemoveMutation = makeMutation< >(removeCollection); const unassignCollectionProduct = gql` - ${productErrorFragment} + ${collectionsErrorFragment} mutation UnassignCollectionProduct( $collectionId: ID! $productIds: [ID]! @@ -188,7 +151,6 @@ const unassignCollectionProduct = gql` edges { node { id - isPublished name productType { id @@ -207,8 +169,8 @@ const unassignCollectionProduct = gql` } } } - errors: productErrors { - ...ProductErrorFragment + errors: collectionErrors { + ...CollectionErrorFragment } } } @@ -233,17 +195,20 @@ export const useCollectionBulkDelete = makeMutation< CollectionBulkDeleteVariables >(collectionBulkDelete); -const collectionBulkPublish = gql` - ${productErrorFragment} - mutation CollectionBulkPublish($ids: [ID]!, $isPublished: Boolean!) { - collectionBulkPublish(ids: $ids, isPublished: $isPublished) { - errors: productErrors { - ...ProductErrorFragment +const collectionChannelListingUpdate = gql` + ${collectionChannelListingErrorFragment} + mutation CollectionChannelListingUpdate( + $id: ID! + $input: CollectionChannelListingUpdateInput! + ) { + collectionChannelListingUpdate(id: $id, input: $input) { + errors: collectionChannelListingErrors { + ...CollectionChannelListingErrorFragment } } } `; -export const useCollectionBulkPublish = makeMutation< - CollectionBulkPublish, - CollectionBulkPublishVariables ->(collectionBulkPublish); +export const useCollectionChannelListingUpdate = makeMutation< + CollectionChannelListingUpdate, + CollectionChannelListingUpdateVariables +>(collectionChannelListingUpdate); diff --git a/src/collections/queries.ts b/src/collections/queries.ts index c14d5b0e3..ce429bced 100644 --- a/src/collections/queries.ts +++ b/src/collections/queries.ts @@ -82,11 +82,6 @@ export const collectionDetails = gql` } } } - shop { - homepageCollection { - id - } - } } `; export const TypedCollectionDetailsQuery = TypedQuery< diff --git a/src/collections/types/CollectionAssignProduct.ts b/src/collections/types/CollectionAssignProduct.ts index a2f5dce38..37ba02405 100644 --- a/src/collections/types/CollectionAssignProduct.ts +++ b/src/collections/types/CollectionAssignProduct.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CollectionAssignProduct @@ -19,13 +19,37 @@ export interface CollectionAssignProduct_collectionAddProducts_collection_produc url: string; } +export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings_channel; +} + export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node { __typename: "Product"; id: string; - isPublished: boolean; name: string; productType: CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_productType; thumbnail: CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_thumbnail | null; + channelListings: CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_channelListings[] | null; } export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges { @@ -54,8 +78,8 @@ export interface CollectionAssignProduct_collectionAddProducts_collection { } export interface CollectionAssignProduct_collectionAddProducts_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } diff --git a/src/collections/types/CollectionChannelListingUpdate.ts b/src/collections/types/CollectionChannelListingUpdate.ts new file mode 100644 index 000000000..18c064b81 --- /dev/null +++ b/src/collections/types/CollectionChannelListingUpdate.ts @@ -0,0 +1,31 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { CollectionChannelListingUpdateInput, ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: CollectionChannelListingUpdate +// ==================================================== + +export interface CollectionChannelListingUpdate_collectionChannelListingUpdate_errors { + __typename: "CollectionChannelListingError"; + code: ProductErrorCode; + field: string | null; + message: string | null; + channels: string[] | null; +} + +export interface CollectionChannelListingUpdate_collectionChannelListingUpdate { + __typename: "CollectionChannelListingUpdate"; + errors: CollectionChannelListingUpdate_collectionChannelListingUpdate_errors[]; +} + +export interface CollectionChannelListingUpdate { + collectionChannelListingUpdate: CollectionChannelListingUpdate_collectionChannelListingUpdate | null; +} + +export interface CollectionChannelListingUpdateVariables { + id: string; + input: CollectionChannelListingUpdateInput; +} diff --git a/src/collections/types/CollectionDetails.ts b/src/collections/types/CollectionDetails.ts index 1746d93fc..8185f7624 100644 --- a/src/collections/types/CollectionDetails.ts +++ b/src/collections/types/CollectionDetails.ts @@ -6,6 +6,19 @@ // GraphQL query operation: CollectionDetails // ==================================================== +export interface CollectionDetails_collection_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CollectionDetails_collection_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionDetails_collection_channelListings_channel; +} + export interface CollectionDetails_collection_metadata { __typename: "MetadataItem"; key: string; @@ -35,13 +48,37 @@ export interface CollectionDetails_collection_products_edges_node_thumbnail { url: string; } +export interface CollectionDetails_collection_products_edges_node_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CollectionDetails_collection_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CollectionDetails_collection_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: CollectionDetails_collection_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: CollectionDetails_collection_products_edges_node_channelListings_channel; +} + export interface CollectionDetails_collection_products_edges_node { __typename: "Product"; id: string; - isPublished: boolean; name: string; productType: CollectionDetails_collection_products_edges_node_productType; thumbnail: CollectionDetails_collection_products_edges_node_thumbnail | null; + channelListings: CollectionDetails_collection_products_edges_node_channelListings[] | null; } export interface CollectionDetails_collection_products_edges { @@ -66,32 +103,20 @@ export interface CollectionDetails_collection_products { export interface CollectionDetails_collection { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionDetails_collection_channelListings[] | null; metadata: (CollectionDetails_collection_metadata | null)[]; privateMetadata: (CollectionDetails_collection_privateMetadata | null)[]; backgroundImage: CollectionDetails_collection_backgroundImage | null; slug: string; descriptionJson: any; - publicationDate: any | null; seoDescription: string | null; seoTitle: string | null; products: CollectionDetails_collection_products | null; } -export interface CollectionDetails_shop_homepageCollection { - __typename: "Collection"; - id: string; -} - -export interface CollectionDetails_shop { - __typename: "Shop"; - homepageCollection: CollectionDetails_shop_homepageCollection | null; -} - export interface CollectionDetails { collection: CollectionDetails_collection | null; - shop: CollectionDetails_shop; } export interface CollectionDetailsVariables { diff --git a/src/collections/types/CollectionList.ts b/src/collections/types/CollectionList.ts index 770a27347..6dabfda67 100644 --- a/src/collections/types/CollectionList.ts +++ b/src/collections/types/CollectionList.ts @@ -8,6 +8,19 @@ import { CollectionFilterInput, CollectionSortingInput } from "./../../types/glo // GraphQL query operation: CollectionList // ==================================================== +export interface CollectionList_collections_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CollectionList_collections_edges_node_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionList_collections_edges_node_channelListings_channel; +} + export interface CollectionList_collections_edges_node_products { __typename: "ProductCountableConnection"; totalCount: number | null; @@ -16,8 +29,8 @@ export interface CollectionList_collections_edges_node_products { export interface CollectionList_collections_edges_node { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionList_collections_edges_node_channelListings[] | null; products: CollectionList_collections_edges_node_products | null; } diff --git a/src/collections/types/CollectionUpdate.ts b/src/collections/types/CollectionUpdate.ts index f390dd7be..831a0c915 100644 --- a/src/collections/types/CollectionUpdate.ts +++ b/src/collections/types/CollectionUpdate.ts @@ -2,12 +2,25 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionInput, ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionInput, CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CollectionUpdate // ==================================================== +export interface CollectionUpdate_collectionUpdate_collection_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CollectionUpdate_collectionUpdate_collection_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionUpdate_collectionUpdate_collection_channelListings_channel; +} + export interface CollectionUpdate_collectionUpdate_collection_metadata { __typename: "MetadataItem"; key: string; @@ -29,21 +42,20 @@ export interface CollectionUpdate_collectionUpdate_collection_backgroundImage { export interface CollectionUpdate_collectionUpdate_collection { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionUpdate_collectionUpdate_collection_channelListings[] | null; metadata: (CollectionUpdate_collectionUpdate_collection_metadata | null)[]; privateMetadata: (CollectionUpdate_collectionUpdate_collection_privateMetadata | null)[]; backgroundImage: CollectionUpdate_collectionUpdate_collection_backgroundImage | null; slug: string; descriptionJson: any; - publicationDate: any | null; seoDescription: string | null; seoTitle: string | null; } export interface CollectionUpdate_collectionUpdate_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } diff --git a/src/collections/types/CollectionUpdateWithHomepage.ts b/src/collections/types/CollectionUpdateWithHomepage.ts index 7c982190f..949518ad5 100644 --- a/src/collections/types/CollectionUpdateWithHomepage.ts +++ b/src/collections/types/CollectionUpdateWithHomepage.ts @@ -2,32 +2,23 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionInput, ShopErrorCode, ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionInput, CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CollectionUpdateWithHomepage // ==================================================== -export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_errors { - __typename: "ShopError"; - code: ShopErrorCode; - field: string | null; -} - -export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_shop_homepageCollection { - __typename: "Collection"; +export interface CollectionUpdateWithHomepage_collectionUpdate_collection_channelListing_channel { + __typename: "Channel"; id: string; + name: string; } -export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_shop { - __typename: "Shop"; - homepageCollection: CollectionUpdateWithHomepage_homepageCollectionUpdate_shop_homepageCollection | null; -} - -export interface CollectionUpdateWithHomepage_homepageCollectionUpdate { - __typename: "HomepageCollectionUpdate"; - errors: CollectionUpdateWithHomepage_homepageCollectionUpdate_errors[]; - shop: CollectionUpdateWithHomepage_homepageCollectionUpdate_shop | null; +export interface CollectionUpdateWithHomepage_collectionUpdate_collection_channelListing { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionUpdateWithHomepage_collectionUpdate_collection_channelListing_channel; } export interface CollectionUpdateWithHomepage_collectionUpdate_collection_metadata { @@ -51,21 +42,20 @@ export interface CollectionUpdateWithHomepage_collectionUpdate_collection_backgr export interface CollectionUpdateWithHomepage_collectionUpdate_collection { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionUpdateWithHomepage_collectionUpdate_collection_channelListing[] | null; metadata: (CollectionUpdateWithHomepage_collectionUpdate_collection_metadata | null)[]; privateMetadata: (CollectionUpdateWithHomepage_collectionUpdate_collection_privateMetadata | null)[]; backgroundImage: CollectionUpdateWithHomepage_collectionUpdate_collection_backgroundImage | null; slug: string; descriptionJson: any; - publicationDate: any | null; seoDescription: string | null; seoTitle: string | null; } export interface CollectionUpdateWithHomepage_collectionUpdate_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } @@ -76,12 +66,10 @@ export interface CollectionUpdateWithHomepage_collectionUpdate { } export interface CollectionUpdateWithHomepage { - homepageCollectionUpdate: CollectionUpdateWithHomepage_homepageCollectionUpdate | null; collectionUpdate: CollectionUpdateWithHomepage_collectionUpdate | null; } export interface CollectionUpdateWithHomepageVariables { id: string; input: CollectionInput; - homepageId?: string | null; } diff --git a/src/collections/types/CreateCollection.ts b/src/collections/types/CreateCollection.ts index efb28ca4b..0644ca478 100644 --- a/src/collections/types/CreateCollection.ts +++ b/src/collections/types/CreateCollection.ts @@ -2,12 +2,25 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionCreateInput, ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionCreateInput, CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CreateCollection // ==================================================== +export interface CreateCollection_collectionCreate_collection_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CreateCollection_collectionCreate_collection_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CreateCollection_collectionCreate_collection_channelListings_channel; +} + export interface CreateCollection_collectionCreate_collection_metadata { __typename: "MetadataItem"; key: string; @@ -29,21 +42,20 @@ export interface CreateCollection_collectionCreate_collection_backgroundImage { export interface CreateCollection_collectionCreate_collection { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CreateCollection_collectionCreate_collection_channelListings[] | null; metadata: (CreateCollection_collectionCreate_collection_metadata | null)[]; privateMetadata: (CreateCollection_collectionCreate_collection_privateMetadata | null)[]; backgroundImage: CreateCollection_collectionCreate_collection_backgroundImage | null; slug: string; descriptionJson: any; - publicationDate: any | null; seoDescription: string | null; seoTitle: string | null; } export interface CreateCollection_collectionCreate_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } diff --git a/src/collections/types/RemoveCollection.ts b/src/collections/types/RemoveCollection.ts index 588349acb..477b3a312 100644 --- a/src/collections/types/RemoveCollection.ts +++ b/src/collections/types/RemoveCollection.ts @@ -2,15 +2,15 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: RemoveCollection // ==================================================== export interface RemoveCollection_collectionDelete_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } diff --git a/src/collections/types/UnassignCollectionProduct.ts b/src/collections/types/UnassignCollectionProduct.ts index 7104a9c85..538206d56 100644 --- a/src/collections/types/UnassignCollectionProduct.ts +++ b/src/collections/types/UnassignCollectionProduct.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductErrorCode } from "./../../types/globalTypes"; +import { CollectionErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: UnassignCollectionProduct @@ -22,7 +22,6 @@ export interface UnassignCollectionProduct_collectionRemoveProducts_collection_p export interface UnassignCollectionProduct_collectionRemoveProducts_collection_products_edges_node { __typename: "Product"; id: string; - isPublished: boolean; name: string; productType: UnassignCollectionProduct_collectionRemoveProducts_collection_products_edges_node_productType; thumbnail: UnassignCollectionProduct_collectionRemoveProducts_collection_products_edges_node_thumbnail | null; @@ -54,8 +53,8 @@ export interface UnassignCollectionProduct_collectionRemoveProducts_collection { } export interface UnassignCollectionProduct_collectionRemoveProducts_errors { - __typename: "ProductError"; - code: ProductErrorCode; + __typename: "CollectionError"; + code: CollectionErrorCode; field: string | null; } diff --git a/src/collections/urls.ts b/src/collections/urls.ts index 65abf5b16..080a8884b 100644 --- a/src/collections/urls.ts +++ b/src/collections/urls.ts @@ -19,11 +19,7 @@ export enum CollectionListUrlFiltersEnum { query = "query" } export type CollectionListUrlFilters = Filters; -export type CollectionListUrlDialog = - | "publish" - | "unpublish" - | "remove" - | TabActionDialog; +export type CollectionListUrlDialog = "remove" | "settings" | TabActionDialog; export enum CollectionListUrlSortField { name = "name", available = "available", diff --git a/src/collections/utils.ts b/src/collections/utils.ts new file mode 100644 index 000000000..394e6a08a --- /dev/null +++ b/src/collections/utils.ts @@ -0,0 +1,21 @@ +import { ChannelCollectionData } from "@saleor/channels/utils"; + +export const createChannelsChangeHandler = ( + channelListings: ChannelCollectionData[], + updateChannels: (data: ChannelCollectionData[]) => void, + triggerChange: () => void +) => (id: string, data: Omit) => { + const channelIndex = channelListings.findIndex(channel => channel.id === id); + const channel = channelListings[channelIndex]; + + const updatedChannels = [ + ...channelListings.slice(0, channelIndex), + { + ...channel, + ...data + }, + ...channelListings.slice(channelIndex + 1) + ]; + updateChannels(updatedChannels); + triggerChange(); +}; diff --git a/src/collections/views/CollectionCreate.tsx b/src/collections/views/CollectionCreate.tsx index 277c67be9..949ce9e69 100644 --- a/src/collections/views/CollectionCreate.tsx +++ b/src/collections/views/CollectionCreate.tsx @@ -1,8 +1,11 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { createCollectionChannels } from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; -import getPublicationData from "@saleor/utils/data/getPublicationData"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -14,7 +17,10 @@ import { useIntl } from "react-intl"; import { CollectionCreateInput } from "../../types/globalTypes"; import CollectionCreatePage from "../components/CollectionCreatePage/CollectionCreatePage"; import { CollectionCreateData } from "../components/CollectionCreatePage/form"; -import { useCollectionCreateMutation } from "../mutations"; +import { + useCollectionChannelListingUpdate, + useCollectionCreateMutation +} from "../mutations"; import { collectionListUrl, collectionUrl } from "../urls"; export const CollectionCreate: React.FC = () => { @@ -24,6 +30,31 @@ export const CollectionCreate: React.FC = () => { const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + const [ + updateChannels, + updateChannelsOpts + ] = useCollectionChannelListingUpdate({}); + const { data: channelsData } = useChannelsList({}); + + const allChannels = createCollectionChannels( + channelsData?.channels + )?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(allChannels); + const [createCollection, createCollectionOpts] = useCollectionCreateMutation({ onCompleted: data => { if (data.collectionCreate.errors.length === 0) { @@ -58,13 +89,29 @@ export const CollectionCreate: React.FC = () => { seo: { description: formData.seoDescription, title: formData.seoTitle - }, - ...getPublicationData(formData) + } } } }); - return result.data?.collectionCreate.collection?.id || null; + const id = result.data?.collectionCreate.collection?.id || null; + if (id) { + updateChannels({ + variables: { + id, + input: { + addChannels: formData.channelListings.map(channel => ({ + channelId: channel.id, + isPublished: channel.isPublished, + publicationDate: channel.publicationDate + })), + removeChannels: [] + } + } + }); + } + + return id; }; const handleSubmit = createMetadataCreateHandler( @@ -81,10 +128,34 @@ export const CollectionCreate: React.FC = () => { description: "window title" })} /> + {!!allChannels?.length && ( + + )} navigate(collectionListUrl())} - disabled={createCollectionOpts.loading} + disabled={createCollectionOpts.loading || updateChannelsOpts.loading} onSubmit={handleSubmit} saveButtonBarState={createCollectionOpts.status} /> diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index d9c886825..1b139249b 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -1,11 +1,19 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; +import { useChannelsList } from "@saleor/channels/queries"; +import { + createCollectionChannels, + createCollectionChannelsData +} from "@saleor/channels/utils"; import ActionDialog from "@saleor/components/ActionDialog"; import AssignProductDialog from "@saleor/components/AssignProductDialog"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannels from "@saleor/hooks/useChannels"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { @@ -19,6 +27,7 @@ import { useMetadataUpdate, usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; +import { diff } from "fast-array-diff"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -29,9 +38,9 @@ import CollectionDetailsPage from "../components/CollectionDetailsPage/Collectio import { CollectionUpdateData } from "../components/CollectionDetailsPage/form"; import { useCollectionAssignProductMutation, + useCollectionChannelListingUpdate, useCollectionRemoveMutation, useCollectionUpdateMutation, - useCollectionUpdateWithHomepageMutation, useUnassignCollectionProductMutation } from "../mutations"; import { TypedCollectionDetailsQuery } from "../queries"; @@ -65,6 +74,12 @@ export const CollectionDetails: React.FC = ({ const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + const [ + updateChannels, + updateChannelsOpts + ] = useCollectionChannelListingUpdate({}); + const { data: channelsData } = useChannelsList({}); + const handleCollectionUpdate = (data: CollectionUpdate) => { if (data.collectionUpdate.errors.length === 0) { notify({ @@ -88,17 +103,6 @@ export const CollectionDetails: React.FC = ({ onCompleted: handleCollectionUpdate }); - const [ - updateCollectionWithHomepage, - updateCollectionWithHomepageOpts - ] = useCollectionUpdateWithHomepageMutation({ - onCompleted: data => { - if (data.homepageCollectionUpdate.errors.length === 0) { - handleCollectionUpdate(data); - } - } - }); - const [assignProduct, assignProductOpts] = useCollectionAssignProductMutation( { onCompleted: data => { @@ -155,6 +159,8 @@ export const CollectionDetails: React.FC = ({ const paginationState = createPaginationState(PAGINATE_BY, params); const handleBack = () => navigate(collectionListUrl()); + const [selectedChannel] = useLocalStorage("collectionListChannel", ""); + return ( = ({ > {({ data, loading }) => { const collection = data?.collection; - if (collection === null) { return ; } + const allChannels = createCollectionChannels( + channelsData?.channels + )?.sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + const collectionChannelsChoices = createCollectionChannelsData( + collection + ); + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(collectionChannelsChoices); const handleUpdate = async (formData: CollectionUpdateData) => { const input: CollectionInput = { backgroundImageAlt: formData.backgroundImageAlt, descriptionJson: JSON.stringify(formData.description), - isPublished: formData.isPublished, name: formData.name, - publicationDate: formData.publicationDate, seo: { description: formData.seoDescription, title: formData.seoTitle }, slug: formData.slug }; - const isFeatured = data.shop.homepageCollection - ? data.shop.homepageCollection.id === data.collection.id - : false; - if (formData.isFeatured !== isFeatured) { - const result = await updateCollectionWithHomepage({ - variables: { - homepageId: formData.isFeatured ? id : null, - id, - input - } - }); - return [ - ...result.data.collectionUpdate.errors, - ...result.data.homepageCollectionUpdate.errors - ]; - } else { - const result = await updateCollection({ - variables: { - id, - input - } - }); + const result = await updateCollection({ + variables: { + id, + input + } + }); + const diffChannels = diff( + collectionChannelsChoices, + formData.channelListings, + (a, b) => a.id === b.id + ); - return result.data.collectionUpdate.errors; - } + updateChannels({ + variables: { + id: collection.id, + input: { + addChannels: formData.channelListings.map(channel => ({ + channelId: channel.id, + isPublished: channel.isPublished, + publicationDate: channel.publicationDate + })), + removeChannels: + diffChannels.removed?.map( + removedChannel => removedChannel.id + ) || [] + } + } + }); + + return result.data.collectionUpdate.errors; }; const handleSubmit = createMetadataUpdateHandler( data?.collection, @@ -215,13 +243,9 @@ export const CollectionDetails: React.FC = ({ ); const formTransitionState = getMutationState( - updateCollectionOpts.called || - updateCollectionWithHomepageOpts.called, - updateCollectionOpts.loading || - updateCollectionWithHomepageOpts.loading, - updateCollectionOpts.data?.collectionUpdate.errors, - updateCollectionWithHomepageOpts.data?.collectionUpdate.errors, - updateCollectionWithHomepageOpts.data?.homepageCollectionUpdate.errors + updateCollectionOpts.called, + updateCollectionOpts.loading, + updateCollectionOpts.data?.collectionUpdate.errors ); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( @@ -232,17 +256,34 @@ export const CollectionDetails: React.FC = ({ return ( <> - data.collection.name)} /> + + {!!allChannels?.length && ( + + )} openModal("assign")} onBack={handleBack} - disabled={loading} + disabled={loading || updateChannelsOpts.loading} collection={data?.collection} + channelsErrors={ + updateChannelsOpts?.data?.collectionChannelListingUpdate + .errors || [] + } errors={updateCollectionOpts?.data?.collectionUpdate.errors || []} - isFeatured={maybe( - () => data.shop.homepageCollection.id === data.collection.id, - false - )} onCollectionRemove={() => openModal("remove")} onImageDelete={() => openModal("removeImage")} onImageUpload={file => @@ -290,6 +331,14 @@ export const CollectionDetails: React.FC = ({ selected={listElements.length} toggle={toggle} toggleAll={toggleAll} + currentChannels={currentChannels} + hasChannelChanged={ + collectionChannelsChoices?.length !== currentChannels?.length + } + channelsCount={channelsData?.channels?.length} + selectedChannel={selectedChannel} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} /> = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); const paginate = usePaginator(); - const shop = useShop(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); @@ -97,24 +90,6 @@ export const CollectionList: React.FC = ({ params }) => { } } }); - - const [ - collectionBulkPublish, - collectionBulkPublishOpts - ] = useCollectionBulkPublish({ - onCompleted: data => { - if (data.collectionBulkPublish.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - refetch(); - reset(); - closeModal(); - } - } - }); - const tabs = getFilterTabs(); const currentTab = @@ -124,23 +99,27 @@ export const CollectionList: React.FC = ({ params }) => { : 0 : parseInt(params.activeTab, 0); - const [ - changeFilters, - resetFilters, - handleSearchChange - ] = createFilterHandlers({ - cleanupFn: reset, - createUrl: collectionListUrl, - getFilterQueryParam, - navigate, - params - }); + const handleSearchChange = (query: string) => { + navigate( + collectionListUrl({ + ...getActiveFilters(params), + activeTab: undefined, + query + }) + ); + }; const [openModal, closeModal] = createDialogActionHandlers< CollectionListUrlDialog, CollectionListUrlQueryParams >(navigate, collectionListUrl, params); + const { + channelChoices, + handleChannelSelectConfirm, + selectedChannel + } = useChannelsSettings("collectionListChannel", { closeModal, openModal }); + const handleTabChange = (tab: number) => { reset(); navigate( @@ -169,19 +148,25 @@ export const CollectionList: React.FC = ({ params }) => { ); const handleSort = createSortHandler(navigate, collectionListUrl, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); return ( <> + {!!channelChoices?.length && ( + + )} navigate(collectionAddUrl)} - onAll={resetFilters} + onAll={() => navigate(collectionListUrl())} onTabChange={handleTabChange} onTabDelete={() => openModal("delete-search")} onTabSave={() => openModal("save-search")} @@ -197,108 +182,27 @@ export const CollectionList: React.FC = ({ params }) => { sort={getSortParams(params)} onRowClick={id => () => navigate(collectionUrl(id))} toolbar={ - <> - - - - openModal("remove", { - ids: listElements - }) - } - > - - - + + openModal("remove", { + ids: listElements + }) + } + > + + } isChecked={isSelected} selected={listElements.length} toggle={toggle} toggleAll={toggleAll} + channelsCount={channelChoices?.length} + selectedChannel={selectedChannel} + onSettingsOpen={ + !!channelChoices?.length ? () => openModal("settings") : undefined + } /> - params.ids.length > 0)} - onClose={closeModal} - confirmButtonState={collectionBulkPublishOpts.status} - onConfirm={() => - collectionBulkPublish({ - variables: { - ids: params.ids, - isPublished: true - } - }) - } - variant="default" - title={intl.formatMessage({ - defaultMessage: "Publish collections", - description: "dialog title" - })} - > - - params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} - }} - /> - - - params.ids.length > 0) - } - onClose={closeModal} - confirmButtonState={collectionBulkPublishOpts.status} - onConfirm={() => - collectionBulkPublish({ - variables: { - ids: params.ids, - isPublished: false - } - }) - } - variant="default" - title={intl.formatMessage({ - defaultMessage: "Unpublish collections", - description: "dialog title" - })} - > - - params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} - }} - /> - - params.ids.length > 0)} onClose={closeModal} diff --git a/src/components/ActionDialog/ActionDialog.tsx b/src/components/ActionDialog/ActionDialog.tsx index c4b41cfb2..d0e01de84 100644 --- a/src/components/ActionDialog/ActionDialog.tsx +++ b/src/components/ActionDialog/ActionDialog.tsx @@ -31,6 +31,7 @@ interface ActionDialogProps extends DialogProps { children?: React.ReactNode; confirmButtonLabel?: string; confirmButtonState: ConfirmButtonTransitionState; + disabled?: boolean; maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false; title: string; variant?: "default" | "delete" | "info"; @@ -42,6 +43,7 @@ const ActionDialog: React.FC = props => { children, confirmButtonLabel, confirmButtonState, + disabled, open, title, variant, @@ -58,11 +60,12 @@ const ActionDialog: React.FC = props => { {title} {children} - {variant !== "info" && ( = props => { className={classNames({ [classes.deleteButton]: variant === "delete" })} + data-test="submit" > {confirmButtonLabel || (variant === "delete" diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index c5fdf190d..3b1b09def 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -238,7 +238,7 @@ const useStyles = makeStyles( padding: 25 }, popover: { - zIndex: 1 + zIndex: 2 }, root: { width: `100%` diff --git a/src/components/AvailabilityCard/AvailabilityCard.stories.tsx b/src/components/AvailabilityCard/AvailabilityCard.stories.tsx index 779afafb3..9bf099229 100644 --- a/src/components/AvailabilityCard/AvailabilityCard.stories.tsx +++ b/src/components/AvailabilityCard/AvailabilityCard.stories.tsx @@ -1,23 +1,24 @@ +import { createChannelsDataFromProduct } from "@saleor/channels/utils"; +import { product } from "@saleor/products/fixtures"; import Decorator from "@saleor/storybook/Decorator"; import { storiesOf } from "@storybook/react"; import React from "react"; +const productChannels = createChannelsDataFromProduct(product("")); + import AvailabilityCard from "./AvailabilityCard"; const props = { - data: { - availableForPurchase: "", - isAvailableForPurchase: false, - isPublished: true, - publicationDate: "", - visibleInListings: true - }, + allChannelsCount: 4, + channels: productChannels, errors: [], messages: { hiddenLabel: "Not published", hiddenSecondLabel: "hidden label", visibleLabel: "Published" }, - onChange: () => undefined + onChange: () => undefined, + openModal: () => undefined, + selectedChannelsCount: 3 }; storiesOf("Generics / AvailabilityCard", module) diff --git a/src/components/AvailabilityCard/AvailabilityCard.tsx b/src/components/AvailabilityCard/AvailabilityCard.tsx index 382c4ecea..fa325a342 100644 --- a/src/components/AvailabilityCard/AvailabilityCard.tsx +++ b/src/components/AvailabilityCard/AvailabilityCard.tsx @@ -1,51 +1,92 @@ -import VisibilityCard, { - VisibilityCardProps -} from "@saleor/components/VisibilityCard"; +import ChannelsAvailability, { + ChannelsAvailabilityProps, + Message +} from "@saleor/components/ChannelsAvailability"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import React from "react"; import { useIntl } from "react-intl"; -interface AvailabilityCardProps extends VisibilityCardProps { - data: { - availableForPurchase: string; - isAvailableForPurchase: boolean; - isPublished: boolean; - publicationDate: string; - visibleInListings: boolean; - }; +interface AvailabilityCardProps { + messages: Message; } -export const AvailabilityCard: React.FC = props => { +export const AvailabilityCard: React.FC> = props => { const intl = useIntl(); const localizeDate = useDateLocalize(); return ( - ({ + ...prevVal, + [currVal.id]: { + ...props.messages, + availableDateText: + currVal.publicationDate && !currVal.isPublished + ? intl.formatMessage( + { + defaultMessage: "Will become available on {date}", + description: "channel publication date" + }, + { + date: localizeDate(currVal.publicationDate, "L") + } + ) + : currVal.publicationDate + ? intl.formatMessage( + { + defaultMessage: "Visible since {date}", + description: "channel publication date" + }, + { + date: localizeDate(currVal.publicationDate, "L") + } + ) + : currVal.isPublished + ? intl.formatMessage({ + defaultMessage: "Visible", + description: "channel publication status" + }) + : intl.formatMessage({ + defaultMessage: "Hidden", + description: "channel publication status" + }), + availableLabel: intl.formatMessage({ + defaultMessage: "Available for purchase", + description: "product availability" + }), + availableSecondLabel: intl.formatMessage( + { + defaultMessage: "will become available on {date}", + description: "product available for purchase date" + }, + { + date: localizeDate(currVal.availableForPurchase, "L") + } + ), + hiddenSecondLabel: intl.formatMessage( + { + defaultMessage: "will become published on {date}", + description: "product publication date label" + }, + { + date: localizeDate(currVal.publicationDate, "L") + } + ), + setAvailabilityDateLabel: intl.formatMessage({ + defaultMessage: "Set availability date", + description: "product availability date label" + }), + unavailableLabel: intl.formatMessage({ + defaultMessage: "Unavailable for purchase", + description: "product unavailability" + }) } - ), - setAvailabilityDateLabel: intl.formatMessage({ - defaultMessage: "Set availability date", - description: "product availability date label" }), - unavailableLabel: intl.formatMessage({ - defaultMessage: "Unavailable for purchase", - description: "product unavailability" - }) - }} + {} + )} /> ); }; diff --git a/src/components/CardMenu/CardMenu.tsx b/src/components/CardMenu/CardMenu.tsx index e7b8afa22..647c9fec2 100644 --- a/src/components/CardMenu/CardMenu.tsx +++ b/src/components/CardMenu/CardMenu.tsx @@ -26,6 +26,9 @@ export interface CardMenuProps { const useStyles = makeStyles( theme => ({ + container: { + zIndex: 1 + }, iconButton: { background: theme.palette.background.paper, borderRadius: "100%", @@ -98,6 +101,7 @@ const CardMenu: React.FC = props => { ({ + id: channel.id, + name: channel.name + })), + errors: [], + onChange: () => undefined, + openModal: () => undefined, + selectedChannelsCount: 3 +}; + +storiesOf("Generics / ChannelsAvailability", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("with onChange", () => ( + ({ + ...prevVal, + [currVal.id]: { + availableLabel: "Available", + availableSecondLabel: "Will become available", + hiddenSecondLabel: "Will become published" + } + }), + {} + )} + /> + )); diff --git a/src/components/ChannelsAvailability/ChannelsAvailability.tsx b/src/components/ChannelsAvailability/ChannelsAvailability.tsx new file mode 100644 index 000000000..bb02e3af8 --- /dev/null +++ b/src/components/ChannelsAvailability/ChannelsAvailability.tsx @@ -0,0 +1,461 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import { Channel as ChannelList } from "@saleor/channels/utils"; +import CardTitle from "@saleor/components/CardTitle"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import Hr from "@saleor/components/Hr"; +import RadioSwitchField from "@saleor/components/RadioSwitchField"; +import { CollectionChannelListingErrorFragment } from "@saleor/fragments/types/CollectionChannelListingErrorFragment"; +import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; +import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import ArrowDropdown from "@saleor/icons/ArrowDropdown"; +import { RequireOnlyOne } from "@saleor/misc"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; +import classNames from "classnames"; +import React, { useState } from "react"; +import { useIntl } from "react-intl"; + +import { DateContext } from "../Date/DateContext"; +import { useStyles } from "./styles"; + +export interface ChannelData { + id: string; + isPublished: boolean; + name: string; + publicationDate: string | null; + availableForPurchase?: string; + isAvailableForPurchase?: boolean; + visibleInListings?: boolean; +} + +export interface Message { + visibleLabel: string; + hiddenLabel: string; + visibleSecondLabel?: string; + hiddenSecondLabel?: string; + availableDateText?: string; + availableLabel?: string; + unavailableLabel?: string; + availableSecondLabel?: string; + setAvailabilityDateLabel?: string; +} + +type Error = + | ProductChannelListingErrorFragment + | CollectionChannelListingErrorFragment; + +interface Value { + availableForPurchase?: string; + isAvailableForPurchase?: boolean; + isPublished: boolean; + publicationDate: string | null; + visibleInListings?: boolean; +} +interface ChannelsAvailability { + channels: ChannelData[]; + channelsList: ChannelList[]; + channelsMessages?: { [id: string]: Message }; + errors: Error[]; + selectedChannelsCount: number; + allChannelsCount: number; + disabled?: boolean; + onChange?: (id: string, data: Value) => void; + openModal: () => void; +} +export type ChannelsAvailabilityProps = RequireOnlyOne< + ChannelsAvailability, + "channels" | "channelsList" +>; + +interface ChannelProps { + disabled?: boolean; + data: ChannelData; + errors: Error[]; + messages: Message; + onChange: (id: string, data: Value) => void; +} + +const Channel: React.FC = ({ + data, + disabled, + errors, + messages, + onChange +}) => { + const { + availableForPurchase, + isAvailableForPurchase: isAvailable, + isPublished, + publicationDate, + visibleInListings, + id, + name + } = data; + const formData = { + ...(availableForPurchase !== undefined ? { availableForPurchase } : {}), + ...(isAvailable !== undefined + ? { isAvailableForPurchase: isAvailable } + : {}), + isPublished, + publicationDate, + ...(visibleInListings !== undefined ? { visibleInListings } : {}) + }; + const dateNow = React.useContext(DateContext); + const localizeDate = useDateLocalize(); + const hasAvailableProps = + isAvailable !== undefined && availableForPurchase !== undefined; + + const [isPublicationDate, setPublicationDate] = useState( + publicationDate === null ? true : false + ); + const [isAvailableDate, setAvailableDate] = useState(false); + const [isOpen, setOpen] = useState(false); + const intl = useIntl(); + const classes = useStyles({}); + + const todayDate = localizeDate(new Date(dateNow).toString(), "YYYY-MM-DD"); + + const visibleMessage = (date: string) => + intl.formatMessage( + { + defaultMessage: "since {date}", + description: "date" + }, + { + date: localizeDate(date, "L") + } + ); + + const formErrors = getFormErrors( + ["availableForPurchaseDate", "publicationDate"], + errors + ); + + return ( + <> +
+
setOpen(open => !open)} + > +
+ {name} + +
+ + {messages.availableDateText} + +
+ {isOpen && ( + <> + +

{messages.visibleLabel}

+ {isPublished && + publicationDate && + Date.parse(publicationDate) < dateNow && ( + + {messages.visibleSecondLabel || + visibleMessage(publicationDate)} + + )} + + } + name="isPublished" + secondOptionLabel={ + <> +

{messages.hiddenLabel}

+ {publicationDate && + !isPublished && + Date.parse(publicationDate) >= dateNow && ( + + {messages.hiddenSecondLabel} + + )} + + } + value={isPublished} + onChange={() => { + onChange(id, { + ...formData, + isPublished: !isPublished, + publicationDate: + !isPublished && !publicationDate + ? todayDate + : publicationDate + }); + }} + /> + {!isPublished && ( + <> + setPublicationDate(!isPublicationDate)} + > + {intl.formatMessage({ + defaultMessage: "Set publication date" + })} + + {isPublicationDate && ( + + onChange(id, { + ...formData, + publicationDate: e.target.value + }) + } + className={classes.date} + InputLabelProps={{ + shrink: true + }} + /> + )} + + )} + {hasAvailableProps && ( + <> +
+ +

{messages.availableLabel}

+ {isAvailable && + availableForPurchase && + Date.parse(availableForPurchase) < dateNow && ( + + {visibleMessage(availableForPurchase)} + + )} + + } + name={`channel:isAvailableForPurchase:${id}`} + secondOptionLabel={ + <> +

+ {messages.unavailableLabel} +

+ {availableForPurchase && !isAvailable && ( + + {messages.availableSecondLabel} + + )} + + } + value={isAvailable} + onChange={e => { + const { value } = e.target; + return onChange(id, { + ...formData, + availableForPurchase: !value + ? null + : availableForPurchase, + isAvailableForPurchase: value + }); + }} + /> + {!isAvailable && ( + <> + setAvailableDate(!isAvailableDate)} + > + {messages.setAvailabilityDateLabel} + + {isAvailableDate && ( + + onChange(id, { + ...formData, + availableForPurchase: e.target.value + }) + } + className={classes.date} + InputLabelProps={{ + shrink: true + }} + /> + )} + + )} + + )} + {visibleInListings !== undefined && ( + <> +
+ +

+ {intl.formatMessage({ + defaultMessage: "Show in product listings" + })} +

+ + + {intl.formatMessage({ + defaultMessage: + "Disabling this checkbox will remove product from search and category pages. It will be available on collection pages." + })} + + + } + onChange={e => + onChange(id, { + ...formData, + visibleInListings: e.target.value + }) + } + /> + + )} + + )} +
+
+ + ); +}; + +export const ChannelsAvailability: React.FC = props => { + const { + channelsList, + errors, + selectedChannelsCount, + allChannelsCount, + channels, + channelsMessages, + openModal, + onChange + } = props; + const intl = useIntl(); + const classes = useStyles({}); + const channelsAvailabilityText = intl.formatMessage( + { + defaultMessage: + "Available at {selectedChannelsCount} out of {allChannelsCount, plural, one {# channel} other {# channels}}", + + description: "channels availability text" + }, + { + allChannelsCount, + selectedChannelsCount + } + ); + return ( + <> + + + {intl.formatMessage({ + defaultMessage: "Manage", + description: "section header button" + })} + + } + /> + + {!!channelsAvailabilityText && ( + <> + + {channelsAvailabilityText} + +
+ + )} + {channels + ? channels.map(data => { + const channelErrors = + errors?.filter(error => error.channels.includes(data.id)) || + []; + return ( + + ); + }) + : channelsList + ? channelsList.map(data => ( + +
+
+ {data.name} +
+
+
+
+ )) + : null} +
+
+ + ); +}; +ChannelsAvailability.displayName = "ChannelsAvailability"; +export default ChannelsAvailability; diff --git a/src/components/ChannelsAvailability/index.ts b/src/components/ChannelsAvailability/index.ts new file mode 100644 index 000000000..b59832015 --- /dev/null +++ b/src/components/ChannelsAvailability/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsAvailability"; +export { default } from "./ChannelsAvailability"; diff --git a/src/components/ChannelsAvailability/styles.ts b/src/components/ChannelsAvailability/styles.ts new file mode 100644 index 000000000..31cc76f5a --- /dev/null +++ b/src/components/ChannelsAvailability/styles.ts @@ -0,0 +1,78 @@ +import { makeStyles } from "@material-ui/core/styles"; + +export const useStyles = makeStyles( + theme => ({ + arrow: { + transition: theme.transitions.duration.short + "ms" + }, + card: { + "&:last-child": { + paddingBottom: 0 + }, + paddingTop: 0 + }, + channelBtn: { + "&:focus": { + outline: "none" + }, + background: "transparent", + border: "none", + cursor: "pointer", + textAlign: "left" + }, + channelInfo: { + fontSize: 14, + padding: theme.spacing(2, 0) + }, + channelItem: { + "&:last-child hr": { + display: "none" + }, + padding: theme.spacing(2, 0) + }, + channelName: { + alignItems: "center", + display: "flex", + justifyContent: "space-between", + marginBottom: theme.spacing(0.5) + }, + checkbox: { + alignItems: "flex-start", + marginTop: 10 + }, + date: { + "& svg": { + fill: theme.palette.primary.main + }, + marginTop: theme.spacing(1) + }, + hr: { + left: theme.spacing(-3), + position: "relative", + width: `calc(100% + ${theme.spacing(6)}px)` + }, + label: { + lineHeight: 1.2, + marginBottom: 5, + marginTop: 0 + }, + listingLabel: { + marginTop: 9 + }, + rotate: { + transform: "rotate(180deg)" + }, + secondLabel: { + color: theme.palette.text.hint, + fontSize: 12 + }, + setPublicationDate: { + color: theme.palette.primary.main, + cursor: "pointer", + fontSize: 14, + paddingBottom: 10, + paddingTop: 0 + } + }), + { name: "ChannelsAvailabilityCard" } +); diff --git a/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx b/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx new file mode 100644 index 000000000..d63fc89c4 --- /dev/null +++ b/src/components/ChannelsAvailabilityContent/ChannelsAvailabilityContent.tsx @@ -0,0 +1,108 @@ +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import { Channel } from "@saleor/channels/utils"; +import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; +import Hr from "@saleor/components/Hr"; +import { filter } from "fuzzaldrin"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "./styles"; + +export interface ChannelsAvailabilityContentProps { + isSelected: (option: Channel) => boolean; + channels: Channel[]; + contentType?: string; + disabled: boolean; + onChange: (option: Channel) => void; + selected?: number; + toggleAllText?: string; + toggleAll?: (items: Channel[], selected: number) => void; +} + +export const ChannelsAvailabilityContent: React.FC = ({ + isSelected, + channels, + contentType = "", + onChange, + selected = 0, + toggleAll, + toggleAllText +}) => { + const classes = useStyles({}); + const intl = useIntl(); + const searchText = intl.formatMessage({ + defaultMessage: "Search through channels" + }); + const [query, onQueryChange] = React.useState(""); + const filteredChannels = filter(channels, query, { key: "name" }); + + return ( +
+ {!!contentType && ( + + + + )} + onQueryChange(e.target.value)} + label={searchText} + placeholder={searchText} + fullWidth + /> +
+ {!!toggleAll && ( + <> + + + + ) + } + onChange={() => toggleAll(channels, selected)} + /> +
+ + )} + + + +
+ {filteredChannels?.length ? ( + filteredChannels.map(option => ( +
+ + {option.name} + + } + onChange={() => onChange(option)} + /> +
+
+ )) + ) : ( +
+ +
+ )} +
+
+
+ ); +}; +ChannelsAvailabilityContent.displayName = "ChannelsAvailabilityContent"; +export default ChannelsAvailabilityContent; diff --git a/src/components/ChannelsAvailabilityContent/index.ts b/src/components/ChannelsAvailabilityContent/index.ts new file mode 100644 index 000000000..2ca745b1f --- /dev/null +++ b/src/components/ChannelsAvailabilityContent/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsAvailabilityContent"; +export { default } from "./ChannelsAvailabilityContent"; diff --git a/src/components/ChannelsAvailabilityContent/styles.ts b/src/components/ChannelsAvailabilityContent/styles.ts new file mode 100644 index 000000000..4bb4d7d9b --- /dev/null +++ b/src/components/ChannelsAvailabilityContent/styles.ts @@ -0,0 +1,47 @@ +import makeStyles from "@material-ui/core/styles/makeStyles"; + +export const useStyles = makeStyles( + theme => ({ + content: { + "& hr": { + left: -24, + position: "relative", + width: "calc(100% + 48px)" + } + }, + contentTitle: { + margin: theme.spacing(1, 0) + }, + dialog: { + marginBottom: -30, + marginTop: theme.spacing(2) + }, + input: { + "& label": { + overflowX: "inherit" + } + }, + label: { + fontSize: 14 + }, + notFound: { + paddingBottom: theme.spacing(2) + }, + option: { + "&:last-child": { + "& hr": { + display: "none" + } + }, + margin: theme.spacing(1, 0) + }, + scrollArea: { + maxHeight: 400, + overflowY: "scroll" + }, + text: { + marginBottom: 5 + } + }), + { name: "ChannelsAvailabilityContent" } +); diff --git a/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.stories.tsx b/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.stories.tsx new file mode 100644 index 000000000..429dca98a --- /dev/null +++ b/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.stories.tsx @@ -0,0 +1,32 @@ +import { channelsList } from "@saleor/channels/fixtures"; +import { createChannelsData } from "@saleor/channels/utils"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ChannelsAvailabilityDialog, { + ChannelsAvailabilityDialogProps +} from "./ChannelsAvailabilityDialog"; + +const props: ChannelsAvailabilityDialogProps = { + channels: createChannelsData(channelsList), + confirmButtonState: "default", + disabled: false, + isSelected: () => undefined, + onChange: () => undefined, + onClose: () => undefined, + onConfirm: () => undefined, + open: true, + title: "Channels", + toggleAll: () => undefined +}; + +storiesOf("Generics / ChannelsAvailabilityDialog", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("with text", () => ( + + )) + .add("disabled", () => ( + + )); diff --git a/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.tsx b/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.tsx new file mode 100644 index 000000000..8f26c0e88 --- /dev/null +++ b/src/components/ChannelsAvailabilityDialog/ChannelsAvailabilityDialog.tsx @@ -0,0 +1,56 @@ +import { Channel } from "@saleor/channels/utils"; +import ActionDialog from "@saleor/components/ActionDialog"; +import { ChannelsAvailabilityContent } from "@saleor/components/ChannelsAvailabilityContent"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import React from "react"; + +export interface ChannelsAvailabilityDialogProps { + isSelected: (option: Channel) => boolean; + channels: Channel[]; + confirmButtonState: ConfirmButtonTransitionState; + contentType?: string; + disabled: boolean; + open: boolean; + onClose: () => void; + onChange: (option: Channel) => void; + onConfirm: () => void; + selected?: number; + title: string; + toggleAll?: (items: Channel[], selected: number) => void; +} + +export const ChannelsAvailabilityDialog: React.FC = ({ + isSelected, + channels, + confirmButtonState, + contentType = "", + disabled, + open, + onClose, + onChange, + onConfirm, + selected = 0, + title, + toggleAll +}) => ( + + + +); +ChannelsAvailabilityDialog.displayName = "ChannelsAvailabilityDialog"; +export default ChannelsAvailabilityDialog; diff --git a/src/components/ChannelsAvailabilityDialog/index.ts b/src/components/ChannelsAvailabilityDialog/index.ts new file mode 100644 index 000000000..602d4777c --- /dev/null +++ b/src/components/ChannelsAvailabilityDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsAvailabilityDialog"; +export { default } from "./ChannelsAvailabilityDialog"; diff --git a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx new file mode 100644 index 000000000..b54afa524 --- /dev/null +++ b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx @@ -0,0 +1,18 @@ +import { productChannels } from "@saleor/channels/fixtures"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ChannelsAvailabilityDropdown, { + ChannelsAvailabilityDropdownProps +} from "./ChannelsAvailabilityDropdown"; + +const props: ChannelsAvailabilityDropdownProps = { + allChannelsCount: 6, + channels: productChannels, + currentChannel: productChannels[0] +}; + +storiesOf("Generics / ChannelsAvailabilityDropdown", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx new file mode 100644 index 000000000..e528ec194 --- /dev/null +++ b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx @@ -0,0 +1,138 @@ +import Menu from "@material-ui/core/Menu"; +import MenuItem from "@material-ui/core/MenuItem"; +import Typography from "@material-ui/core/Typography"; +import { CollectionList_collections_edges_node_channelListings } from "@saleor/collections/types/CollectionList"; +import Hr from "@saleor/components/Hr"; +import StatusLabel from "@saleor/components/StatusLabel"; +import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import { ProductList_products_edges_node_channelListings } from "@saleor/products/types/ProductList"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "./styles"; + +type Channels = + | ProductList_products_edges_node_channelListings + | CollectionList_collections_edges_node_channelListings; + +export interface ChannelsAvailabilityDropdownProps { + allChannelsCount: number; + channels: Channels[]; + currentChannel: Channels; +} + +const isActive = (channelData: Channels) => channelData?.isPublished; + +export const ChannelsAvailabilityDropdown: React.FC = ({ + allChannelsCount, + channels, + currentChannel +}) => { + const intl = useIntl(); + const classes = useStyles({}); + const localizeDate = useDateLocalize(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = event => setAnchorEl(event.currentTarget); + + const handleClose = () => setAnchorEl(null); + return ( +
e.stopPropagation()}> +
+ +
+ + + + +
+ {channels.map(channelData => { + const notPublishedText = intl.formatMessage( + { + defaultMessage: "Will become available on {date}", + description: "product channel publication date" + }, + { + date: localizeDate(channelData.publicationDate, "L") + } + ); + + const publishedText = intl.formatMessage( + { + defaultMessage: "published since {date}", + description: "product channel publication date" + }, + { + date: localizeDate(channelData.publicationDate, "L") + } + ); + + return ( + + +
+ + {channelData.isPublished && channelData.publicationDate + ? publishedText + : channelData.publicationDate && !channelData.isPublished + ? notPublishedText + : channelData.isPublished + ? "" + : intl.formatMessage({ + defaultMessage: "hidden", + description: "product channel publication status" + })} + +
+
+ ); + })} +
+
+ ); +}; +ChannelsAvailabilityDropdown.displayName = "ChannelsAvailabilityDropdown"; +export default ChannelsAvailabilityDropdown; diff --git a/src/components/ChannelsAvailabilityDropdown/index.ts b/src/components/ChannelsAvailabilityDropdown/index.ts new file mode 100644 index 000000000..01c17bac9 --- /dev/null +++ b/src/components/ChannelsAvailabilityDropdown/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsAvailabilityDropdown"; +export { default } from "./ChannelsAvailabilityDropdown"; diff --git a/src/components/ChannelsAvailabilityDropdown/styles.ts b/src/components/ChannelsAvailabilityDropdown/styles.ts new file mode 100644 index 000000000..4fd53e7e4 --- /dev/null +++ b/src/components/ChannelsAvailabilityDropdown/styles.ts @@ -0,0 +1,21 @@ +import makeStyles from "@material-ui/core/styles/makeStyles"; + +export const useStyles = makeStyles( + theme => ({ + caption: { + paddingLeft: theme.spacing(2) + }, + hr: { + left: theme.spacing(-1), + position: "relative", + width: `calc(100% + ${theme.spacing(2)}px)` + }, + menuItem: { + display: "block" + }, + title: { + padding: theme.spacing(1, 2, 1, 2) + } + }), + { name: "ChannelsAvailabilityDropdown" } +); diff --git a/src/components/ChannelsSelect/ChannelsSelect.stories.tsx b/src/components/ChannelsSelect/ChannelsSelect.stories.tsx new file mode 100644 index 000000000..062c4550b --- /dev/null +++ b/src/components/ChannelsSelect/ChannelsSelect.stories.tsx @@ -0,0 +1,22 @@ +import { product as productFixture } from "@saleor/products/fixtures"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ChannelsSelect, { ChannelsSelectProps } from "./ChannelsSelect"; + +const product = productFixture(""); +const channelChoices = product.channelListings.map(listing => ({ + label: listing.channel.name, + value: listing.channel.id +})); + +const props: ChannelsSelectProps = { + channelChoice: channelChoices[0].value, + channelChoices, + setChannelChoice: () => undefined +}; + +storiesOf("Generics / ChannelsSelect", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/components/ChannelsSelect/ChannelsSelect.tsx b/src/components/ChannelsSelect/ChannelsSelect.tsx new file mode 100644 index 000000000..9f640ee91 --- /dev/null +++ b/src/components/ChannelsSelect/ChannelsSelect.tsx @@ -0,0 +1,53 @@ +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import LinkChoice from "@saleor/components/LinkChoice"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { FormChange } from "@saleor/hooks/useForm"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +export interface ChannelsSelectProps { + channelChoices: SingleAutocompleteChoiceType[]; + channelChoice: string; + setChannelChoice: FormChange; +} + +const useStyles = makeStyles( + theme => ({ + label: { + display: "inline-block", + marginRight: theme.spacing(1) + }, + select: { + display: "inline-block" + } + }), + { name: "ChannelsSelect" } +); + +export const ChannelsSelect: React.FC = ({ + channelChoice, + channelChoices, + setChannelChoice +}) => { + const classes = useStyles({}); + + return channelChoices?.length ? ( + <> + + + + setChannelChoice(event.target.value)} + /> + + ) : null; +}; +export default ChannelsSelect; diff --git a/src/components/ChannelsSelect/index.ts b/src/components/ChannelsSelect/index.ts new file mode 100644 index 000000000..adc6e5d84 --- /dev/null +++ b/src/components/ChannelsSelect/index.ts @@ -0,0 +1,2 @@ +export * from "./ChannelsSelect"; +export { default } from "./ChannelsSelect"; diff --git a/src/components/Filter/Filter.tsx b/src/components/Filter/Filter.tsx index 098dc7837..b5dd8ad76 100644 --- a/src/components/Filter/Filter.tsx +++ b/src/components/Filter/Filter.tsx @@ -14,7 +14,7 @@ import { IFilter, IFilterElement } from "./types"; import useFilter from "./useFilter"; export interface FilterProps { - currencySymbol: string; + currencySymbol?: string; menu: IFilter; onFilterAdd: (filter: Array>) => void; } diff --git a/src/components/Filter/FilterContent.tsx b/src/components/Filter/FilterContent.tsx index 4807800e0..18924d80d 100644 --- a/src/components/Filter/FilterContent.tsx +++ b/src/components/Filter/FilterContent.tsx @@ -25,11 +25,11 @@ import { FilterReducerAction } from "./reducer"; import { FieldType, FilterType, IFilter } from "./types"; export interface FilterContentProps { - currencySymbol: string; filters: IFilter; onFilterPropertyChange: React.Dispatch>; onClear: () => void; onSubmit: () => void; + currencySymbol?: string; } const useStyles = makeStyles( diff --git a/src/components/FilterBar/FilterBar.tsx b/src/components/FilterBar/FilterBar.tsx index fd912aabf..2fc7dd187 100644 --- a/src/components/FilterBar/FilterBar.tsx +++ b/src/components/FilterBar/FilterBar.tsx @@ -84,8 +84,8 @@ const FilterBar: React.FC = props => {
= props => { onClickAway={() => setExpandedState(false)} mouseEvent="onClick" > - {languages.map(lang => ( - + + {languages.map(lang => ( { setExpandedState(false); @@ -113,8 +114,8 @@ const LanguageSwitch: React.FC = props => { }} /> - - ))} + ))} + diff --git a/src/components/Money/Money.tsx b/src/components/Money/Money.tsx index c515c102d..f7de8e879 100644 --- a/src/components/Money/Money.tsx +++ b/src/components/Money/Money.tsx @@ -7,19 +7,22 @@ export interface IMoney { currency: string; } export interface MoneyProps { - money: IMoney; + money: IMoney | null; } -export const Money: React.FC = ({ money }) => ( - - {({ locale }) => - money.amount.toLocaleString(locale, { - currency: money.currency, - style: "currency" - }) - } - -); +export const Money: React.FC = ({ money }) => + money ? ( + + {({ locale }) => + money.amount.toLocaleString(locale, { + currency: money.currency, + style: "currency" + }) + } + + ) : ( + <>- + ); Money.displayName = "Money"; export default Money; diff --git a/src/components/Navigator/modes/catalog.ts b/src/components/Navigator/modes/catalog.ts index dcb1e3142..10b2b253b 100644 --- a/src/components/Navigator/modes/catalog.ts +++ b/src/components/Navigator/modes/catalog.ts @@ -42,11 +42,6 @@ export function searchInCatalog( ) .map(collection => ({ caption: intl.formatMessage(messages.collection), - extraInfo: intl.formatMessage( - collection.isPublished - ? messages.collectionPublished - : messages.collectionUnpublished - ), label: collection.name, onClick: () => { navigate(collectionUrl(collection.id)); diff --git a/src/components/Navigator/queries/types/SearchCatalog.ts b/src/components/Navigator/queries/types/SearchCatalog.ts index da0d4967c..d1badceea 100644 --- a/src/components/Navigator/queries/types/SearchCatalog.ts +++ b/src/components/Navigator/queries/types/SearchCatalog.ts @@ -22,12 +22,24 @@ export interface SearchCatalog_categories { edges: SearchCatalog_categories_edges[]; } +export interface SearchCatalog_collections_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface SearchCatalog_collections_edges_node_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: SearchCatalog_collections_edges_node_channelListings_channel; +} + export interface SearchCatalog_collections_edges_node { __typename: "Collection"; id: string; name: string; - isPublished: boolean; - publicationDate: any | null; + channelListings: SearchCatalog_collections_edges_node_channelListings[] | null; } export interface SearchCatalog_collections_edges { diff --git a/src/components/Navigator/queries/useCatalogSearch.ts b/src/components/Navigator/queries/useCatalogSearch.ts index 5b6c3a9fd..daf276902 100644 --- a/src/components/Navigator/queries/useCatalogSearch.ts +++ b/src/components/Navigator/queries/useCatalogSearch.ts @@ -1,3 +1,4 @@ +import { collectionFragment } from "@saleor/fragments/collections"; import makeQuery, { UseQueryResult } from "@saleor/hooks/makeQuery"; import useDebounce from "@saleor/hooks/useDebounce"; import gql from "graphql-tag"; @@ -6,6 +7,7 @@ import { useState } from "react"; import { SearchCatalog, SearchCatalogVariables } from "./types/SearchCatalog"; const searchCatalog = gql` + ${collectionFragment} query SearchCatalog($first: Int!, $query: String!) { categories(first: $first, filter: { search: $query }) { edges { @@ -19,10 +21,7 @@ const searchCatalog = gql` collections(first: $first, filter: { search: $query }) { edges { node { - id - name - isPublished - publicationDate + ...CollectionFragment } } } diff --git a/src/components/PriceField/PriceField.tsx b/src/components/PriceField/PriceField.tsx index 27e59de30..a5b0c97f4 100644 --- a/src/components/PriceField/PriceField.tsx +++ b/src/components/PriceField/PriceField.tsx @@ -3,6 +3,7 @@ import InputAdornment from "@material-ui/core/InputAdornment"; import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import React from "react"; +import { FormattedMessage } from "react-intl"; const useStyles = makeStyles( theme => ({ @@ -38,6 +39,7 @@ interface PriceFieldProps { name?: string; value?: string | number; InputProps?: InputProps; + required?: boolean; onChange(event: any); } @@ -47,21 +49,30 @@ export const PriceField: React.FC = props => { disabled, error, label, - hint, + hint = "", currencySymbol, name, onChange, + required, value, InputProps } = props; const classes = useStyles(props); - + const minValue = 0; return ( + ) : ( + "" + ) + } label={label} fullWidth value={value} @@ -80,8 +91,13 @@ export const PriceField: React.FC = props => { }, type: "number" }} + inputProps={{ + min: minValue, + type: "number" + }} name={name} disabled={disabled} + required={required} onChange={onChange} /> ); diff --git a/src/components/SeoForm/SeoForm.tsx b/src/components/SeoForm/SeoForm.tsx index 474b48122..6959d7aac 100644 --- a/src/components/SeoForm/SeoForm.tsx +++ b/src/components/SeoForm/SeoForm.tsx @@ -4,6 +4,7 @@ import CardContent from "@material-ui/core/CardContent"; import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; +import { CollectionErrorFragment } from "@saleor/fragments/types/CollectionErrorFragment"; import { PageErrorFragment } from "@saleor/fragments/types/PageErrorFragment"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { getFieldError, getProductErrorMessage } from "@saleor/utils/errors"; @@ -81,7 +82,9 @@ interface SeoFormProps { description?: string; descriptionPlaceholder: string; disabled?: boolean; - errors?: Array; + errors?: Array< + PageErrorFragment | ProductErrorFragment | CollectionErrorFragment + >; loading?: boolean; helperText?: string; allowEmptySlug?: boolean; diff --git a/src/components/Shop/query.ts b/src/components/Shop/query.ts index 3b8ac4d5b..f082c68a2 100644 --- a/src/components/Shop/query.ts +++ b/src/components/Shop/query.ts @@ -14,7 +14,6 @@ const shopInfo = gql` code country } - defaultCurrency defaultWeightUnit displayGrossPrices domain { diff --git a/src/components/Shop/types/ShopInfo.ts b/src/components/Shop/types/ShopInfo.ts index 685af9f96..ed0a6726d 100644 --- a/src/components/Shop/types/ShopInfo.ts +++ b/src/components/Shop/types/ShopInfo.ts @@ -42,7 +42,6 @@ export interface ShopInfo_shop { __typename: "Shop"; countries: ShopInfo_shop_countries[]; defaultCountry: ShopInfo_shop_defaultCountry | null; - defaultCurrency: string; defaultWeightUnit: WeightUnitsEnum | null; displayGrossPrices: boolean; domain: ShopInfo_shop_domain; diff --git a/src/components/SingleSelectField/SingleSelectField.tsx b/src/components/SingleSelectField/SingleSelectField.tsx index 6009b4c76..170b33ef8 100644 --- a/src/components/SingleSelectField/SingleSelectField.tsx +++ b/src/components/SingleSelectField/SingleSelectField.tsx @@ -25,11 +25,14 @@ const useStyles = makeStyles( { name: "SingleSelectField" } ); +export interface Choice { + value: string; + label: string | React.ReactNode; +} + +export type Choices = Choice[]; interface SingleSelectFieldProps { - choices: Array<{ - value: string; - label: string | React.ReactNode; - }>; + choices: Choices; className?: string; disabled?: boolean; error?: boolean; diff --git a/src/config.ts b/src/config.ts index fb0bd87d4..f36ee4d6c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -18,7 +18,7 @@ export const DEFAULT_INITIAL_PAGINATION_DATA: Pagination = { export const PAGINATE_BY = 20; -export type ProductListColumns = "productType" | "isPublished" | "price"; +export type ProductListColumns = "productType" | "availability" | "price"; export interface AppListViewSettings { [ListViews.APPS_LIST]: ListSettings; [ListViews.CATEGORY_LIST]: ListSettings; @@ -67,7 +67,7 @@ export const defaultListSettings: AppListViewSettings = { rowNumber: PAGINATE_BY }, [ListViews.PRODUCT_LIST]: { - columns: ["isPublished", "price", "productType"], + columns: ["availability", "price", "productType"], rowNumber: PAGINATE_BY }, [ListViews.SALES_LIST]: { diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 1a41b7f78..451c1feae 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -1,8 +1,10 @@ import { attributeListUrl } from "@saleor/attributes/urls"; +import { channelsListUrl } from "@saleor/channels/urls"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; import Attributes from "@saleor/icons/Attributes"; +import Channels from "@saleor/icons/Channels"; import Navigation from "@saleor/icons/Navigation"; import Pages from "@saleor/icons/Pages"; import PermissionGroups from "@saleor/icons/PermissionGroups"; @@ -44,7 +46,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { id: "configurationMenuAttributes" }), icon: , - permission: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, + permission: PermissionEnum.MANAGE_PRODUCTS, title: intl.formatMessage(sectionNames.attributes), url: attributeListUrl() }, @@ -54,7 +56,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { id: "configurationMenuProductTypes" }), icon: , - permission: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, + permission: PermissionEnum.MANAGE_PRODUCTS, title: intl.formatMessage(sectionNames.productTypes), url: productTypeListUrl() } @@ -132,6 +134,23 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { } ] }, + { + label: intl.formatMessage({ + defaultMessage: "Multichannel" + }), + menuItems: [ + { + description: intl.formatMessage({ + defaultMessage: "Define and manage your sales channels", + id: "configurationMenuChannels" + }), + icon: , + permission: PermissionEnum.MANAGE_CHANNELS, + title: intl.formatMessage(sectionNames.channels), + url: channelsListUrl() + } + ] + }, { label: intl.formatMessage({ defaultMessage: "Miscellaneous" diff --git a/src/customers/components/CustomerListPage/CustomerListPage.tsx b/src/customers/components/CustomerListPage/CustomerListPage.tsx index 51f0c9464..787e0e576 100644 --- a/src/customers/components/CustomerListPage/CustomerListPage.tsx +++ b/src/customers/components/CustomerListPage/CustomerListPage.tsx @@ -33,7 +33,6 @@ export interface CustomerListPageProps } const CustomerListPage: React.FC = ({ - currencySymbol, currentTab, filterOpts, initialSearch, @@ -67,7 +66,6 @@ const CustomerListPage: React.FC = ({ defaultMessage: "All Customers", description: "tab name" })} - currencySymbol={currencySymbol} currentTab={currentTab} filterStructure={structure} initialSearch={initialSearch} diff --git a/src/customers/components/CustomerListPage/filters.ts b/src/customers/components/CustomerListPage/filters.ts index eb4642493..43607309a 100644 --- a/src/customers/components/CustomerListPage/filters.ts +++ b/src/customers/components/CustomerListPage/filters.ts @@ -8,13 +8,11 @@ import { defineMessages, IntlShape } from "react-intl"; export enum CustomerFilterKeys { joined = "joined", - moneySpent = "spent", numberOfOrders = "orders" } export interface CustomerListFilterOpts { joined: FilterOpts; - moneySpent: FilterOpts; numberOfOrders: FilterOpts; } @@ -23,10 +21,6 @@ const messages = defineMessages({ defaultMessage: "Join Date", description: "customer" }, - moneySpent: { - defaultMessage: "Money Spent", - description: "customer" - }, numberOfOrders: { defaultMessage: "Number of Orders" } @@ -45,14 +39,6 @@ export function createFilterStructure( ), active: opts.joined.active }, - { - ...createNumberField( - CustomerFilterKeys.moneySpent, - intl.formatMessage(messages.moneySpent), - opts.moneySpent.value - ), - active: opts.moneySpent.active - }, { ...createNumberField( CustomerFilterKeys.numberOfOrders, diff --git a/src/customers/urls.ts b/src/customers/urls.ts index f770d63c9..006669759 100644 --- a/src/customers/urls.ts +++ b/src/customers/urls.ts @@ -18,8 +18,6 @@ export const customerListPath = customerSection; export enum CustomerListUrlFiltersEnum { joinedFrom = "joinedFrom", joinedTo = "joinedTo", - moneySpentFrom = "moneySpentFrom", - moneySpentTo = "moneySpentTo", numberOfOrdersFrom = "numberOfOrdersFrom", numberOfOrdersTo = "numberOfOrdersTo", query = "query" diff --git a/src/customers/views/CustomerList/CustomerList.tsx b/src/customers/views/CustomerList/CustomerList.tsx index 63cda7d45..3ffd052b0 100644 --- a/src/customers/views/CustomerList/CustomerList.tsx +++ b/src/customers/views/CustomerList/CustomerList.tsx @@ -13,7 +13,6 @@ import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; @@ -62,7 +61,6 @@ export const CustomerList: React.FC = ({ params }) => { ListViews.CUSTOMER_LIST ); const intl = useIntl(); - const shop = useShop(); const paginationState = createPaginationState(settings.rowNumber, params); const queryVariables = React.useMemo( @@ -144,14 +142,12 @@ export const CustomerList: React.FC = ({ params }) => { }; const handleSort = createSortHandler(navigate, customerListUrl, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); return ( {(bulkRemoveCustomers, bulkRemoveCustomersOpts) => ( <> { it("should not be empty object if params given", () => { const params: CustomerListUrlFilters = { joinedFrom: date.from, - moneySpentFrom: "2", - moneySpentTo: "39.50", numberOfOrdersTo: "5" }; const filterVariables = getFilterVariables(params); - expect(getExistingKeys(filterVariables)).toHaveLength(3); + expect(getExistingKeys(filterVariables)).toHaveLength(2); }); }); @@ -41,13 +39,6 @@ describe("Filtering URL params", () => { min: date.from } }, - moneySpent: { - active: false, - value: { - max: "39.50", - min: "2" - } - }, numberOfOrders: { active: false, value: { diff --git a/src/customers/views/CustomerList/filters.ts b/src/customers/views/CustomerList/filters.ts index 0068da444..513c3611d 100644 --- a/src/customers/views/CustomerList/filters.ts +++ b/src/customers/views/CustomerList/filters.ts @@ -37,19 +37,6 @@ export function getFilterOpts( min: maybe(() => params.joinedFrom, "") } }, - moneySpent: { - active: maybe( - () => - [params.moneySpentFrom, params.moneySpentTo].some( - field => field !== undefined - ), - false - ), - value: { - max: maybe(() => params.moneySpentTo, ""), - min: maybe(() => params.moneySpentFrom, "") - } - }, numberOfOrders: { active: maybe( () => @@ -74,10 +61,6 @@ export function getFilterVariables( gte: params.joinedFrom, lte: params.joinedTo }), - moneySpent: getGteLteVariables({ - gte: parseInt(params.moneySpentFrom, 0), - lte: parseInt(params.moneySpentTo, 0) - }), numberOfOrders: getGteLteVariables({ gte: parseInt(params.numberOfOrdersFrom, 0), lte: parseInt(params.numberOfOrdersTo, 0) @@ -99,13 +82,6 @@ export function getFilterQueryParam( CustomerListUrlFiltersEnum.joinedTo ); - case CustomerFilterKeys.moneySpent: - return getMinMaxQueryParam( - filter, - CustomerListUrlFiltersEnum.moneySpentFrom, - CustomerListUrlFiltersEnum.moneySpentTo - ); - case CustomerFilterKeys.numberOfOrders: return getMinMaxQueryParam( filter, diff --git a/src/discounts/components/DiscountDates/DiscountDates.tsx b/src/discounts/components/DiscountDates/DiscountDates.tsx index be0368439..b75dd66fd 100644 --- a/src/discounts/components/DiscountDates/DiscountDates.tsx +++ b/src/discounts/components/DiscountDates/DiscountDates.tsx @@ -19,7 +19,6 @@ interface DiscountDatesProps { startDate: string; startTime: string; }; - defaultCurrency: string; disabled: boolean; errors: DiscountErrorFragment[]; onChange: (event: React.ChangeEvent) => void; diff --git a/src/discounts/components/DiscountProducts/DiscountProducts.tsx b/src/discounts/components/DiscountProducts/DiscountProducts.tsx index e4d682400..e002983d6 100644 --- a/src/discounts/components/DiscountProducts/DiscountProducts.tsx +++ b/src/discounts/components/DiscountProducts/DiscountProducts.tsx @@ -8,10 +8,10 @@ import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; import CardTitle from "@saleor/components/CardTitle"; +import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown"; import Checkbox from "@saleor/components/Checkbox"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; -import StatusLabel from "@saleor/components/StatusLabel"; import TableCellAvatar, { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar"; @@ -27,6 +27,8 @@ import { VoucherDetails_voucher } from "../../types/VoucherDetails"; export interface SaleProductsProps extends ListProps, ListActions { discount: SaleDetails_sale | VoucherDetails_voucher; + channelsCount: number; + selectedChannel: string; onProductAssign: () => void; onProductUnassign: (id: string) => void; } @@ -66,8 +68,8 @@ const numberOfColumns = 5; const DiscountProducts: React.FC = props => { const { + channelsCount, discount: sale, - disabled, pageInfo, onRowClick, @@ -77,6 +79,7 @@ const DiscountProducts: React.FC = props => { onNextPage, isChecked, selected, + selectedChannel, toggle, toggleAll, toolbar @@ -127,8 +130,8 @@ const DiscountProducts: React.FC = props => { @@ -151,6 +154,10 @@ const DiscountProducts: React.FC = props => { maybe(() => sale.products.edges.map(edge => edge.node)), product => { const isSelected = product ? isChecked(product.id) : false; + const channel = + product?.channelListings.find( + listing => listing.channel.id === selectedChannel + ) || product?.channelListings[0]; return ( = props => { )} - - {product && product.isPublished !== undefined ? ( - + {product && !product?.channelListings?.length ? ( + "-" + ) : product?.channelListings !== undefined ? ( + ) : ( diff --git a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx index 42a005283..128ebe80f 100644 --- a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx +++ b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx @@ -1,5 +1,7 @@ +import { ChannelSaleData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; +import ChannelsAvailability from "@saleor/components/ChannelsAvailability"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; @@ -17,6 +19,7 @@ import SaleInfo from "../SaleInfo"; import SaleType from "../SaleType"; export interface FormData { + channelListings: ChannelSaleData[]; endDate: string; endTime: string; hasEndDate: boolean; @@ -28,25 +31,30 @@ export interface FormData { } export interface SaleCreatePageProps { - defaultCurrency: string; + allChannelsCount: number; + channelListings: ChannelSaleData[]; disabled: boolean; errors: DiscountErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; + openChannelsModal: () => void; onSubmit: (data: FormData) => void; } const SaleCreatePage: React.FC = ({ - defaultCurrency, + allChannelsCount, + channelListings = [], disabled, errors, onSubmit, + openChannelsModal, saveButtonBarState, onBack }) => { const intl = useIntl(); const initialForm: FormData = { + channelListings, endDate: "", endTime: "", hasEndDate: false, @@ -83,11 +91,22 @@ const SaleCreatePage: React.FC = ({
+
+ ({ + id: channel.id, + name: channel.name + }))} + disabled={disabled} + openModal={openChannelsModal} + /> +
{ activeTab: SaleDetailsPageTab; - defaultCurrency: string; errors: DiscountErrorFragment[]; sale: SaleDetails_sale; + selectedChannel: string; + allChannelsCount: number; + channelListings: ChannelSaleData[]; + hasChannelChanged: boolean; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; onCategoryAssign: () => void; @@ -72,6 +79,8 @@ export interface SaleDetailsPageProps onRemove: () => void; onSubmit: (data: SaleDetailsPageFormData) => void; onTabClick: (index: SaleDetailsPageTab) => void; + onChannelsChange: (data: ChannelSaleData[]) => void; + openChannelsModal: () => void; } const CategoriesTab = Tab(SaleDetailsPageTab.categories); @@ -80,12 +89,15 @@ const ProductsTab = Tab(SaleDetailsPageTab.products); const SaleDetailsPage: React.FC = ({ activeTab, - defaultCurrency, + allChannelsCount, + channelListings = [], disabled, errors, onRemove, onSubmit, onTabClick, + hasChannelChanged, + openChannelsModal, pageInfo, sale, saveButtonBarState, @@ -93,6 +105,7 @@ const SaleDetailsPage: React.FC = ({ onCategoryAssign, onCategoryUnassign, onCategoryClick, + onChannelsChange, onCollectionAssign, onCollectionUnassign, onCollectionClick, @@ -106,176 +119,200 @@ const SaleDetailsPage: React.FC = ({ productListToolbar, isChecked, selected, + selectedChannel, toggle, toggleAll }) => { const intl = useIntl(); const initialForm: SaleDetailsPageFormData = { + channelListings, endDate: splitDateTime(maybe(() => sale.endDate, "")).date, endTime: splitDateTime(maybe(() => sale.endDate, "")).time, hasEndDate: maybe(() => !!sale.endDate), name: maybe(() => sale.name, ""), startDate: splitDateTime(maybe(() => sale.startDate, "")).date, startTime: splitDateTime(maybe(() => sale.startDate, "")).time, - type: maybe(() => sale.type, SaleTypeEnum.FIXED), - value: maybe(() => sale.value.toString(), "") + type: maybe(() => sale.type, SaleTypeEnum.FIXED) }; return (
- {({ change, data, hasChanged, submit }) => ( - - - {intl.formatMessage(sectionNames.sales)} - - sale.name)} /> - -
- - - - - - - - - {intl.formatMessage( - { - defaultMessage: "Categories ({quantity})", - description: "number of categories", - id: "saleDetailsPageCategoriesQuantity" - }, - { - quantity: maybe( - () => sale.categories.totalCount.toString(), - "…" - ) - } - )} - - - {intl.formatMessage( - { - defaultMessage: "Collections ({quantity})", - description: "number of collections", - id: "saleDetailsPageCollectionsQuantity" - }, - { - quantity: maybe( - () => sale.collections.totalCount.toString(), - "…" - ) - } - )} - - - {intl.formatMessage( - { - defaultMessage: "Products ({quantity})", - description: "number of products", - id: "saleDetailsPageProductsQuantity" - }, - { - quantity: maybe( - () => sale.products.totalCount.toString(), - "…" - ) - } - )} - - - - {activeTab === SaleDetailsPageTab.categories ? ( - { + const handleChannelChange = createSaleChannelsChangeHandler( + data.channelListings, + onChannelsChange, + triggerChange + ); + const formDisabled = data.channelListings?.some(channel => + validatePrice(channel.discountValue) + ); + return ( + + + {intl.formatMessage(sectionNames.sales)} + + sale.name)} /> + +
+ - ) : activeTab === SaleDetailsPageTab.collections ? ( - + + + - ) : ( - + + + {intl.formatMessage( + { + defaultMessage: "Categories ({quantity})", + description: "number of categories", + id: "saleDetailsPageCategoriesQuantity" + }, + { + quantity: maybe( + () => sale.categories.totalCount.toString(), + "…" + ) + } + )} + + + {intl.formatMessage( + { + defaultMessage: "Collections ({quantity})", + description: "number of collections", + id: "saleDetailsPageCollectionsQuantity" + }, + { + quantity: maybe( + () => sale.collections.totalCount.toString(), + "…" + ) + } + )} + + + {intl.formatMessage( + { + defaultMessage: "Products ({quantity})", + description: "number of products", + id: "saleDetailsPageProductsQuantity" + }, + { + quantity: maybe( + () => sale.products.totalCount.toString(), + "…" + ) + } + )} + + + + {activeTab === SaleDetailsPageTab.categories ? ( + + ) : activeTab === SaleDetailsPageTab.collections ? ( + + ) : ( + + )} + + - )} - - -
-
- -
-
- -
- )} +
+
+ + + ({ + id: channel.id, + name: channel.name + }))} + disabled={disabled} + openModal={openChannelsModal} + /> +
+
+ +
+ ); + }}
); }; diff --git a/src/discounts/components/SaleList/SaleList.tsx b/src/discounts/components/SaleList/SaleList.tsx index 6f640d982..1de183138 100644 --- a/src/discounts/components/SaleList/SaleList.tsx +++ b/src/discounts/components/SaleList/SaleList.tsx @@ -26,8 +26,8 @@ export interface SaleListProps extends ListProps, ListActions, SortPage { - defaultCurrency: string; sales: SaleList_sales_edges_node[]; + selectedChannel: string; } const useStyles = makeStyles( @@ -68,7 +68,6 @@ const numberOfColumns = 5; const SaleList: React.FC = props => { const { settings, - defaultCurrency, disabled, onNextPage, onPreviousPage, @@ -77,6 +76,7 @@ const SaleList: React.FC = props => { onSort, pageInfo, sales, + selectedChannel, isChecked, selected, sort, @@ -169,7 +169,9 @@ const SaleList: React.FC = props => { sales, sale => { const isSelected = sale ? isChecked(sale.id) : false; - + const channel = sale?.channelListings?.find( + lisiting => lisiting.channel.id === selectedChannel + ); return ( = props => { className={classes.colValue} onClick={sale ? onRowClick(sale.id) : undefined} > - {sale && sale.type && sale.value ? ( + {sale?.type && channel?.discountValue ? ( sale.type === SaleType.FIXED ? ( + ) : channel?.discountValue ? ( + ) : ( - + "-" ) + ) : sale && !channel ? ( + "_" ) : ( )} diff --git a/src/discounts/components/SaleListPage/SaleListPage.tsx b/src/discounts/components/SaleListPage/SaleListPage.tsx index 96f593619..5882187c5 100644 --- a/src/discounts/components/SaleListPage/SaleListPage.tsx +++ b/src/discounts/components/SaleListPage/SaleListPage.tsx @@ -1,5 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import CardMenu from "@saleor/components/CardMenu"; import Container from "@saleor/components/Container"; import FilterBar from "@saleor/components/FilterBar"; import PageHeader from "@saleor/components/PageHeader"; @@ -29,12 +31,20 @@ export interface SaleListPageProps FilterPageProps, SortPage, TabPageProps { - defaultCurrency: string; sales: SaleList_sales_edges_node[]; + selectedChannel: string; + onSettingsOpen?: () => void; } +const useStyles = makeStyles( + theme => ({ + settings: { + marginRight: theme.spacing(2) + } + }), + { name: "SaleListPage" } +); const SaleListPage: React.FC = ({ - currencySymbol, currentTab, filterOpts, initialSearch, @@ -42,6 +52,7 @@ const SaleListPage: React.FC = ({ onAll, onFilterChange, onSearchChange, + onSettingsOpen, onTabChange, onTabDelete, onTabSave, @@ -49,12 +60,26 @@ const SaleListPage: React.FC = ({ ...listProps }) => { const intl = useIntl(); - + const classes = useStyles({}); const structure = createFilterStructure(intl, filterOpts); return ( + {!!onSettingsOpen && ( + + )} @@ -65,7 +90,6 @@ const SaleListPage: React.FC = ({ defaultMessage: "All Sales", description: "tab name" })} - currencySymbol={currencySymbol} currentTab={currentTab} filterStructure={structure} initialSearch={initialSearch} diff --git a/src/discounts/components/SaleSummary/SaleSummary.tsx b/src/discounts/components/SaleSummary/SaleSummary.tsx index 8c5787386..39097b43b 100644 --- a/src/discounts/components/SaleSummary/SaleSummary.tsx +++ b/src/discounts/components/SaleSummary/SaleSummary.tsx @@ -18,13 +18,16 @@ import { SaleType } from "../../../types/globalTypes"; import { SaleDetails_sale } from "../../types/SaleDetails"; export interface SaleSummaryProps { - defaultCurrency: string; + selectedChannel: string; sale: SaleDetails_sale; } -const SaleSummary: React.FC = ({ defaultCurrency, sale }) => { +const SaleSummary: React.FC = ({ selectedChannel, sale }) => { const intl = useIntl(); + const channel = sale?.channelListings?.find( + listing => listing.channel.id === selectedChannel + ); return ( @@ -41,18 +44,20 @@ const SaleSummary: React.FC = ({ defaultCurrency, sale }) => { - {maybe( - () => - sale.type === SaleType.FIXED ? ( - - ) : ( - - ), + {sale ? ( + sale.type === SaleType.FIXED && channel?.discountValue ? ( + + ) : channel?.discountValue ? ( + + ) : ( + "-" + ) + ) : ( )} diff --git a/src/discounts/components/SaleValue/SaleValue.tsx b/src/discounts/components/SaleValue/SaleValue.tsx index 1112365f6..73d84fc9c 100644 --- a/src/discounts/components/SaleValue/SaleValue.tsx +++ b/src/discounts/components/SaleValue/SaleValue.tsx @@ -1,34 +1,42 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; import CardTitle from "@saleor/components/CardTitle"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import TableHead from "@saleor/components/TableHead"; import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFragment"; -import { FormChange } from "@saleor/hooks/useForm"; +import { renderCollection } from "@saleor/misc"; import { SaleType } from "@saleor/types/globalTypes"; import { getFormErrors } from "@saleor/utils/errors"; import getDiscountErrorMessage from "@saleor/utils/errors/discounts"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import { SaleDetailsPageFormData } from "../SaleDetailsPage"; +import { useStyles } from "./styles"; export interface SaleValueProps { - currencySymbol: string; data: SaleDetailsPageFormData; disabled: boolean; errors: DiscountErrorFragment[]; - onChange: FormChange; + onChange: (channelId: string, discountValue: string) => void; } +const numberOfColumns = 2; + const SaleValue: React.FC = ({ - currencySymbol, data, disabled, errors, onChange }) => { const intl = useIntl(); - + const classes = useStyles({}); const formErrors = getFormErrors(["value"], errors); return ( @@ -39,23 +47,97 @@ const SaleValue: React.FC = ({ description: "sale value, header" })} /> - - + + + + +
+ + + + + + + + + + + + + + + {renderCollection( + data.channelListings, + (listing, index) => { + const error = formErrors.value?.channels?.find( + id => id === listing.id + ); + return ( + + + {listing?.name || } + + + {listing ? ( + onChange(listing.id, e.target.value)} + label={intl.formatMessage({ + defaultMessage: "Discount Value", + description: "sale discount" + })} + value={listing.discountValue || ""} + type="number" + fullWidth + inputProps={{ + min: 0 + }} + InputProps={{ + endAdornment: + data.type === SaleType.FIXED + ? listing.currency + : "%" + }} + /> + ) : ( + + )} + + + ); + }, + () => ( + + + + + + ) + )} + + +
); diff --git a/src/discounts/components/SaleValue/styles.ts b/src/discounts/components/SaleValue/styles.ts new file mode 100644 index 000000000..b165c5364 --- /dev/null +++ b/src/discounts/components/SaleValue/styles.ts @@ -0,0 +1,44 @@ +import { makeStyles } from "@material-ui/core/styles"; + +export const useStyles = makeStyles( + theme => ({ + card: { + "&:last-child": { + paddingBottom: 0 + } + }, + colName: { + fontSize: 14, + paddingLeft: 0, + width: "auto" + }, + colPrice: { + minWidth: 240 + }, + colType: { + fontSize: 14, + textAlign: "right", + width: 200 + }, + info: { + fontSize: 14 + }, + row: { + "&:last-child": { + "& td": { + borderBottom: "none" + } + } + }, + table: { + tableLayout: "fixed" + }, + tableContainer: { + margin: theme.spacing(0, -3), + width: `calc(100% + ${theme.spacing(6)}px)` + } + }), + { + name: "SaleValue" + } +); diff --git a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx index 65899494e..3e97b6f81 100644 --- a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx +++ b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx @@ -1,13 +1,17 @@ +import { ChannelVoucherData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; +import ChannelsAvailability from "@saleor/components/ChannelsAvailability"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { createChannelsChangeHandler } from "@saleor/discounts/handlers"; import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFragment"; import { sectionNames } from "@saleor/intl"; +import { validatePrice } from "@saleor/products/utils/validation"; import React from "react"; import { useIntl } from "react-intl"; @@ -26,6 +30,7 @@ import VoucherValue from "../VoucherValue"; export interface FormData { applyOncePerCustomer: boolean; applyOncePerOrder: boolean; + channelListings: ChannelVoucherData[]; code: string; discountType: DiscountValueTypeEnum; endDate: string; @@ -33,7 +38,6 @@ export interface FormData { hasEndDate: boolean; hasUsageLimit: boolean; minCheckoutItemsQuantity: string; - minSpent: string; requirementsPicker: RequirementsPicker; startDate: string; startTime: string; @@ -43,27 +47,36 @@ export interface FormData { } export interface VoucherCreatePageProps { - defaultCurrency: string; + allChannelsCount: number; + channelListings: ChannelVoucherData[]; + hasChannelChanged: boolean; disabled: boolean; errors: DiscountErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; + onChannelsChange: (data: ChannelVoucherData[]) => void; + openChannelsModal: () => void; onSubmit: (data: FormData) => void; } const VoucherCreatePage: React.FC = ({ - defaultCurrency, + allChannelsCount, + channelListings = [], disabled, errors, saveButtonBarState, onBack, - onSubmit + onChannelsChange, + onSubmit, + hasChannelChanged, + openChannelsModal }) => { const intl = useIntl(); const initialForm: FormData = { applyOncePerCustomer: false, applyOncePerOrder: false, + channelListings, code: "", discountType: DiscountValueTypeEnum.FIXED, endDate: "", @@ -71,7 +84,6 @@ const VoucherCreatePage: React.FC = ({ hasEndDate: false, hasUsageLimit: false, minCheckoutItemsQuantity: "0", - minSpent: "0", requirementsPicker: RequirementsPicker.NONE, startDate: "", startTime: "", @@ -82,77 +94,105 @@ const VoucherCreatePage: React.FC = ({ return (
- {({ change, data, hasChanged, submit }) => ( - - - {intl.formatMessage(sectionNames.vouchers)} - - - -
- - - - {data.discountType.toString() !== "SHIPPING" ? ( - { + const handleChannelChange = createChannelsChangeHandler( + data.channelListings, + onChannelsChange, + triggerChange + ); + const formDisabled = data.channelListings?.some( + channel => + validatePrice(channel.discountValue) || + (data.requirementsPicker === RequirementsPicker.ORDER && + validatePrice(channel.minSpent)) + ); + return ( + + + {intl.formatMessage(sectionNames.vouchers)} + + + +
+ - ) : null} - - - - - - -
-
- -
- )} + + + {data.discountType.toString() !== "SHIPPING" ? ( + <> + + + + ) : null} + + + + + + +
+
+ ({ + id: channel.id, + name: channel.name + }))} + disabled={disabled} + openModal={openChannelsModal} + /> +
+
+ +
+ ); + }}
); }; diff --git a/src/discounts/components/VoucherDates/VoucherDates.tsx b/src/discounts/components/VoucherDates/VoucherDates.tsx index 3589aa142..9bec1f4c9 100644 --- a/src/discounts/components/VoucherDates/VoucherDates.tsx +++ b/src/discounts/components/VoucherDates/VoucherDates.tsx @@ -15,7 +15,6 @@ import { VoucherDetailsPageFormData } from "../VoucherDetailsPage"; interface VoucherDatesProps { data: VoucherDetailsPageFormData; - defaultCurrency: string; disabled: boolean; errors: DiscountErrorFragment[]; onChange: (event: React.ChangeEvent) => void; diff --git a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx index 660a8196c..0fbaafe0f 100644 --- a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx +++ b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx @@ -1,6 +1,8 @@ import Typography from "@material-ui/core/Typography"; +import { ChannelVoucherData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; +import ChannelsAvailability from "@saleor/components/ChannelsAvailability"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import CountryList from "@saleor/components/CountryList"; @@ -9,9 +11,11 @@ import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { Tab, TabContainer } from "@saleor/components/Tab"; +import { createChannelsChangeHandler } from "@saleor/discounts/handlers"; import { RequirementsPicker } from "@saleor/discounts/types"; import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFragment"; import { sectionNames } from "@saleor/intl"; +import { validatePrice } from "@saleor/products/utils/validation"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -50,20 +54,19 @@ export function voucherDetailsPageTab(tab: string): VoucherDetailsPageTab { export interface VoucherDetailsPageFormData { applyOncePerCustomer: boolean; applyOncePerOrder: boolean; + channelListings: ChannelVoucherData[]; code: string; discountType: DiscountValueTypeEnum; endDate: string; endTime: string; hasEndDate: boolean; hasUsageLimit: boolean; - minSpent: string; minCheckoutItemsQuantity: string; requirementsPicker: RequirementsPicker; startDate: string; startTime: string; type: VoucherTypeEnum; usageLimit: string; - value: number; } export interface VoucherDetailsPageProps @@ -72,10 +75,13 @@ export interface VoucherDetailsPageProps "categoryListToolbar" | "collectionListToolbar" | "productListToolbar" > { activeTab: VoucherDetailsPageTab; - defaultCurrency: string; errors: DiscountErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; voucher: VoucherDetails_voucher; + selectedChannel: string; + allChannelsCount: number; + channelListings: ChannelVoucherData[]; + hasChannelChanged: boolean; onBack: () => void; onCategoryAssign: () => void; onCategoryUnassign: (id: string) => void; @@ -91,6 +97,8 @@ export interface VoucherDetailsPageProps onRemove: () => void; onSubmit: (data: VoucherDetailsPageFormData) => void; onTabClick: (index: VoucherDetailsPageTab) => void; + onChannelsChange: (data: ChannelVoucherData[]) => void; + openChannelsModal: () => void; } const CategoriesTab = Tab(VoucherDetailsPageTab.categories); @@ -99,7 +107,8 @@ const ProductsTab = Tab(VoucherDetailsPageTab.products); const VoucherDetailsPage: React.FC = ({ activeTab, - defaultCurrency, + allChannelsCount, + channelListings = [], disabled, errors, pageInfo, @@ -109,6 +118,7 @@ const VoucherDetailsPage: React.FC = ({ onCategoryAssign, onCategoryClick, onCategoryUnassign, + onChannelsChange, onCountryAssign, onCountryUnassign, onCollectionAssign, @@ -120,31 +130,37 @@ const VoucherDetailsPage: React.FC = ({ onProductClick, onProductUnassign, onTabClick, + hasChannelChanged, + openChannelsModal, onRemove, onSubmit, toggle, toggleAll, selected, + selectedChannel, isChecked, categoryListToolbar, collectionListToolbar, productListToolbar }) => { const intl = useIntl(); - + const channel = voucher?.channelListings?.find( + listing => listing.channel.id === selectedChannel + ); let requirementsPickerInitValue; - if (maybe(() => voucher.minSpent.amount) > 0) { - requirementsPickerInitValue = RequirementsPicker.ORDER; - } else if (maybe(() => voucher.minCheckoutItemsQuantity) > 0) { + if (voucher?.minCheckoutItemsQuantity > 0) { requirementsPickerInitValue = RequirementsPicker.ITEM; + } else if (channel?.minSpent?.amount > 0) { + requirementsPickerInitValue = RequirementsPicker.ORDER; } else { requirementsPickerInitValue = RequirementsPicker.NONE; } const initialForm: VoucherDetailsPageFormData = { - applyOncePerCustomer: maybe(() => voucher.applyOncePerCustomer, false), - applyOncePerOrder: maybe(() => voucher.applyOncePerOrder, false), - code: maybe(() => voucher.code, ""), + applyOncePerCustomer: voucher?.applyOncePerCustomer || false, + applyOncePerOrder: voucher?.applyOncePerOrder || false, + channelListings, + code: voucher?.code || "", discountType: maybe( () => voucher.discountValueType, DiscountValueTypeEnum.FIXED @@ -157,223 +173,251 @@ const VoucherDetailsPage: React.FC = ({ () => voucher.minCheckoutItemsQuantity.toString(), "0" ), - minSpent: maybe(() => voucher.minSpent.amount.toString(), "0"), requirementsPicker: requirementsPickerInitValue, startDate: splitDateTime(maybe(() => voucher.startDate, "")).date, startTime: splitDateTime(maybe(() => voucher.startDate, "")).time, type: maybe(() => voucher.type, VoucherTypeEnum.ENTIRE_ORDER), - usageLimit: maybe(() => voucher.usageLimit.toString(), "0"), - value: maybe(() => voucher.discountValue, 0) + usageLimit: maybe(() => voucher.usageLimit.toString(), "0") }; return (
- {({ change, data, hasChanged, submit }) => ( - - - {intl.formatMessage(sectionNames.vouchers)} - - voucher.code)} /> - -
- - - - - {data.discountType.toString() !== "SHIPPING" ? ( - { + const handleChannelChange = createChannelsChangeHandler( + data.channelListings, + onChannelsChange, + triggerChange + ); + const formDisabled = data.channelListings?.some( + channel => + validatePrice(channel.discountValue) || + (data.requirementsPicker === RequirementsPicker.ORDER && + validatePrice(channel.minSpent)) + ); + return ( + + + {intl.formatMessage(sectionNames.vouchers)} + + + +
+ - ) : null} - - {data.type === VoucherTypeEnum.SPECIFIC_PRODUCT && - data.discountType.toString() !== "SHIPPING" ? ( - <> - - - {intl.formatMessage( - { - defaultMessage: "Categories ({quantity})", - description: "number of categories" - }, - { - quantity: maybe( - () => voucher.categories.totalCount.toString(), - "…" - ) - } - )} - - - {intl.formatMessage( - { - defaultMessage: "Collections ({quantity})", - description: "number of collections" - }, - { - quantity: maybe( - () => voucher.collections.totalCount.toString(), - "…" - ) - } - )} - - - {intl.formatMessage( - { - defaultMessage: "Products ({quantity})", - description: "number of products" - }, - { - quantity: maybe( - () => voucher.products.totalCount.toString(), - "…" - ) - } - )} - - - - {activeTab === VoucherDetailsPageTab.categories ? ( - - ) : activeTab === VoucherDetailsPageTab.collections ? ( - - ) : ( - - )} - - ) : null} - - {data.discountType.toString() === "SHIPPING" ? ( - voucher.countries)} + + - {intl.formatMessage({ - defaultMessage: "Countries", - description: "voucher country range" - })} - - - - - } - onCountryAssign={onCountryAssign} - onCountryUnassign={onCountryUnassign} + errors={errors} + onChange={change} /> - ) : null} - - - - - - -
-
- -
-
- -
- )} + + {data.discountType.toString() !== "SHIPPING" ? ( + + ) : null} + + {data.type === VoucherTypeEnum.SPECIFIC_PRODUCT && + data.discountType.toString() !== "SHIPPING" ? ( + <> + + + {intl.formatMessage( + { + defaultMessage: "Categories ({quantity})", + description: "number of categories" + }, + { + quantity: maybe( + () => voucher.categories.totalCount.toString(), + "…" + ) + } + )} + + + {intl.formatMessage( + { + defaultMessage: "Collections ({quantity})", + description: "number of collections" + }, + { + quantity: maybe( + () => voucher.collections.totalCount.toString(), + "…" + ) + } + )} + + + {intl.formatMessage( + { + defaultMessage: "Products ({quantity})", + description: "number of products" + }, + { + quantity: maybe( + () => voucher.products.totalCount.toString(), + "…" + ) + } + )} + + + + {activeTab === VoucherDetailsPageTab.categories ? ( + + ) : activeTab === VoucherDetailsPageTab.collections ? ( + + ) : ( + + )} + + ) : null} + + {data.discountType.toString() === "SHIPPING" ? ( + voucher.countries)} + disabled={disabled} + emptyText={intl.formatMessage({ + defaultMessage: "Voucher applies to all countries" + })} + title={ + <> + {intl.formatMessage({ + defaultMessage: "Countries", + description: "voucher country range" + })} + + + + + } + onCountryAssign={onCountryAssign} + onCountryUnassign={onCountryUnassign} + /> + ) : null} + + + + + + +
+
+ + + ({ + id: channel.id, + name: channel.name + }))} + disabled={disabled} + openModal={openChannelsModal} + /> +
+
+ +
+ ); + }}
); }; diff --git a/src/discounts/components/VoucherLimits/VoucherLimits.tsx b/src/discounts/components/VoucherLimits/VoucherLimits.tsx index 35a0bb0c3..970a3529e 100644 --- a/src/discounts/components/VoucherLimits/VoucherLimits.tsx +++ b/src/discounts/components/VoucherLimits/VoucherLimits.tsx @@ -13,7 +13,6 @@ import { VoucherDetailsPageFormData } from "../VoucherDetailsPage"; interface VoucherLimitsProps { data: VoucherDetailsPageFormData; - defaultCurrency: string; disabled: boolean; errors: DiscountErrorFragment[]; onChange: (event: React.ChangeEvent) => void; diff --git a/src/discounts/components/VoucherList/VoucherList.tsx b/src/discounts/components/VoucherList/VoucherList.tsx index db0536d7a..c1fa415cd 100644 --- a/src/discounts/components/VoucherList/VoucherList.tsx +++ b/src/discounts/components/VoucherList/VoucherList.tsx @@ -27,8 +27,8 @@ export interface VoucherListProps extends ListProps, ListActions, SortPage { - defaultCurrency: string; vouchers: VoucherList_vouchers_edges_node[]; + selectedChannel: string; } const useStyles = makeStyles( @@ -84,7 +84,6 @@ const numberOfColumns = 6; const VoucherList: React.FC = props => { const { settings, - defaultCurrency, disabled, onNextPage, onPreviousPage, @@ -95,6 +94,7 @@ const VoucherList: React.FC = props => { vouchers, isChecked, selected, + selectedChannel, sort, toggle, toggleAll, @@ -218,7 +218,9 @@ const VoucherList: React.FC = props => { vouchers, voucher => { const isSelected = voucher ? isChecked(voucher.id) : false; - + const channel = voucher?.channelListings?.find( + listing => listing.channel.id === selectedChannel + ); return ( = props => { {maybe(() => voucher.code, )}
- {voucher && voucher.minSpent ? ( - - ) : voucher && voucher.minSpent === null ? ( + {channel?.minSpent ? ( + + ) : channel && channel.minSpent === null ? ( "-" ) : ( )} - {voucher && voucher.startDate ? ( + {voucher?.startDate ? ( ) : ( )} - {voucher && voucher.endDate ? ( + {voucher?.endDate ? ( ) : voucher && voucher.endDate === null ? ( "-" @@ -269,17 +271,19 @@ const VoucherList: React.FC = props => { > {voucher && voucher.discountValueType && - voucher.discountValue ? ( + channel?.discountValue ? ( voucher.discountValueType === DiscountValueTypeEnum.FIXED ? ( + ) : channel?.discountValue ? ( + ) : ( - + "-" ) ) : ( diff --git a/src/discounts/components/VoucherListPage/VoucherListPage.tsx b/src/discounts/components/VoucherListPage/VoucherListPage.tsx index 11193de57..8eb2ece05 100644 --- a/src/discounts/components/VoucherListPage/VoucherListPage.tsx +++ b/src/discounts/components/VoucherListPage/VoucherListPage.tsx @@ -1,5 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import CardMenu from "@saleor/components/CardMenu"; import Container from "@saleor/components/Container"; import FilterBar from "@saleor/components/FilterBar"; import PageHeader from "@saleor/components/PageHeader"; @@ -29,12 +31,20 @@ export interface VoucherListPageProps FilterPageProps, SortPage, TabPageProps { - defaultCurrency: string; vouchers: VoucherList_vouchers_edges_node[]; + selectedChannel: string; + onSettingsOpen?: () => void; } +const useStyles = makeStyles( + theme => ({ + settings: { + marginRight: theme.spacing(2) + } + }), + { name: "VoucherListPage" } +); const VoucherListPage: React.FC = ({ - currencySymbol, currentTab, filterOpts, initialSearch, @@ -42,6 +52,7 @@ const VoucherListPage: React.FC = ({ onAll, onFilterChange, onSearchChange, + onSettingsOpen, onTabChange, onTabDelete, onTabSave, @@ -49,12 +60,26 @@ const VoucherListPage: React.FC = ({ ...listProps }) => { const intl = useIntl(); - + const classes = useStyles({}); const structure = createFilterStructure(intl, filterOpts); return ( + {onSettingsOpen && ( + + )} - } - collectionListToolbar={ - - } - productListToolbar={ - - } - isChecked={isSelected} - selected={listElements.length} - toggle={toggle} - toggleAll={toggleAll} - /> - - saleCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - products: products.map( - product => product.id - ) - } - } - }) - } - products={maybe(() => - searchProductsOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedProduct => suggestedProduct.id - ) - )} - /> - - searchCategoriesOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedCategory => suggestedCategory.id - ) - )} - confirmButtonState={saleCataloguesAddOpts.status} - hasMore={ - searchCategoriesOpts.data?.search.pageInfo - .hasNextPage - } - open={params.action === "assign-category"} - onFetch={searchCategories} - onFetchMore={loadMoreCategories} - loading={searchCategoriesOpts.loading} - onClose={closeModal} - onSubmit={categories => - saleCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - categories - } - } - }) - } - /> - - searchCollectionsOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedCategory => suggestedCategory.id - ) - )} - confirmButtonState={saleCataloguesAddOpts.status} - hasMore={ - searchCollectionsOpts.data?.search.pageInfo - .hasNextPage - } - open={params.action === "assign-collection"} - onFetch={searchCollections} - onFetchMore={loadMoreCollections} - loading={searchCollectionsOpts.loading} - onClose={closeModal} - onSubmit={collections => - saleCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - collections - } - } - }) - } - /> - - handleCategoriesUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - handleCollectionsUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - handleProductsUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - saleDelete({ - variables: { id } - }) - } - > - - - {maybe(() => data.sale.name, "...")} - - ) - }} - /> - - - - ); - }} - - )} - - )} - - )} - - )} - - ); -}; -export default SaleDetails; diff --git a/src/discounts/views/SaleDetails/SaleDetails.tsx b/src/discounts/views/SaleDetails/SaleDetails.tsx new file mode 100644 index 000000000..468eb1b8e --- /dev/null +++ b/src/discounts/views/SaleDetails/SaleDetails.tsx @@ -0,0 +1,589 @@ +import Button from "@material-ui/core/Button"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import { categoryUrl } from "@saleor/categories/urls"; +import { useChannelsList } from "@saleor/channels/queries"; +import { + ChannelSaleData, + createChannelsDataWithSaleDiscountPrice, + createSortedChannelsDataFromSale +} from "@saleor/channels/utils"; +import { collectionUrl } from "@saleor/collections/urls"; +import ActionDialog from "@saleor/components/ActionDialog"; +import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog"; +import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog"; +import AssignProductDialog from "@saleor/components/AssignProductDialog"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config"; +import SaleDetailsPage, { + SaleDetailsPageTab +} from "@saleor/discounts/components/SaleDetailsPage"; +import { + TypedSaleCataloguesAdd, + TypedSaleCataloguesRemove, + TypedSaleDelete, + TypedSaleUpdate, + useSaleChannelListingUpdate +} from "@saleor/discounts/mutations"; +import { useSaleDetails } from "@saleor/discounts/queries"; +import { SaleCataloguesAdd } from "@saleor/discounts/types/SaleCataloguesAdd"; +import { SaleCataloguesRemove } from "@saleor/discounts/types/SaleCataloguesRemove"; +import { SaleDelete } from "@saleor/discounts/types/SaleDelete"; +import { SaleUpdate } from "@saleor/discounts/types/SaleUpdate"; +import { + saleListUrl, + saleUrl, + SaleUrlDialog, + SaleUrlQueryParams +} from "@saleor/discounts/urls"; +import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannels from "@saleor/hooks/useChannels"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; +import { commonMessages, sectionNames } from "@saleor/intl"; +import { maybe } from "@saleor/misc"; +import { productUrl } from "@saleor/products/urls"; +import useCategorySearch from "@saleor/searches/useCategorySearch"; +import useCollectionSearch from "@saleor/searches/useCollectionSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { createUpdateHandler } from "./handlers"; + +interface SaleDetailsProps { + id: string; + params: SaleUrlQueryParams; +} + +export const SaleDetails: React.FC = ({ id, params }) => { + const navigate = useNavigator(); + const paginate = usePaginator(); + const notify = useNotifier(); + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + params.ids + ); + const intl = useIntl(); + const { + loadMore: loadMoreCategories, + search: searchCategories, + result: searchCategoriesOpts + } = useCategorySearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const { + loadMore: loadMoreCollections, + search: searchCollections, + result: searchCollectionsOpts + } = useCollectionSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + + const { data: channelsData } = useChannelsList({}); + + const paginationState = createPaginationState(PAGINATE_BY, params); + const changeTab = (tab: SaleDetailsPageTab) => { + reset(); + navigate( + saleUrl(id, { + activeTab: tab + }) + ); + }; + + const { data, loading } = useSaleDetails({ + displayLoader: true, + variables: { + id, + ...paginationState + } + }); + + const allChannels: ChannelSaleData[] = createChannelsDataWithSaleDiscountPrice( + data?.sale, + channelsData?.channels + ); + const saleChannelsChoices: ChannelSaleData[] = createSortedChannelsDataFromSale( + data?.sale + ); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(saleChannelsChoices); + + const [updateChannels, updateChannelsOpts] = useSaleChannelListingUpdate({}); + + const [selectedChannel] = useLocalStorage("salesListChannel", ""); + + const handleSaleDelete = (data: SaleDelete) => { + if (data.saleDelete.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage({ + defaultMessage: "Removed sale" + }) + }); + navigate(saleListUrl(), true); + } + }; + + const handleSaleUpdate = (data: SaleUpdate) => { + if (data.saleUpdate.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + } + }; + + const [openModal, closeModal] = createDialogActionHandlers< + SaleUrlDialog, + SaleUrlQueryParams + >(navigate, params => saleUrl(id, params), params); + + const handleCatalogueAdd = (data: SaleCataloguesAdd) => { + if (data.saleCataloguesAdd.errors.length === 0) { + closeModal(); + } + }; + + const handleCatalogueRemove = (data: SaleCataloguesRemove) => { + if (data.saleCataloguesRemove.errors.length === 0) { + closeModal(); + reset(); + } + }; + + const canOpenBulkActionDialog = maybe(() => params.ids.length > 0); + + return ( + <> + {!!allChannels?.length && ( + + )} + + {(saleCataloguesRemove, saleCataloguesRemoveOpts) => ( + + {(saleCataloguesAdd, saleCataloguesAddOpts) => ( + + {(saleUpdate, saleUpdateOpts) => ( + + {(saleDelete, saleDeleteOpts) => { + const tabPageInfo = + params.activeTab === SaleDetailsPageTab.categories + ? maybe(() => data.sale.categories.pageInfo) + : params.activeTab === SaleDetailsPageTab.collections + ? maybe(() => data.sale.collections.pageInfo) + : maybe(() => data.sale.products.pageInfo); + + const handleCategoriesUnassign = (ids: string[]) => + saleCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + categories: ids + } + } + }); + + const handleCollectionsUnassign = (ids: string[]) => + saleCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + collections: ids + } + } + }); + + const handleProductsUnassign = (ids: string[]) => + saleCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + products: ids + } + } + }); + + const { + loadNextPage, + loadPreviousPage, + pageInfo + } = paginate(tabPageInfo, paginationState, params); + + const handleSubmit = createUpdateHandler( + data?.sale, + saleChannelsChoices, + variables => saleUpdate({ variables }), + updateChannels + ); + return ( + <> + + data.sale)} + allChannelsCount={allChannels?.length} + channelListings={currentChannels} + hasChannelChanged={ + saleChannelsChoices?.length !== + currentChannels?.length + } + disabled={ + loading || + saleCataloguesRemoveOpts.loading || + updateChannelsOpts.loading + } + errors={[ + ...(saleUpdateOpts.data?.saleUpdate.errors || []), + ...(updateChannelsOpts.data + ?.saleChannelListingUpdate.errors || []) + ]} + selectedChannel={selectedChannel} + pageInfo={pageInfo} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onCategoryAssign={() => + openModal("assign-category") + } + onCategoryClick={id => () => + navigate(categoryUrl(id))} + onCollectionAssign={() => + openModal("assign-collection") + } + onCollectionUnassign={collectionId => + handleCollectionsUnassign([collectionId]) + } + onCategoryUnassign={categoryId => + handleCategoriesUnassign([categoryId]) + } + onCollectionClick={id => () => + navigate(collectionUrl(id))} + onProductAssign={() => openModal("assign-product")} + onProductUnassign={productId => + handleProductsUnassign([productId]) + } + onProductClick={id => () => + navigate(productUrl(id))} + activeTab={params.activeTab} + onBack={() => navigate(saleListUrl())} + onTabClick={changeTab} + onSubmit={handleSubmit} + onRemove={() => openModal("remove")} + saveButtonBarState={saleUpdateOpts.status} + categoryListToolbar={ + + } + collectionListToolbar={ + + } + productListToolbar={ + + } + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + /> + + saleCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + products: products.map( + product => product.id + ) + } + } + }) + } + products={maybe(() => + searchProductsOpts.data.search.edges + .map(edge => edge.node) + .filter(suggestedProduct => suggestedProduct.id) + )} + /> + + searchCategoriesOpts.data.search.edges + .map(edge => edge.node) + .filter( + suggestedCategory => suggestedCategory.id + ) + )} + confirmButtonState={saleCataloguesAddOpts.status} + hasMore={ + searchCategoriesOpts.data?.search.pageInfo + .hasNextPage + } + open={params.action === "assign-category"} + onFetch={searchCategories} + onFetchMore={loadMoreCategories} + loading={searchCategoriesOpts.loading} + onClose={closeModal} + onSubmit={categories => + saleCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + categories + } + } + }) + } + /> + + searchCollectionsOpts.data.search.edges + .map(edge => edge.node) + .filter( + suggestedCategory => suggestedCategory.id + ) + )} + confirmButtonState={saleCataloguesAddOpts.status} + hasMore={ + searchCollectionsOpts.data?.search.pageInfo + .hasNextPage + } + open={params.action === "assign-collection"} + onFetch={searchCollections} + onFetchMore={loadMoreCollections} + loading={searchCollectionsOpts.loading} + onClose={closeModal} + onSubmit={collections => + saleCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + collections + } + } + }) + } + /> + + handleCategoriesUnassign(params.ids) + } + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + + handleCollectionsUnassign(params.ids) + } + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + handleProductsUnassign(params.ids)} + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + + saleDelete({ + variables: { id } + }) + } + > + + + {maybe(() => data.sale.name, "...")} + + ) + }} + /> + + + + ); + }} + + )} + + )} + + )} + + + ); +}; +export default SaleDetails; diff --git a/src/discounts/views/SaleDetails/handlers.ts b/src/discounts/views/SaleDetails/handlers.ts new file mode 100644 index 000000000..2a0317778 --- /dev/null +++ b/src/discounts/views/SaleDetails/handlers.ts @@ -0,0 +1,51 @@ +import { ChannelSaleData } from "@saleor/channels/utils"; +import { SaleDetailsPageFormData } from "@saleor/discounts/components/SaleDetailsPage"; +import { getSaleChannelsVariables } from "@saleor/discounts/handlers"; +import { + SaleChannelListingUpdate, + SaleChannelListingUpdateVariables +} from "@saleor/discounts/types/SaleChannelListingUpdate"; +import { SaleDetails_sale } from "@saleor/discounts/types/SaleDetails"; +import { + SaleUpdate, + SaleUpdateVariables +} from "@saleor/discounts/types/SaleUpdate"; +import { joinDateTime } from "@saleor/misc"; +import { DiscountValueTypeEnum, SaleType } from "@saleor/types/globalTypes"; +import { MutationFetchResult } from "react-apollo"; + +function discountValueTypeEnum(type: SaleType): DiscountValueTypeEnum { + return type.toString() === DiscountValueTypeEnum.FIXED + ? DiscountValueTypeEnum.FIXED + : DiscountValueTypeEnum.PERCENTAGE; +} + +export function createUpdateHandler( + sale: SaleDetails_sale, + saleChannelsChoices: ChannelSaleData[], + updateSale: ( + variables: SaleUpdateVariables + ) => Promise>, + updateChannels: (options: { + variables: SaleChannelListingUpdateVariables; + }) => Promise> +) { + return (formData: SaleDetailsPageFormData) => { + const { id } = sale; + updateSale({ + id, + input: { + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + name: formData.name, + startDate: joinDateTime(formData.startDate, formData.startTime), + type: discountValueTypeEnum(formData.type) + } + }); + + updateChannels({ + variables: getSaleChannelsVariables(id, formData, saleChannelsChoices) + }); + }; +} diff --git a/src/discounts/views/SaleDetails/index.ts b/src/discounts/views/SaleDetails/index.ts new file mode 100644 index 000000000..4753415a0 --- /dev/null +++ b/src/discounts/views/SaleDetails/index.ts @@ -0,0 +1,2 @@ +export * from "./SaleDetails"; +export { default } from "./SaleDetails"; diff --git a/src/discounts/views/SaleList/SaleList.tsx b/src/discounts/views/SaleList/SaleList.tsx index d10865328..e58058bbd 100644 --- a/src/discounts/views/SaleList/SaleList.tsx +++ b/src/discounts/views/SaleList/SaleList.tsx @@ -1,6 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog"; import ActionDialog from "@saleor/components/ActionDialog"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { @@ -8,13 +9,13 @@ import SaveFilterTabDialog, { } from "@saleor/components/SaveFilterTabDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannelsSettings from "@saleor/hooks/useChannelsSettings"; import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages, sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; @@ -56,7 +57,6 @@ export const SaleList: React.FC = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); const paginate = usePaginator(); - const shop = useShop(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); @@ -65,6 +65,17 @@ export const SaleList: React.FC = ({ params }) => { ); const intl = useIntl(); + const [openModal, closeModal] = createDialogActionHandlers< + SaleListUrlDialog, + SaleListUrlQueryParams + >(navigate, saleListUrl, params); + + const { + channelChoices, + handleChannelSelectConfirm, + selectedChannel + } = useChannelsSettings("salesListChannel", { closeModal, openModal }); + const paginationState = createPaginationState(settings.rowNumber, params); const queryVariables = React.useMemo( () => ({ @@ -100,11 +111,6 @@ export const SaleList: React.FC = ({ params }) => { params }); - const [openModal, closeModal] = createDialogActionHandlers< - SaleListUrlDialog, - SaleListUrlQueryParams - >(navigate, saleListUrl, params); - const handleTabChange = (tab: number) => { reset(); navigate( @@ -147,7 +153,6 @@ export const SaleList: React.FC = ({ params }) => { }; const handleSort = createSortHandler(navigate, saleListUrl, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); return ( @@ -162,8 +167,17 @@ export const SaleList: React.FC = ({ params }) => { return ( <> + {!!channelChoices?.length && ( + + )} = ({ params }) => { onTabDelete={() => openModal("delete-search")} onTabSave={() => openModal("save-search")} tabs={tabs.map(tab => tab.name)} - defaultCurrency={maybe(() => shop.defaultCurrency)} sales={maybe(() => data.sales.edges.map(edge => edge.node))} settings={settings} disabled={loading} @@ -202,6 +215,12 @@ export const SaleList: React.FC = ({ params }) => { } + selectedChannel={selectedChannel} + onSettingsOpen={ + !!channelChoices?.length + ? () => openModal("settings") + : undefined + } /> { - const navigate = useNavigator(); - const notify = useNotifier(); - const shop = useShop(); - const intl = useIntl(); - - const handleVoucherCreate = (data: VoucherCreate) => { - if (data.voucherCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage({ - defaultMessage: "Successfully created voucher" - }) - }); - navigate(voucherUrl(data.voucherCreate.voucher.id), true); - } - }; - - return ( - - {(voucherCreate, voucherCreateOpts) => ( - <> - - shop.defaultCurrency)} - disabled={voucherCreateOpts.loading} - errors={voucherCreateOpts.data?.voucherCreate.errors || []} - onBack={() => navigate(voucherListUrl())} - onSubmit={formData => - voucherCreate({ - variables: { - input: { - applyOncePerCustomer: formData.applyOncePerCustomer, - applyOncePerOrder: formData.applyOncePerOrder, - code: formData.code, - discountValue: - formData.discountType.toString() === "SHIPPING" - ? 100 - : decimal(formData.value), - discountValueType: - formData.discountType.toString() === "SHIPPING" - ? DiscountValueTypeEnum.PERCENTAGE - : formData.discountType, - endDate: formData.hasEndDate - ? joinDateTime(formData.endDate, formData.endTime) - : null, - minAmountSpent: - formData.requirementsPicker !== RequirementsPicker.ORDER - ? 0 - : parseFloat(formData.minSpent), - minCheckoutItemsQuantity: - formData.requirementsPicker !== RequirementsPicker.ITEM - ? 0 - : parseFloat(formData.minCheckoutItemsQuantity), - startDate: joinDateTime( - formData.startDate, - formData.startTime - ), - type: - formData.discountType.toString() === "SHIPPING" - ? VoucherTypeEnum.ENTIRE_ORDER - : formData.type, - usageLimit: formData.hasUsageLimit - ? parseInt(formData.usageLimit, 10) - : null - } - } - }) - } - saveButtonBarState={voucherCreateOpts.status} - /> - - )} - - ); -}; -export default VoucherDetails; diff --git a/src/discounts/views/VoucherCreate/VoucherCreate.tsx b/src/discounts/views/VoucherCreate/VoucherCreate.tsx new file mode 100644 index 000000000..ca420e00d --- /dev/null +++ b/src/discounts/views/VoucherCreate/VoucherCreate.tsx @@ -0,0 +1,114 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { + ChannelVoucherData, + createSortedVoucherData +} from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useChannels from "@saleor/hooks/useChannels"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { sectionNames } from "@saleor/intl"; +import React from "react"; +import { useIntl } from "react-intl"; + +import VoucherCreatePage from "../../components/VoucherCreatePage"; +import { + TypedVoucherCreate, + useVoucherChannelListingUpdate +} from "../../mutations"; +import { VoucherCreate } from "../../types/VoucherCreate"; +import { voucherListUrl, voucherUrl } from "../../urls"; +import { createHandler } from "./handlers"; + +export const VoucherDetails: React.FC = () => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data: channelsData } = useChannelsList({}); + const allChannels: ChannelVoucherData[] = createSortedVoucherData( + channelsData?.channels + ); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(allChannels); + + const [updateChannels, updateChannelsOpts] = useVoucherChannelListingUpdate( + {} + ); + + const handleVoucherCreate = (data: VoucherCreate) => { + if (data.voucherCreate.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage({ + defaultMessage: "Successfully created voucher" + }) + }); + navigate(voucherUrl(data.voucherCreate.voucher.id), true); + } + }; + + return ( + + {(voucherCreate, voucherCreateOpts) => { + const handleSubmit = createHandler( + variables => voucherCreate({ variables }), + updateChannels + ); + return ( + <> + {!!allChannels?.length && ( + + )} + + navigate(voucherListUrl())} + onSubmit={handleSubmit} + saveButtonBarState={voucherCreateOpts.status} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} + /> + + ); + }} + + ); +}; +export default VoucherDetails; diff --git a/src/discounts/views/VoucherCreate/handlers.ts b/src/discounts/views/VoucherCreate/handlers.ts new file mode 100644 index 000000000..599484c6b --- /dev/null +++ b/src/discounts/views/VoucherCreate/handlers.ts @@ -0,0 +1,65 @@ +import { VoucherDetailsPageFormData } from "@saleor/discounts/components/VoucherDetailsPage"; +import { getChannelsVariables } from "@saleor/discounts/handlers"; +import { RequirementsPicker } from "@saleor/discounts/types"; +import { + VoucherChannelListingUpdate, + VoucherChannelListingUpdateVariables +} from "@saleor/discounts/types/VoucherChannelListingUpdate"; +import { + VoucherCreate, + VoucherCreateVariables +} from "@saleor/discounts/types/VoucherCreate"; +import { joinDateTime } from "@saleor/misc"; +import { + DiscountValueTypeEnum, + VoucherTypeEnum +} from "@saleor/types/globalTypes"; +import { MutationFetchResult } from "react-apollo"; + +export function createHandler( + voucherCreate: ( + variables: VoucherCreateVariables + ) => Promise>, + updateChannels: (options: { + variables: VoucherChannelListingUpdateVariables; + }) => Promise> +) { + return async (formData: VoucherDetailsPageFormData) => { + const response = await voucherCreate({ + input: { + applyOncePerCustomer: formData.applyOncePerCustomer, + applyOncePerOrder: formData.applyOncePerOrder, + code: formData.code, + discountValueType: + formData.discountType.toString() === "SHIPPING" + ? DiscountValueTypeEnum.PERCENTAGE + : formData.discountType, + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + minCheckoutItemsQuantity: + formData.requirementsPicker !== RequirementsPicker.ITEM + ? 0 + : parseFloat(formData.minCheckoutItemsQuantity), + startDate: joinDateTime(formData.startDate, formData.startTime), + type: + formData.discountType.toString() === "SHIPPING" + ? VoucherTypeEnum.SHIPPING + : formData.type, + usageLimit: formData.hasUsageLimit + ? parseInt(formData.usageLimit, 10) + : null + } + }); + + if (!response.data.voucherCreate.errors.length) { + updateChannels({ + variables: getChannelsVariables( + response.data.voucherCreate.voucher.id, + formData, + formData.channelListings + ) + }); + } + }; +} diff --git a/src/discounts/views/VoucherCreate/index.ts b/src/discounts/views/VoucherCreate/index.ts new file mode 100644 index 000000000..a8895e27a --- /dev/null +++ b/src/discounts/views/VoucherCreate/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherCreate"; +export * from "./VoucherCreate"; diff --git a/src/discounts/views/VoucherDetails.tsx b/src/discounts/views/VoucherDetails.tsx deleted file mode 100644 index d227497a2..000000000 --- a/src/discounts/views/VoucherDetails.tsx +++ /dev/null @@ -1,665 +0,0 @@ -import Button from "@material-ui/core/Button"; -import DialogContentText from "@material-ui/core/DialogContentText"; -import ActionDialog from "@saleor/components/ActionDialog"; -import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog"; -import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog"; -import AssignProductDialog from "@saleor/components/AssignProductDialog"; -import { WindowTitle } from "@saleor/components/WindowTitle"; -import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config"; -import useBulkActions from "@saleor/hooks/useBulkActions"; -import useNavigator from "@saleor/hooks/useNavigator"; -import useNotifier from "@saleor/hooks/useNotifier"; -import usePaginator, { - createPaginationState -} from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; -import { commonMessages, sectionNames } from "@saleor/intl"; -import useCategorySearch from "@saleor/searches/useCategorySearch"; -import useCollectionSearch from "@saleor/searches/useCollectionSearch"; -import useProductSearch from "@saleor/searches/useProductSearch"; -import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { categoryUrl } from "../../categories/urls"; -import { collectionUrl } from "../../collections/urls"; -import { decimal, joinDateTime, maybe } from "../../misc"; -import { productUrl } from "../../products/urls"; -import { - DiscountValueTypeEnum, - VoucherTypeEnum -} from "../../types/globalTypes"; -import DiscountCountrySelectDialog from "../components/DiscountCountrySelectDialog"; -import VoucherDetailsPage, { - VoucherDetailsPageFormData, - VoucherDetailsPageTab -} from "../components/VoucherDetailsPage"; -import { - TypedVoucherCataloguesAdd, - TypedVoucherCataloguesRemove, - TypedVoucherDelete, - TypedVoucherUpdate -} from "../mutations"; -import { TypedVoucherDetails } from "../queries"; -import { RequirementsPicker } from "../types"; -import { VoucherCataloguesAdd } from "../types/VoucherCataloguesAdd"; -import { VoucherCataloguesRemove } from "../types/VoucherCataloguesRemove"; -import { VoucherDelete } from "../types/VoucherDelete"; -import { VoucherUpdate } from "../types/VoucherUpdate"; -import { - voucherListUrl, - voucherUrl, - VoucherUrlDialog, - VoucherUrlQueryParams -} from "../urls"; - -interface VoucherDetailsProps { - id: string; - params: VoucherUrlQueryParams; -} - -export const VoucherDetails: React.FC = ({ - id, - params -}) => { - const navigate = useNavigator(); - const paginate = usePaginator(); - const notify = useNotifier(); - const shop = useShop(); - const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( - params.ids - ); - const intl = useIntl(); - const { - loadMore: loadMoreCategories, - search: searchCategories, - result: searchCategoriesOpts - } = useCategorySearch({ - variables: DEFAULT_INITIAL_SEARCH_DATA - }); - const { - loadMore: loadMoreCollections, - search: searchCollections, - result: searchCollectionsOpts - } = useCollectionSearch({ - variables: DEFAULT_INITIAL_SEARCH_DATA - }); - const { - loadMore: loadMoreProducts, - search: searchProducts, - result: searchProductsOpts - } = useProductSearch({ - variables: DEFAULT_INITIAL_SEARCH_DATA - }); - - const paginationState = createPaginationState(PAGINATE_BY, params); - const changeTab = (tab: VoucherDetailsPageTab) => { - reset(); - navigate( - voucherUrl(id, { - activeTab: tab - }) - ); - }; - - const handleVoucherDelete = (data: VoucherDelete) => { - if (data.voucherDelete.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage({ - defaultMessage: "Deleted voucher" - }) - }); - navigate(voucherListUrl(), true); - } - }; - - const handleVoucherUpdate = (data: VoucherUpdate) => { - if (data.voucherUpdate.errors.length === 0) { - closeModal(); - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - } - }; - - const [openModal, closeModal] = createDialogActionHandlers< - VoucherUrlDialog, - VoucherUrlQueryParams - >(navigate, params => voucherUrl(id, params), params); - - const handleCatalogueAdd = (data: VoucherCataloguesAdd) => { - if (data.voucherCataloguesAdd.errors.length === 0) { - closeModal(); - } - }; - - const handleCatalogueRemove = (data: VoucherCataloguesRemove) => { - if (data.voucherCataloguesRemove.errors.length === 0) { - closeModal(); - reset(); - } - }; - - const canOpenBulkActionDialog = maybe(() => params.ids.length > 0); - - return ( - - {(voucherCataloguesRemove, voucherCataloguesRemoveOpts) => ( - - {(voucherCataloguesAdd, voucherCataloguesAddOpts) => ( - - {(voucherUpdate, voucherUpdateOpts) => ( - - {(voucherDelete, voucherDeleteOpts) => ( - - {({ data, loading }) => { - const tabPageInfo = - params.activeTab === VoucherDetailsPageTab.categories - ? maybe(() => data.voucher.categories.pageInfo) - : params.activeTab === - VoucherDetailsPageTab.collections - ? maybe(() => data.voucher.collections.pageInfo) - : maybe(() => data.voucher.products.pageInfo); - - const handleCategoriesUnassign = (ids: string[]) => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - categories: ids - } - } - }); - - const handleCollectionsUnassign = (ids: string[]) => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - collections: ids - } - } - }); - - const handleProductsUnassign = (ids: string[]) => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - products: ids - } - } - }); - - const handleSubmit = async ( - data: VoucherDetailsPageFormData - ) => { - const result = await voucherUpdate({ - variables: { - id, - input: { - applyOncePerCustomer: data.applyOncePerCustomer, - applyOncePerOrder: data.applyOncePerOrder, - discountValue: - data.discountType.toString() === "SHIPPING" - ? 100 - : decimal(data.value), - discountValueType: - data.discountType.toString() === "SHIPPING" - ? DiscountValueTypeEnum.PERCENTAGE - : data.discountType, - endDate: data.hasEndDate - ? joinDateTime(data.endDate, data.endTime) - : null, - minAmountSpent: - data.requirementsPicker !== - RequirementsPicker.ORDER - ? 0 - : parseFloat(data.minSpent), - minCheckoutItemsQuantity: - data.requirementsPicker !== - RequirementsPicker.ITEM - ? 0 - : parseFloat(data.minCheckoutItemsQuantity), - startDate: joinDateTime( - data.startDate, - data.startTime - ), - type: - data.discountType.toString() === "SHIPPING" - ? VoucherTypeEnum.SHIPPING - : data.type, - usageLimit: data.hasUsageLimit - ? parseInt(data.usageLimit, 10) - : null - } - } - }); - - return result.data.voucherUpdate.errors; - }; - - const { - loadNextPage, - loadPreviousPage, - pageInfo - } = paginate(tabPageInfo, paginationState, params); - - return ( - <> - - shop.defaultCurrency - )} - voucher={maybe(() => data.voucher)} - disabled={ - loading || voucherCataloguesRemoveOpts.loading - } - errors={ - voucherUpdateOpts.data?.voucherUpdate.errors || - [] - } - pageInfo={pageInfo} - onNextPage={loadNextPage} - onPreviousPage={loadPreviousPage} - onCategoryAssign={() => - openModal("assign-category") - } - onCategoryClick={id => () => - navigate(categoryUrl(id))} - onCollectionAssign={() => - openModal("assign-collection") - } - onCollectionUnassign={collectionId => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - collections: [collectionId] - } - } - }) - } - onCountryAssign={() => - openModal("assign-country") - } - onCountryUnassign={countryCode => - voucherUpdate({ - variables: { - ...paginationState, - id, - input: { - countries: data.voucher.countries - .filter( - country => - country.code !== countryCode - ) - .map(country => country.code) - } - } - }) - } - onCategoryUnassign={categoryId => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - categories: [categoryId] - } - } - }) - } - onCollectionClick={id => () => - navigate(collectionUrl(id))} - onProductAssign={() => - openModal("assign-product") - } - onProductUnassign={productId => - voucherCataloguesRemove({ - variables: { - ...paginationState, - id, - input: { - products: [productId] - } - } - }) - } - onProductClick={id => () => - navigate(productUrl(id))} - activeTab={params.activeTab} - onBack={() => navigate(voucherListUrl())} - onTabClick={changeTab} - onSubmit={handleSubmit} - onRemove={() => openModal("remove")} - saveButtonBarState={voucherUpdateOpts.status} - categoryListToolbar={ - - } - collectionListToolbar={ - - } - productListToolbar={ - - } - isChecked={isSelected} - selected={listElements.length} - toggle={toggle} - toggleAll={toggleAll} - /> - - searchCategoriesOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedCategory => suggestedCategory.id - ) - )} - confirmButtonState={ - voucherCataloguesAddOpts.status - } - hasMore={ - searchCategoriesOpts.data?.search.pageInfo - .hasNextPage - } - open={params.action === "assign-category"} - onFetch={searchCategories} - onFetchMore={loadMoreCategories} - loading={searchCategoriesOpts.loading} - onClose={closeModal} - onSubmit={categories => - voucherCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - categories - } - } - }) - } - /> - - searchCollectionsOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedCategory => suggestedCategory.id - ) - )} - confirmButtonState={ - voucherCataloguesAddOpts.status - } - hasMore={ - searchCollectionsOpts.data?.search.pageInfo - .hasNextPage - } - open={params.action === "assign-collection"} - onFetch={searchCollections} - onFetchMore={loadMoreCollections} - loading={searchCollectionsOpts.loading} - onClose={closeModal} - onSubmit={collections => - voucherCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - collections - } - } - }) - } - /> - shop.countries, [])} - onClose={() => navigate(voucherUrl(id))} - onConfirm={formData => - voucherUpdate({ - variables: { - id, - input: { - countries: formData.countries - } - } - }) - } - open={params.action === "assign-country"} - initial={maybe( - () => - data.voucher.countries.map( - country => country.code - ), - [] - )} - /> - - voucherCataloguesAdd({ - variables: { - ...paginationState, - id, - input: { - products: products.map( - product => product.id - ) - } - } - }) - } - products={maybe(() => - searchProductsOpts.data.search.edges - .map(edge => edge.node) - .filter( - suggestedProduct => suggestedProduct.id - ) - )} - /> - - handleCategoriesUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - handleCollectionsUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - handleProductsUnassign(params.ids) - } - > - {canOpenBulkActionDialog && ( - - {params.ids.length} - ) - }} - /> - - )} - - - voucherDelete({ - variables: { id } - }) - } - > - - - {maybe(() => data.voucher.code, "...")} - - ) - }} - /> - - - - ); - }} - - )} - - )} - - )} - - )} - - ); -}; -export default VoucherDetails; diff --git a/src/discounts/views/VoucherDetails/VoucherDetails.tsx b/src/discounts/views/VoucherDetails/VoucherDetails.tsx new file mode 100644 index 000000000..cff685c59 --- /dev/null +++ b/src/discounts/views/VoucherDetails/VoucherDetails.tsx @@ -0,0 +1,676 @@ +import Button from "@material-ui/core/Button"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import { useChannelsList } from "@saleor/channels/queries"; +import { + ChannelVoucherData, + createChannelsDataWithDiscountPrice, + createSortedChannelsDataFromVoucher +} from "@saleor/channels/utils"; +import ActionDialog from "@saleor/components/ActionDialog"; +import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog"; +import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog"; +import AssignProductDialog from "@saleor/components/AssignProductDialog"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config"; +import DiscountCountrySelectDialog from "@saleor/discounts/components/DiscountCountrySelectDialog"; +import VoucherDetailsPage, { + VoucherDetailsPageTab +} from "@saleor/discounts/components/VoucherDetailsPage"; +import { + TypedVoucherCataloguesAdd, + TypedVoucherCataloguesRemove, + TypedVoucherDelete, + TypedVoucherUpdate, + useVoucherChannelListingUpdate +} from "@saleor/discounts/mutations"; +import { useVoucherDetails } from "@saleor/discounts/queries"; +import { VoucherCataloguesAdd } from "@saleor/discounts/types/VoucherCataloguesAdd"; +import { VoucherCataloguesRemove } from "@saleor/discounts/types/VoucherCataloguesRemove"; +import { VoucherDelete } from "@saleor/discounts/types/VoucherDelete"; +import { VoucherUpdate } from "@saleor/discounts/types/VoucherUpdate"; +import { + voucherListUrl, + voucherUrl, + VoucherUrlDialog, + VoucherUrlQueryParams +} from "@saleor/discounts/urls"; +import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannels from "@saleor/hooks/useChannels"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; +import useShop from "@saleor/hooks/useShop"; +import { commonMessages, sectionNames } from "@saleor/intl"; +import useCategorySearch from "@saleor/searches/useCategorySearch"; +import useCollectionSearch from "@saleor/searches/useCollectionSearch"; +import useProductSearch from "@saleor/searches/useProductSearch"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { categoryUrl } from "../../../categories/urls"; +import { collectionUrl } from "../../../collections/urls"; +import { maybe } from "../../../misc"; +import { productUrl } from "../../../products/urls"; +import { createUpdateHandler } from "./handlers"; + +interface VoucherDetailsProps { + id: string; + params: VoucherUrlQueryParams; +} + +export const VoucherDetails: React.FC = ({ + id, + params +}) => { + const navigate = useNavigator(); + const paginate = usePaginator(); + const notify = useNotifier(); + const shop = useShop(); + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + params.ids + ); + const intl = useIntl(); + const { + loadMore: loadMoreCategories, + search: searchCategories, + result: searchCategoriesOpts + } = useCategorySearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const { + loadMore: loadMoreCollections, + search: searchCollections, + result: searchCollectionsOpts + } = useCollectionSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + const { + loadMore: loadMoreProducts, + search: searchProducts, + result: searchProductsOpts + } = useProductSearch({ + variables: DEFAULT_INITIAL_SEARCH_DATA + }); + + const paginationState = createPaginationState(PAGINATE_BY, params); + const changeTab = (tab: VoucherDetailsPageTab) => { + reset(); + navigate( + voucherUrl(id, { + activeTab: tab + }) + ); + }; + + const { data, loading } = useVoucherDetails({ + displayLoader: true, + variables: { + id, + ...paginationState + } + }); + const { data: channelsData } = useChannelsList({}); + + const allChannels: ChannelVoucherData[] = createChannelsDataWithDiscountPrice( + data?.voucher, + channelsData?.channels + ); + const voucherChannelsChoices: ChannelVoucherData[] = createSortedChannelsDataFromVoucher( + data?.voucher + ); + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(voucherChannelsChoices); + + React.useEffect(() => { + if (!currentChannels.length && voucherChannelsChoices.length) { + setCurrentChannels(voucherChannelsChoices); + } + }, [voucherChannelsChoices]); + + const [updateChannels, updateChannelsOpts] = useVoucherChannelListingUpdate( + {} + ); + + const [selectedChannel] = useLocalStorage("vouchersListChannel", ""); + + const handleVoucherDelete = (data: VoucherDelete) => { + if (data.voucherDelete.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage({ + defaultMessage: "Deleted voucher" + }) + }); + navigate(voucherListUrl(), true); + } + }; + + const handleVoucherUpdate = (data: VoucherUpdate) => { + if (data.voucherUpdate.errors.length === 0) { + closeModal(); + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + } + }; + + const [openModal, closeModal] = createDialogActionHandlers< + VoucherUrlDialog, + VoucherUrlQueryParams + >(navigate, params => voucherUrl(id, params), params); + + const handleCatalogueAdd = (data: VoucherCataloguesAdd) => { + if (data.voucherCataloguesAdd.errors.length === 0) { + closeModal(); + } + }; + + const handleCatalogueRemove = (data: VoucherCataloguesRemove) => { + if (data.voucherCataloguesRemove.errors.length === 0) { + closeModal(); + reset(); + } + }; + + const canOpenBulkActionDialog = maybe(() => params.ids.length > 0); + + return ( + <> + {!!allChannels?.length && ( + + )} + + {(voucherCataloguesRemove, voucherCataloguesRemoveOpts) => ( + + {(voucherCataloguesAdd, voucherCataloguesAddOpts) => ( + + {(voucherUpdate, voucherUpdateOpts) => ( + + {(voucherDelete, voucherDeleteOpts) => { + const handleSubmit = createUpdateHandler( + data?.voucher, + voucherChannelsChoices, + variables => voucherUpdate({ variables }), + updateChannels + ); + + const tabPageInfo = + params.activeTab === VoucherDetailsPageTab.categories + ? maybe(() => data.voucher.categories.pageInfo) + : params.activeTab === + VoucherDetailsPageTab.collections + ? maybe(() => data.voucher.collections.pageInfo) + : maybe(() => data.voucher.products.pageInfo); + + const handleCategoriesUnassign = (ids: string[]) => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + categories: ids + } + } + }); + + const handleCollectionsUnassign = (ids: string[]) => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + collections: ids + } + } + }); + + const handleProductsUnassign = (ids: string[]) => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + products: ids + } + } + }); + + const { + loadNextPage, + loadPreviousPage, + pageInfo + } = paginate(tabPageInfo, paginationState, params); + + return ( + <> + + + openModal("assign-category") + } + onCategoryClick={id => () => + navigate(categoryUrl(id))} + onCollectionAssign={() => + openModal("assign-collection") + } + onCollectionUnassign={collectionId => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + collections: [collectionId] + } + } + }) + } + onCountryAssign={() => openModal("assign-country")} + onCountryUnassign={countryCode => + voucherUpdate({ + variables: { + ...paginationState, + id, + input: { + countries: data.voucher.countries + .filter( + country => country.code !== countryCode + ) + .map(country => country.code) + } + } + }) + } + onCategoryUnassign={categoryId => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + categories: [categoryId] + } + } + }) + } + onCollectionClick={id => () => + navigate(collectionUrl(id))} + onProductAssign={() => openModal("assign-product")} + onProductUnassign={productId => + voucherCataloguesRemove({ + variables: { + ...paginationState, + id, + input: { + products: [productId] + } + } + }) + } + onProductClick={id => () => + navigate(productUrl(id))} + activeTab={params.activeTab} + onBack={() => navigate(voucherListUrl())} + onTabClick={changeTab} + onSubmit={handleSubmit} + onRemove={() => openModal("remove")} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} + saveButtonBarState={voucherUpdateOpts.status} + categoryListToolbar={ + + } + collectionListToolbar={ + + } + productListToolbar={ + + } + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + /> + + searchCategoriesOpts.data.search.edges + .map(edge => edge.node) + .filter( + suggestedCategory => suggestedCategory.id + ) + )} + confirmButtonState={voucherCataloguesAddOpts.status} + hasMore={ + searchCategoriesOpts.data?.search.pageInfo + .hasNextPage + } + open={params.action === "assign-category"} + onFetch={searchCategories} + onFetchMore={loadMoreCategories} + loading={searchCategoriesOpts.loading} + onClose={closeModal} + onSubmit={categories => + voucherCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + categories + } + } + }) + } + /> + + searchCollectionsOpts.data.search.edges + .map(edge => edge.node) + .filter( + suggestedCategory => suggestedCategory.id + ) + )} + confirmButtonState={voucherCataloguesAddOpts.status} + hasMore={ + searchCollectionsOpts.data?.search.pageInfo + .hasNextPage + } + open={params.action === "assign-collection"} + onFetch={searchCollections} + onFetchMore={loadMoreCollections} + loading={searchCollectionsOpts.loading} + onClose={closeModal} + onSubmit={collections => + voucherCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + collections + } + } + }) + } + /> + shop.countries, [])} + onClose={() => navigate(voucherUrl(id))} + onConfirm={formData => + voucherUpdate({ + variables: { + id, + input: { + countries: formData.countries + } + } + }) + } + open={params.action === "assign-country"} + initial={maybe( + () => + data.voucher.countries.map( + country => country.code + ), + [] + )} + /> + + voucherCataloguesAdd({ + variables: { + ...paginationState, + id, + input: { + products: products.map( + product => product.id + ) + } + } + }) + } + products={maybe(() => + searchProductsOpts.data.search.edges + .map(edge => edge.node) + .filter(suggestedProduct => suggestedProduct.id) + )} + /> + + handleCategoriesUnassign(params.ids) + } + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + + handleCollectionsUnassign(params.ids) + } + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + handleProductsUnassign(params.ids)} + > + {canOpenBulkActionDialog && ( + + {params.ids.length} + ) + }} + /> + + )} + + + voucherDelete({ + variables: { id } + }) + } + > + + + {maybe(() => data.voucher.code, "...")} + + ) + }} + /> + + + + ); + }} + + )} + + )} + + )} + + + ); +}; +export default VoucherDetails; diff --git a/src/discounts/views/VoucherDetails/handlers.ts b/src/discounts/views/VoucherDetails/handlers.ts new file mode 100644 index 000000000..3814e7a06 --- /dev/null +++ b/src/discounts/views/VoucherDetails/handlers.ts @@ -0,0 +1,64 @@ +import { ChannelVoucherData } from "@saleor/channels/utils"; +import { VoucherDetailsPageFormData } from "@saleor/discounts/components/VoucherDetailsPage"; +import { getChannelsVariables } from "@saleor/discounts/handlers"; +import { RequirementsPicker } from "@saleor/discounts/types"; +import { + VoucherChannelListingUpdate, + VoucherChannelListingUpdateVariables +} from "@saleor/discounts/types/VoucherChannelListingUpdate"; +import { VoucherDetails_voucher } from "@saleor/discounts/types/VoucherDetails"; +import { + VoucherUpdate, + VoucherUpdateVariables +} from "@saleor/discounts/types/VoucherUpdate"; +import { joinDateTime } from "@saleor/misc"; +import { + DiscountValueTypeEnum, + VoucherTypeEnum +} from "@saleor/types/globalTypes"; +import { MutationFetchResult } from "react-apollo"; + +export function createUpdateHandler( + voucher: VoucherDetails_voucher, + voucherChannelsChoices: ChannelVoucherData[], + updateVoucher: ( + variables: VoucherUpdateVariables + ) => Promise>, + updateChannels: (options: { + variables: VoucherChannelListingUpdateVariables; + }) => Promise> +) { + return (formData: VoucherDetailsPageFormData) => { + const { id } = voucher; + updateVoucher({ + id, + input: { + applyOncePerCustomer: formData.applyOncePerCustomer, + applyOncePerOrder: formData.applyOncePerOrder, + discountValueType: + formData.discountType.toString() === "SHIPPING" + ? DiscountValueTypeEnum.PERCENTAGE + : formData.discountType, + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + minCheckoutItemsQuantity: + formData.requirementsPicker !== RequirementsPicker.ITEM + ? 0 + : parseFloat(formData.minCheckoutItemsQuantity), + startDate: joinDateTime(formData.startDate, formData.startTime), + type: + formData.discountType.toString() === "SHIPPING" + ? VoucherTypeEnum.SHIPPING + : formData.type, + usageLimit: formData.hasUsageLimit + ? parseInt(formData.usageLimit, 10) + : null + } + }); + + updateChannels({ + variables: getChannelsVariables(id, formData, voucherChannelsChoices) + }); + }; +} diff --git a/src/discounts/views/VoucherDetails/index.ts b/src/discounts/views/VoucherDetails/index.ts new file mode 100644 index 000000000..d161158b6 --- /dev/null +++ b/src/discounts/views/VoucherDetails/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherDetails"; +export * from "./VoucherDetails"; diff --git a/src/discounts/views/VoucherList/VoucherList.tsx b/src/discounts/views/VoucherList/VoucherList.tsx index 2eb624f65..db5cd505f 100644 --- a/src/discounts/views/VoucherList/VoucherList.tsx +++ b/src/discounts/views/VoucherList/VoucherList.tsx @@ -1,6 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog"; import ActionDialog from "@saleor/components/ActionDialog"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { @@ -8,13 +9,13 @@ import SaveFilterTabDialog, { } from "@saleor/components/SaveFilterTabDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannelsSettings from "@saleor/hooks/useChannelsSettings"; import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages, sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; @@ -56,7 +57,6 @@ export const VoucherList: React.FC = ({ params }) => { const navigate = useNavigator(); const notify = useNotifier(); const paginate = usePaginator(); - const shop = useShop(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); @@ -65,6 +65,17 @@ export const VoucherList: React.FC = ({ params }) => { ); const intl = useIntl(); + const [openModal, closeModal] = createDialogActionHandlers< + VoucherListUrlDialog, + VoucherListUrlQueryParams + >(navigate, voucherListUrl, params); + + const { + channelChoices, + handleChannelSelectConfirm, + selectedChannel + } = useChannelsSettings("vouchersListChannel", { closeModal, openModal }); + const paginationState = createPaginationState(settings.rowNumber, params); const queryVariables = React.useMemo( () => ({ @@ -100,11 +111,6 @@ export const VoucherList: React.FC = ({ params }) => { params }); - const [openModal, closeModal] = createDialogActionHandlers< - VoucherListUrlDialog, - VoucherListUrlQueryParams - >(navigate, voucherListUrl, params); - const handleTabChange = (tab: number) => { reset(); navigate( @@ -129,7 +135,7 @@ export const VoucherList: React.FC = ({ params }) => { const canOpenBulkActionDialog = maybe(() => params.ids.length > 0); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( - maybe(() => data.vouchers.pageInfo), + data?.vouchers?.pageInfo, paginationState, params ); @@ -147,7 +153,6 @@ export const VoucherList: React.FC = ({ params }) => { }; const handleSort = createSortHandler(navigate, voucherListUrl, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); return ( @@ -162,8 +167,17 @@ export const VoucherList: React.FC = ({ params }) => { return ( <> + {!!channelChoices?.length && ( + + )} = ({ params }) => { onTabDelete={() => openModal("delete-search")} onTabSave={() => openModal("save-search")} tabs={tabs.map(tab => tab.name)} - defaultCurrency={maybe(() => shop.defaultCurrency)} settings={settings} vouchers={maybe(() => data.vouchers.edges.map(edge => edge.node))} disabled={loading} @@ -202,6 +215,12 @@ export const VoucherList: React.FC = ({ params }) => { } + selectedChannel={selectedChannel} + onSettingsOpen={ + !!channelChoices?.length + ? () => openModal("settings") + : undefined + } /> = { ...searchPageProps, ...tabPageProps, - currencySymbol: "USD", filterOpts: {}, onFilterChange: () => undefined }; @@ -346,7 +345,7 @@ export const permissions: ShopInfo_shop_permissions[] = [ name: "Manage products." }, { - code: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, + code: PermissionEnum.MANAGE_PRODUCTS, name: "Manage product types and attributes." }, { @@ -445,7 +444,7 @@ export const adminUserPermissions: User_userPermissions[] = [ }, { __typename: "UserPermission", - code: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, + code: PermissionEnum.MANAGE_PRODUCTS, name: "Manage product types and attributes." }, { diff --git a/src/fragments/channels.ts b/src/fragments/channels.ts new file mode 100644 index 000000000..1aebf0016 --- /dev/null +++ b/src/fragments/channels.ts @@ -0,0 +1,19 @@ +import gql from "graphql-tag"; + +export const channelErrorFragment = gql` + fragment ChannelErrorFragment on ChannelError { + code + field + message + } +`; + +export const channelDetailsFragment = gql` + fragment ChannelDetailsFragment on Channel { + id + isActive + name + slug + currencyCode + } +`; diff --git a/src/fragments/collections.ts b/src/fragments/collections.ts index 984db729b..cbc553ef7 100644 --- a/src/fragments/collections.ts +++ b/src/fragments/collections.ts @@ -1,3 +1,4 @@ +import { channelListingProductFragment } from "@saleor/fragments/products"; import gql from "graphql-tag"; import { metadataFragment } from "./metadata"; @@ -5,8 +6,15 @@ import { metadataFragment } from "./metadata"; export const collectionFragment = gql` fragment CollectionFragment on Collection { id - isPublished name + channelListings { + isPublished + publicationDate + channel { + id + name + } + } } `; @@ -22,10 +30,8 @@ export const collectionDetailsFragment = gql` } slug descriptionJson - publicationDate seoDescription seoTitle - isPublished } `; @@ -34,9 +40,9 @@ export const collectionDetailsFragment = gql` // https://github.com/apollographql/apollo-client/issues/2496 // https://github.com/apollographql/apollo-client/issues/3468 export const collectionProductFragment = gql` + ${channelListingProductFragment} fragment CollectionProductFragment on Product { id - isPublished name productType { id @@ -45,5 +51,8 @@ export const collectionProductFragment = gql` thumbnail { url } + channelListings { + ...ChannelListingProductFragment + } } `; diff --git a/src/fragments/discounts.ts b/src/fragments/discounts.ts index 2e99da371..579bc93be 100644 --- a/src/fragments/discounts.ts +++ b/src/fragments/discounts.ts @@ -1,3 +1,4 @@ +import { channelListingProductFragment } from "@saleor/fragments/products"; import gql from "graphql-tag"; import { pageInfoFragment } from "./pageInfo"; @@ -9,11 +10,21 @@ export const saleFragment = gql` type startDate endDate - value + channelListings { + id + channel { + id + name + currencyCode + } + discountValue + currency + } } `; export const saleDetailsFragment = gql` + ${channelListingProductFragment} ${pageInfoFragment} ${saleFragment} fragment SaleDetailsFragment on Sale { @@ -23,7 +34,6 @@ export const saleDetailsFragment = gql` node { id name - isPublished productType { id name @@ -31,6 +41,9 @@ export const saleDetailsFragment = gql` thumbnail { url } + channelListings { + ...ChannelListingProductFragment + } } } pageInfo { @@ -79,22 +92,32 @@ export const voucherFragment = gql` endDate usageLimit discountValueType - discountValue countries { code country } - minSpent { - currency - amount - } minCheckoutItemsQuantity + channelListings { + id + channel { + id + name + currencyCode + } + discountValue + currency + minSpent { + amount + currency + } + } } `; export const voucherDetailsFragment = gql` ${pageInfoFragment} ${voucherFragment} + ${channelListingProductFragment} fragment VoucherDetailsFragment on Voucher { ...VoucherFragment type @@ -112,10 +135,12 @@ export const voucherDetailsFragment = gql` id name } - isPublished thumbnail { url } + channelListings { + ...ChannelListingProductFragment + } } } totalCount diff --git a/src/fragments/errors.ts b/src/fragments/errors.ts index 2670f2d6f..26d79d844 100644 --- a/src/fragments/errors.ts +++ b/src/fragments/errors.ts @@ -15,6 +15,24 @@ export const productErrorWithAttributesFragment = gql` } `; +export const productChannelListingErrorFragment = gql` + fragment ProductChannelListingErrorFragment on ProductChannelListingError { + code + field + message + channels + } +`; + +export const collectionChannelListingErrorFragment = gql` + fragment CollectionChannelListingErrorFragment on CollectionChannelListingError { + code + field + message + channels + } +`; + export const accountErrorFragment = gql` fragment AccountErrorFragment on AccountError { code @@ -26,6 +44,7 @@ export const discountErrorFragment = gql` fragment DiscountErrorFragment on DiscountError { code field + channels } `; @@ -62,6 +81,7 @@ export const bulkProductErrorFragment = gql` field code index + channels } `; export const bulkStockErrorFragment = gql` @@ -78,6 +98,14 @@ export const stockErrorFragment = gql` } `; +export const shippingChannelsErrorFragment = gql` + fragment ShippingChannelsErrorFragment on ShippingError { + code + field + channels + } +`; + export const shippingErrorFragment = gql` fragment ShippingErrorFragment on ShippingError { code @@ -149,3 +177,10 @@ export const metadataErrorFragment = gql` field } `; + +export const collectionsErrorFragment = gql` + fragment CollectionErrorFragment on CollectionError { + code + field + } +`; diff --git a/src/fragments/orders.ts b/src/fragments/orders.ts index 1068cca35..3fbe90c7c 100644 --- a/src/fragments/orders.ts +++ b/src/fragments/orders.ts @@ -27,11 +27,6 @@ export const fragmentOrderLine = gql` isShippingRequired variant { id - product { - id - isAvailableForPurchase - isPublished - } quantityAvailable } productName @@ -170,5 +165,11 @@ export const fragmentOrderDetails = gql` invoices { ...InvoiceFragment } + channel { + isActive + id + name + currencyCode + } } `; diff --git a/src/fragments/products.ts b/src/fragments/products.ts index 33e0fbc33..1d20a7dcd 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -32,20 +32,58 @@ export const fragmentProductImage = gql` } `; +export const channelListingProductFragment = gql` + ${fragmentMoney} + fragment ChannelListingProductFragment on ProductChannelListing { + isPublished + publicationDate + discountedPrice { + ...Money + } + isAvailableForPurchase + availableForPurchase + visibleInListings + channel { + id + name + currencyCode + } + } +`; + +export const channelListingProductVariantFragment = gql` + ${fragmentMoney} + fragment ChannelListingProductVariantFragment on ProductVariantChannelListing { + channel { + id + name + currencyCode + } + price { + ...Money + } + costPrice { + ...Money + } + } +`; + export const productFragment = gql` + ${channelListingProductFragment} fragment ProductFragment on Product { id name thumbnail { url } - isAvailable - isPublished productType { id name hasVariants } + channelListings { + ...ChannelListingProductFragment + } } `; @@ -84,18 +122,14 @@ export const productVariantAttributesFragment = gql` } } } - pricing { - priceRangeUndiscounted { - start { - gross { - ...Money - } - } - stop { - gross { - ...Money - } - } + channelListings { + channel { + id + name + currencyCode + } + discountedPrice { + ...Money } } } @@ -103,12 +137,13 @@ export const productVariantAttributesFragment = gql` export const productFragmentDetails = gql` ${fragmentProductImage} - ${fragmentMoney} ${productVariantAttributesFragment} ${stockFragment} ${weightFragment} ${metadataFragment} ${taxTypeFragment} + ${channelListingProductFragment} + ${channelListingProductVariantFragment} fragment Product on Product { ...ProductVariantAttributesFragment ...MetadataFragment @@ -128,52 +163,26 @@ export const productFragmentDetails = gql` id name } - margin { - start - stop - } - purchaseCost { - start { - ...Money - } - stop { - ...Money - } - } - isAvailableForPurchase - isAvailable - isPublished chargeTaxes - publicationDate - pricing { - priceRangeUndiscounted { - start { - gross { - ...Money - } - } - stop { - gross { - ...Money - } - } - } + channelListings { + ...ChannelListingProductFragment } images { ...ProductImageFragment } + isAvailable variants { id sku name - price { - ...Money - } margin stocks { ...StockFragment } trackInventory + channelListings { + ...ChannelListingProductVariantFragment + } } productType { id @@ -189,8 +198,6 @@ export const productFragmentDetails = gql` taxType { ...TaxTypeFragment } - availableForPurchase - visibleInListings } `; @@ -200,6 +207,7 @@ export const fragmentVariant = gql` ${stockFragment} ${weightFragment} ${metadataFragment} + ${channelListingProductVariantFragment} fragment ProductVariant on ProductVariant { id ...MetadataFragment @@ -221,17 +229,11 @@ export const fragmentVariant = gql` slug } } - costPrice { - ...Money - } images { id url } name - price { - ...Money - } product { id defaultVariant { @@ -244,6 +246,16 @@ export const fragmentVariant = gql` thumbnail { url } + channelListings { + channel { + id + name + currencyCode + } + discountedPrice { + ...Money + } + } variants { id name @@ -257,6 +269,9 @@ export const fragmentVariant = gql` id } } + channelListings { + ...ChannelListingProductVariantFragment + } sku stocks { ...StockFragment diff --git a/src/fragments/shipping.ts b/src/fragments/shipping.ts index b2845edc7..33d6bab2b 100644 --- a/src/fragments/shipping.ts +++ b/src/fragments/shipping.ts @@ -1,3 +1,4 @@ +import { fragmentMoney } from "@saleor/fragments/products"; import gql from "graphql-tag"; export const shippingZoneFragment = gql` @@ -11,30 +12,36 @@ export const shippingZoneFragment = gql` } `; export const shippingMethodFragment = gql` + ${fragmentMoney} fragment ShippingMethodFragment on ShippingMethod { id - minimumOrderPrice { - amount - currency - } minimumOrderWeight { unit value } - maximumOrderPrice { - amount - currency - } maximumOrderWeight { unit value } name - price { - amount - currency - } type + channelListings { + id + channel { + id + name + currencyCode + } + price { + ...Money + } + minimumOrderPrice { + ...Money + } + maximumOrderPrice { + ...Money + } + } } `; export const shippingZoneDetailsFragment = gql` diff --git a/src/fragments/translations.ts b/src/fragments/translations.ts index 2a05816f4..49ac5dcd5 100644 --- a/src/fragments/translations.ts +++ b/src/fragments/translations.ts @@ -1,12 +1,7 @@ import gql from "graphql-tag"; export const categoryTranslationFragment = gql` - fragment CategoryTranslationFragment on Category { - id - name - descriptionJson - seoDescription - seoTitle + fragment CategoryTranslationFragment on CategoryTranslatableContent { translation(languageCode: $language) { id descriptionJson @@ -17,15 +12,24 @@ export const categoryTranslationFragment = gql` seoDescription seoTitle } + category { + id + name + descriptionJson + seoDescription + seoTitle + } } `; export const collectionTranslationFragment = gql` - fragment CollectionTranslationFragment on Collection { - id - name - descriptionJson - seoDescription - seoTitle + fragment CollectionTranslationFragment on CollectionTranslatableContent { + collection { + id + name + descriptionJson + seoDescription + seoTitle + } translation(languageCode: $language) { id descriptionJson @@ -39,12 +43,14 @@ export const collectionTranslationFragment = gql` } `; export const productTranslationFragment = gql` - fragment ProductTranslationFragment on Product { - id - name - descriptionJson - seoDescription - seoTitle + fragment ProductTranslationFragment on ProductTranslatableContent { + product { + id + name + descriptionJson + seoDescription + seoTitle + } translation(languageCode: $language) { id descriptionJson @@ -59,9 +65,11 @@ export const productTranslationFragment = gql` } `; export const saleTranslationFragment = gql` - fragment SaleTranslationFragment on Sale { - id - name + fragment SaleTranslationFragment on SaleTranslatableContent { + sale { + id + name + } translation(languageCode: $language) { id language { @@ -73,9 +81,11 @@ export const saleTranslationFragment = gql` } `; export const voucherTranslationFragment = gql` - fragment VoucherTranslationFragment on Voucher { - id - name + fragment VoucherTranslationFragment on VoucherTranslatableContent { + voucher { + id + name + } translation(languageCode: $language) { id language { @@ -87,7 +97,7 @@ export const voucherTranslationFragment = gql` } `; export const shippingMethodTranslationFragment = gql` - fragment ShippingMethodTranslationFragment on ShippingMethod { + fragment ShippingMethodTranslationFragment on ShippingMethodTranslatableContent { id name translation(languageCode: $language) { @@ -100,14 +110,16 @@ export const shippingMethodTranslationFragment = gql` } } `; -export const pageTranslationFragment = gql` - fragment PageTranslationFragment on Page { - id - contentJson - seoDescription - seoTitle - title +export const pageTranslationFragment = gql` + fragment PageTranslationFragment on PageTranslatableContent { + page { + id + contentJson + seoDescription + seoTitle + title + } translation(languageCode: $language) { id contentJson @@ -142,31 +154,24 @@ export const pageTranslatableFragment = gql` } } `; -export const productTypeTranslationFragment = gql` - fragment AttributeTranslationFragment on Attribute { - id - name + +export const attributeTranslationFragment = gql` + fragment AttributeTranslationFragment on AttributeTranslatableContent { translation(languageCode: $language) { id name } - values { + attribute { id name - translation(languageCode: $language) { + values { id name + translation(languageCode: $language) { + id + name + } } } } - fragment ProductTypeTranslationFragment on ProductType { - id - name - productAttributes { - ...AttributeTranslationFragment - } - variantAttributes { - ...AttributeTranslationFragment - } - } `; diff --git a/src/fragments/types/AttributeTranslationFragment.ts b/src/fragments/types/AttributeTranslationFragment.ts index 59d52d3a4..c3e891cd1 100644 --- a/src/fragments/types/AttributeTranslationFragment.ts +++ b/src/fragments/types/AttributeTranslationFragment.ts @@ -12,23 +12,28 @@ export interface AttributeTranslationFragment_translation { name: string; } -export interface AttributeTranslationFragment_values_translation { +export interface AttributeTranslationFragment_attribute_values_translation { __typename: "AttributeValueTranslation"; id: string; name: string; } -export interface AttributeTranslationFragment_values { +export interface AttributeTranslationFragment_attribute_values { __typename: "AttributeValue"; id: string; name: string | null; - translation: AttributeTranslationFragment_values_translation | null; + translation: AttributeTranslationFragment_attribute_values_translation | null; } -export interface AttributeTranslationFragment { +export interface AttributeTranslationFragment_attribute { __typename: "Attribute"; id: string; name: string | null; - translation: AttributeTranslationFragment_translation | null; - values: (AttributeTranslationFragment_values | null)[] | null; + values: (AttributeTranslationFragment_attribute_values | null)[] | null; +} + +export interface AttributeTranslationFragment { + __typename: "AttributeTranslatableContent"; + translation: AttributeTranslationFragment_translation | null; + attribute: AttributeTranslationFragment_attribute | null; } diff --git a/src/fragments/types/BulkProductErrorFragment.ts b/src/fragments/types/BulkProductErrorFragment.ts index 940f13b10..36c542fda 100644 --- a/src/fragments/types/BulkProductErrorFragment.ts +++ b/src/fragments/types/BulkProductErrorFragment.ts @@ -13,4 +13,5 @@ export interface BulkProductErrorFragment { field: string | null; code: ProductErrorCode; index: number | null; + channels: string[] | null; } diff --git a/src/fragments/types/CategoryTranslationFragment.ts b/src/fragments/types/CategoryTranslationFragment.ts index 669ed30cf..8729af0f2 100644 --- a/src/fragments/types/CategoryTranslationFragment.ts +++ b/src/fragments/types/CategoryTranslationFragment.ts @@ -21,12 +21,17 @@ export interface CategoryTranslationFragment_translation { seoTitle: string | null; } -export interface CategoryTranslationFragment { +export interface CategoryTranslationFragment_category { __typename: "Category"; id: string; name: string; descriptionJson: any; seoDescription: string | null; seoTitle: string | null; - translation: CategoryTranslationFragment_translation | null; +} + +export interface CategoryTranslationFragment { + __typename: "CategoryTranslatableContent"; + translation: CategoryTranslationFragment_translation | null; + category: CategoryTranslationFragment_category | null; } diff --git a/src/fragments/types/CategoryTranslationUpdateFragment.ts b/src/fragments/types/CategoryTranslationUpdateFragment.ts new file mode 100644 index 000000000..f065f15c6 --- /dev/null +++ b/src/fragments/types/CategoryTranslationUpdateFragment.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: CategoryTranslationUpdateFragment +// ==================================================== + +export interface CategoryTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + language: string; +} + +export interface CategoryTranslationUpdateFragment_translation { + __typename: "CategoryTranslation"; + id: string; + descriptionJson: any; + language: CategoryTranslationUpdateFragment_translation_language; + name: string; + seoDescription: string | null; + seoTitle: string | null; +} + +export interface CategoryTranslationUpdateFragment { + __typename: "Category"; + id: string; + name: string; + descriptionJson: any; + seoDescription: string | null; + seoTitle: string | null; + translation: CategoryTranslationUpdateFragment_translation | null; +} diff --git a/src/fragments/types/ChannelDetailsFragment.ts b/src/fragments/types/ChannelDetailsFragment.ts new file mode 100644 index 000000000..01999b257 --- /dev/null +++ b/src/fragments/types/ChannelDetailsFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelDetailsFragment +// ==================================================== + +export interface ChannelDetailsFragment { + __typename: "Channel"; + id: string; + isActive: boolean; + name: string; + slug: string; + currencyCode: string; +} diff --git a/src/fragments/types/ChannelErrorFragment.ts b/src/fragments/types/ChannelErrorFragment.ts new file mode 100644 index 000000000..5e647c72c --- /dev/null +++ b/src/fragments/types/ChannelErrorFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ChannelErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ChannelErrorFragment +// ==================================================== + +export interface ChannelErrorFragment { + __typename: "ChannelError"; + code: ChannelErrorCode; + field: string | null; + message: string | null; +} diff --git a/src/fragments/types/ChannelListingAvailabilityFragment.ts b/src/fragments/types/ChannelListingAvailabilityFragment.ts new file mode 100644 index 000000000..1e2e43cec --- /dev/null +++ b/src/fragments/types/ChannelListingAvailabilityFragment.ts @@ -0,0 +1,20 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelListingAvailabilityFragment +// ==================================================== + +export interface ChannelListingAvailabilityFragment_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface ChannelListingAvailabilityFragment { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: ChannelListingAvailabilityFragment_channel; +} diff --git a/src/fragments/types/ChannelListingProductFragment.ts b/src/fragments/types/ChannelListingProductFragment.ts new file mode 100644 index 000000000..584e6ed7c --- /dev/null +++ b/src/fragments/types/ChannelListingProductFragment.ts @@ -0,0 +1,31 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelListingProductFragment +// ==================================================== + +export interface ChannelListingProductFragment_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ChannelListingProductFragment_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ChannelListingProductFragment { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: ChannelListingProductFragment_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: ChannelListingProductFragment_channel; +} diff --git a/src/fragments/types/ChannelListingProductVariantFragment.ts b/src/fragments/types/ChannelListingProductVariantFragment.ts new file mode 100644 index 000000000..07962c57f --- /dev/null +++ b/src/fragments/types/ChannelListingProductVariantFragment.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: ChannelListingProductVariantFragment +// ==================================================== + +export interface ChannelListingProductVariantFragment_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ChannelListingProductVariantFragment_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ChannelListingProductVariantFragment_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ChannelListingProductVariantFragment { + __typename: "ProductVariantChannelListing"; + channel: ChannelListingProductVariantFragment_channel; + price: ChannelListingProductVariantFragment_price | null; + costPrice: ChannelListingProductVariantFragment_costPrice | null; +} diff --git a/src/fragments/types/CollectionChannelListingErrorFragment.ts b/src/fragments/types/CollectionChannelListingErrorFragment.ts new file mode 100644 index 000000000..9a3275f00 --- /dev/null +++ b/src/fragments/types/CollectionChannelListingErrorFragment.ts @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: CollectionChannelListingErrorFragment +// ==================================================== + +export interface CollectionChannelListingErrorFragment { + __typename: "CollectionChannelListingError"; + code: ProductErrorCode; + field: string | null; + message: string | null; + channels: string[] | null; +} diff --git a/src/fragments/types/CollectionDetailsFragment.ts b/src/fragments/types/CollectionDetailsFragment.ts index 0464eb6e7..07db07c20 100644 --- a/src/fragments/types/CollectionDetailsFragment.ts +++ b/src/fragments/types/CollectionDetailsFragment.ts @@ -6,6 +6,19 @@ // GraphQL fragment: CollectionDetailsFragment // ==================================================== +export interface CollectionDetailsFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CollectionDetailsFragment_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionDetailsFragment_channelListings_channel; +} + export interface CollectionDetailsFragment_metadata { __typename: "MetadataItem"; key: string; @@ -27,14 +40,13 @@ export interface CollectionDetailsFragment_backgroundImage { export interface CollectionDetailsFragment { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionDetailsFragment_channelListings[] | null; metadata: (CollectionDetailsFragment_metadata | null)[]; privateMetadata: (CollectionDetailsFragment_privateMetadata | null)[]; backgroundImage: CollectionDetailsFragment_backgroundImage | null; slug: string; descriptionJson: any; - publicationDate: any | null; seoDescription: string | null; seoTitle: string | null; } diff --git a/src/fragments/types/CollectionErrorFragment.ts b/src/fragments/types/CollectionErrorFragment.ts new file mode 100644 index 000000000..6a17c6508 --- /dev/null +++ b/src/fragments/types/CollectionErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { CollectionErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: CollectionErrorFragment +// ==================================================== + +export interface CollectionErrorFragment { + __typename: "CollectionError"; + code: CollectionErrorCode; + field: string | null; +} diff --git a/src/fragments/types/CollectionFragment.ts b/src/fragments/types/CollectionFragment.ts index 0dfb0475a..c75972e68 100644 --- a/src/fragments/types/CollectionFragment.ts +++ b/src/fragments/types/CollectionFragment.ts @@ -6,9 +6,22 @@ // GraphQL fragment: CollectionFragment // ==================================================== +export interface CollectionFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; +} + +export interface CollectionFragment_channelListings { + __typename: "CollectionChannelListing"; + isPublished: boolean; + publicationDate: any | null; + channel: CollectionFragment_channelListings_channel; +} + export interface CollectionFragment { __typename: "Collection"; id: string; - isPublished: boolean; name: string; + channelListings: CollectionFragment_channelListings[] | null; } diff --git a/src/fragments/types/CollectionProductFragment.ts b/src/fragments/types/CollectionProductFragment.ts index 987f310e0..98e85959f 100644 --- a/src/fragments/types/CollectionProductFragment.ts +++ b/src/fragments/types/CollectionProductFragment.ts @@ -17,11 +17,35 @@ export interface CollectionProductFragment_thumbnail { url: string; } +export interface CollectionProductFragment_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CollectionProductFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CollectionProductFragment_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: CollectionProductFragment_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: CollectionProductFragment_channelListings_channel; +} + export interface CollectionProductFragment { __typename: "Product"; id: string; - isPublished: boolean; name: string; productType: CollectionProductFragment_productType; thumbnail: CollectionProductFragment_thumbnail | null; + channelListings: CollectionProductFragment_channelListings[] | null; } diff --git a/src/fragments/types/CollectionTranslationFragment.ts b/src/fragments/types/CollectionTranslationFragment.ts index b217a814a..3488c0ce8 100644 --- a/src/fragments/types/CollectionTranslationFragment.ts +++ b/src/fragments/types/CollectionTranslationFragment.ts @@ -6,6 +6,15 @@ // GraphQL fragment: CollectionTranslationFragment // ==================================================== +export interface CollectionTranslationFragment_collection { + __typename: "Collection"; + id: string; + name: string; + descriptionJson: any; + seoDescription: string | null; + seoTitle: string | null; +} + export interface CollectionTranslationFragment_translation_language { __typename: "LanguageDisplay"; language: string; @@ -22,11 +31,7 @@ export interface CollectionTranslationFragment_translation { } export interface CollectionTranslationFragment { - __typename: "Collection"; - id: string; - name: string; - descriptionJson: any; - seoDescription: string | null; - seoTitle: string | null; + __typename: "CollectionTranslatableContent"; + collection: CollectionTranslationFragment_collection | null; translation: CollectionTranslationFragment_translation | null; } diff --git a/src/fragments/types/CollectionTranslationUpdateFragment.ts b/src/fragments/types/CollectionTranslationUpdateFragment.ts new file mode 100644 index 000000000..7b5ab4f2d --- /dev/null +++ b/src/fragments/types/CollectionTranslationUpdateFragment.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: CollectionTranslationUpdateFragment +// ==================================================== + +export interface CollectionTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + language: string; +} + +export interface CollectionTranslationUpdateFragment_translation { + __typename: "CollectionTranslation"; + id: string; + descriptionJson: any; + language: CollectionTranslationUpdateFragment_translation_language; + name: string; + seoDescription: string | null; + seoTitle: string | null; +} + +export interface CollectionTranslationUpdateFragment { + __typename: "Collection"; + id: string; + name: string; + descriptionJson: any; + seoDescription: string | null; + seoTitle: string | null; + translation: CollectionTranslationUpdateFragment_translation | null; +} diff --git a/src/fragments/types/DiscountErrorFragment.ts b/src/fragments/types/DiscountErrorFragment.ts index 966c8c2ea..6026be228 100644 --- a/src/fragments/types/DiscountErrorFragment.ts +++ b/src/fragments/types/DiscountErrorFragment.ts @@ -12,4 +12,5 @@ export interface DiscountErrorFragment { __typename: "DiscountError"; code: DiscountErrorCode; field: string | null; + channels: string[] | null; } diff --git a/src/fragments/types/FulfillmentFragment.ts b/src/fragments/types/FulfillmentFragment.ts index 2c0fd2839..9299ac8f2 100644 --- a/src/fragments/types/FulfillmentFragment.ts +++ b/src/fragments/types/FulfillmentFragment.ts @@ -8,17 +8,9 @@ import { FulfillmentStatus } from "./../../types/globalTypes"; // GraphQL fragment: FulfillmentFragment // ==================================================== -export interface FulfillmentFragment_lines_orderLine_variant_product { - __typename: "Product"; - id: string; - isAvailableForPurchase: boolean | null; - isPublished: boolean; -} - export interface FulfillmentFragment_lines_orderLine_variant { __typename: "ProductVariant"; id: string; - product: FulfillmentFragment_lines_orderLine_variant_product; quantityAvailable: number; } diff --git a/src/fragments/types/OrderDetailsFragment.ts b/src/fragments/types/OrderDetailsFragment.ts index 73d5a3206..8355e63e4 100644 --- a/src/fragments/types/OrderDetailsFragment.ts +++ b/src/fragments/types/OrderDetailsFragment.ts @@ -62,17 +62,9 @@ export interface OrderDetailsFragment_events { user: OrderDetailsFragment_events_user | null; } -export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant_product { - __typename: "Product"; - id: string; - isAvailableForPurchase: boolean | null; - isPublished: boolean; -} - export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant { __typename: "ProductVariant"; id: string; - product: OrderDetailsFragment_fulfillments_lines_orderLine_variant_product; quantityAvailable: number; } @@ -135,17 +127,9 @@ export interface OrderDetailsFragment_fulfillments { warehouse: OrderDetailsFragment_fulfillments_warehouse | null; } -export interface OrderDetailsFragment_lines_variant_product { - __typename: "Product"; - id: string; - isAvailableForPurchase: boolean | null; - isPublished: boolean; -} - export interface OrderDetailsFragment_lines_variant { __typename: "ProductVariant"; id: string; - product: OrderDetailsFragment_lines_variant_product; quantityAvailable: number; } @@ -298,6 +282,14 @@ export interface OrderDetailsFragment_invoices { status: JobStatusEnum; } +export interface OrderDetailsFragment_channel { + __typename: "Channel"; + isActive: boolean; + id: string; + name: string; + currencyCode: string; +} + export interface OrderDetailsFragment { __typename: "Order"; id: string; @@ -327,4 +319,5 @@ export interface OrderDetailsFragment { availableShippingMethods: (OrderDetailsFragment_availableShippingMethods | null)[] | null; discount: OrderDetailsFragment_discount | null; invoices: (OrderDetailsFragment_invoices | null)[] | null; + channel: OrderDetailsFragment_channel; } diff --git a/src/fragments/types/OrderLineFragment.ts b/src/fragments/types/OrderLineFragment.ts index 06fbdb57a..e61e981a1 100644 --- a/src/fragments/types/OrderLineFragment.ts +++ b/src/fragments/types/OrderLineFragment.ts @@ -6,17 +6,9 @@ // GraphQL fragment: OrderLineFragment // ==================================================== -export interface OrderLineFragment_variant_product { - __typename: "Product"; - id: string; - isAvailableForPurchase: boolean | null; - isPublished: boolean; -} - export interface OrderLineFragment_variant { __typename: "ProductVariant"; id: string; - product: OrderLineFragment_variant_product; quantityAvailable: number; } diff --git a/src/fragments/types/PageTranslationFragment.ts b/src/fragments/types/PageTranslationFragment.ts index cbbd01c62..24ba7cc69 100644 --- a/src/fragments/types/PageTranslationFragment.ts +++ b/src/fragments/types/PageTranslationFragment.ts @@ -8,6 +8,15 @@ import { LanguageCodeEnum } from "./../../types/globalTypes"; // GraphQL fragment: PageTranslationFragment // ==================================================== +export interface PageTranslationFragment_page { + __typename: "Page"; + id: string; + contentJson: any; + seoDescription: string | null; + seoTitle: string | null; + title: string; +} + export interface PageTranslationFragment_translation_language { __typename: "LanguageDisplay"; code: LanguageCodeEnum; @@ -25,11 +34,7 @@ export interface PageTranslationFragment_translation { } export interface PageTranslationFragment { - __typename: "Page"; - id: string; - contentJson: any; - seoDescription: string | null; - seoTitle: string | null; - title: string; + __typename: "PageTranslatableContent"; + page: PageTranslationFragment_page | null; translation: PageTranslationFragment_translation | null; } diff --git a/src/fragments/types/PageTranslationUpdateFragment.ts b/src/fragments/types/PageTranslationUpdateFragment.ts new file mode 100644 index 000000000..e801e2430 --- /dev/null +++ b/src/fragments/types/PageTranslationUpdateFragment.ts @@ -0,0 +1,35 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { LanguageCodeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: PageTranslationUpdateFragment +// ==================================================== + +export interface PageTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + code: LanguageCodeEnum; + language: string; +} + +export interface PageTranslationUpdateFragment_translation { + __typename: "PageTranslation"; + id: string; + contentJson: any; + seoDescription: string | null; + seoTitle: string | null; + title: string; + language: PageTranslationUpdateFragment_translation_language; +} + +export interface PageTranslationUpdateFragment { + __typename: "Page"; + id: string; + contentJson: any; + seoDescription: string | null; + seoTitle: string | null; + title: string; + translation: PageTranslationUpdateFragment_translation | null; +} diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index 203e734a9..ec1c76096 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -67,37 +67,28 @@ export interface Product_productType { taxType: Product_productType_taxType | null; } -export interface Product_pricing_priceRangeUndiscounted_start_gross { +export interface Product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface Product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface Product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: Product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface Product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface Product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: Product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface Product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: Product_pricing_priceRangeUndiscounted_start | null; - stop: Product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface Product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: Product_pricing_priceRangeUndiscounted | null; +export interface Product_channelListings { + __typename: "ProductChannelListing"; + channel: Product_channelListings_channel; + discountedPrice: Product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface Product_metadata { @@ -129,30 +120,6 @@ export interface Product_collections { name: string; } -export interface Product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface Product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface Product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface Product_purchaseCost { - __typename: "MoneyRange"; - start: Product_purchaseCost_start | null; - stop: Product_purchaseCost_stop | null; -} - export interface Product_images { __typename: "ProductImage"; id: string; @@ -161,12 +128,6 @@ export interface Product_images { url: string; } -export interface Product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface Product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -181,15 +142,41 @@ export interface Product_variants_stocks { warehouse: Product_variants_stocks_warehouse; } +export interface Product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface Product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface Product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface Product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: Product_variants_channelListings_channel; + price: Product_variants_channelListings_price | null; + costPrice: Product_variants_channelListings_costPrice | null; +} + export interface Product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: Product_variants_price | null; margin: number | null; stocks: (Product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: Product_variants_channelListings[] | null; } export interface Product_weight { @@ -209,7 +196,7 @@ export interface Product { id: string; attributes: Product_attributes[]; productType: Product_productType; - pricing: Product_pricing | null; + channelListings: Product_channelListings[] | null; metadata: (Product_metadata | null)[]; privateMetadata: (Product_privateMetadata | null)[]; name: string; @@ -220,17 +207,10 @@ export interface Product { defaultVariant: Product_defaultVariant | null; category: Product_category | null; collections: (Product_collections | null)[] | null; - margin: Product_margin | null; - purchaseCost: Product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (Product_images | null)[] | null; + isAvailable: boolean | null; variants: (Product_variants | null)[] | null; weight: Product_weight | null; taxType: Product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } diff --git a/src/fragments/types/ProductChannelListingErrorFragment.ts b/src/fragments/types/ProductChannelListingErrorFragment.ts new file mode 100644 index 000000000..6a4348dcc --- /dev/null +++ b/src/fragments/types/ProductChannelListingErrorFragment.ts @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ProductChannelListingErrorFragment +// ==================================================== + +export interface ProductChannelListingErrorFragment { + __typename: "ProductChannelListingError"; + code: ProductErrorCode; + field: string | null; + message: string | null; + channels: string[] | null; +} diff --git a/src/fragments/types/ProductFragment.ts b/src/fragments/types/ProductFragment.ts index c7ab4b127..e3a996d00 100644 --- a/src/fragments/types/ProductFragment.ts +++ b/src/fragments/types/ProductFragment.ts @@ -18,12 +18,35 @@ export interface ProductFragment_productType { hasVariants: boolean; } +export interface ProductFragment_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductFragment_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: ProductFragment_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: ProductFragment_channelListings_channel; +} + export interface ProductFragment { __typename: "Product"; id: string; name: string; thumbnail: ProductFragment_thumbnail | null; - isAvailable: boolean | null; - isPublished: boolean; productType: ProductFragment_productType; + channelListings: ProductFragment_channelListings[] | null; } diff --git a/src/fragments/types/ProductTranslationFragment.ts b/src/fragments/types/ProductTranslationFragment.ts index e540e23c9..91a3d8d8c 100644 --- a/src/fragments/types/ProductTranslationFragment.ts +++ b/src/fragments/types/ProductTranslationFragment.ts @@ -8,6 +8,15 @@ import { LanguageCodeEnum } from "./../../types/globalTypes"; // GraphQL fragment: ProductTranslationFragment // ==================================================== +export interface ProductTranslationFragment_product { + __typename: "Product"; + id: string; + name: string; + descriptionJson: any; + seoDescription: string | null; + seoTitle: string | null; +} + export interface ProductTranslationFragment_translation_language { __typename: "LanguageDisplay"; code: LanguageCodeEnum; @@ -25,11 +34,7 @@ export interface ProductTranslationFragment_translation { } export interface ProductTranslationFragment { - __typename: "Product"; - id: string; - name: string; - descriptionJson: any; - seoDescription: string | null; - seoTitle: string | null; + __typename: "ProductTranslatableContent"; + product: ProductTranslationFragment_product | null; translation: ProductTranslationFragment_translation | null; } diff --git a/src/fragments/types/ProductTranslationUpdateFragment.ts b/src/fragments/types/ProductTranslationUpdateFragment.ts new file mode 100644 index 000000000..96abef4b2 --- /dev/null +++ b/src/fragments/types/ProductTranslationUpdateFragment.ts @@ -0,0 +1,35 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { LanguageCodeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ProductTranslationUpdateFragment +// ==================================================== + +export interface ProductTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + code: LanguageCodeEnum; + language: string; +} + +export interface ProductTranslationUpdateFragment_translation { + __typename: "ProductTranslation"; + id: string; + descriptionJson: any; + language: ProductTranslationUpdateFragment_translation_language; + name: string; + seoDescription: string | null; + seoTitle: string | null; +} + +export interface ProductTranslationUpdateFragment { + __typename: "Product"; + id: string; + name: string; + descriptionJson: any; + seoDescription: string | null; + seoTitle: string | null; + translation: ProductTranslationUpdateFragment_translation | null; +} diff --git a/src/fragments/types/ProductTypeTranslationFragment.ts b/src/fragments/types/ProductTypeTranslationFragment.ts index ac30567ab..ba108edb8 100644 --- a/src/fragments/types/ProductTypeTranslationFragment.ts +++ b/src/fragments/types/ProductTypeTranslationFragment.ts @@ -6,64 +6,46 @@ // GraphQL fragment: ProductTypeTranslationFragment // ==================================================== -export interface ProductTypeTranslationFragment_productAttributes_translation { +export interface ProductTypeTranslationFragment_product_productType_productAttributes_translation { __typename: "AttributeTranslation"; id: string; name: string; } -export interface ProductTypeTranslationFragment_productAttributes_values_translation { - __typename: "AttributeValueTranslation"; - id: string; - name: string; -} - -export interface ProductTypeTranslationFragment_productAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - translation: ProductTypeTranslationFragment_productAttributes_values_translation | null; -} - -export interface ProductTypeTranslationFragment_productAttributes { +export interface ProductTypeTranslationFragment_product_productType_productAttributes { __typename: "Attribute"; id: string; name: string | null; - translation: ProductTypeTranslationFragment_productAttributes_translation | null; - values: (ProductTypeTranslationFragment_productAttributes_values | null)[] | null; + translation: ProductTypeTranslationFragment_product_productType_productAttributes_translation | null; } -export interface ProductTypeTranslationFragment_variantAttributes_translation { +export interface ProductTypeTranslationFragment_product_productType_variantAttributes_translation { __typename: "AttributeTranslation"; id: string; name: string; } -export interface ProductTypeTranslationFragment_variantAttributes_values_translation { - __typename: "AttributeValueTranslation"; - id: string; - name: string; -} - -export interface ProductTypeTranslationFragment_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - translation: ProductTypeTranslationFragment_variantAttributes_values_translation | null; -} - -export interface ProductTypeTranslationFragment_variantAttributes { +export interface ProductTypeTranslationFragment_product_productType_variantAttributes { __typename: "Attribute"; id: string; name: string | null; - translation: ProductTypeTranslationFragment_variantAttributes_translation | null; - values: (ProductTypeTranslationFragment_variantAttributes_values | null)[] | null; + translation: ProductTypeTranslationFragment_product_productType_variantAttributes_translation | null; } -export interface ProductTypeTranslationFragment { +export interface ProductTypeTranslationFragment_product_productType { __typename: "ProductType"; id: string; name: string; - productAttributes: (ProductTypeTranslationFragment_productAttributes | null)[] | null; - variantAttributes: (ProductTypeTranslationFragment_variantAttributes | null)[] | null; + productAttributes: (ProductTypeTranslationFragment_product_productType_productAttributes | null)[] | null; + variantAttributes: (ProductTypeTranslationFragment_product_productType_variantAttributes | null)[] | null; +} + +export interface ProductTypeTranslationFragment_product { + __typename: "Product"; + productType: ProductTypeTranslationFragment_product_productType; +} + +export interface ProductTypeTranslationFragment { + __typename: "ProductTranslatableContent"; + product: ProductTypeTranslationFragment_product | null; } diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index 928e068c8..b02a1d6c9 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -49,24 +49,12 @@ export interface ProductVariant_attributes { values: (ProductVariant_attributes_values | null)[]; } -export interface ProductVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface ProductVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -85,6 +73,25 @@ export interface ProductVariant_product_thumbnail { url: string; } +export interface ProductVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariant_product_channelListings_channel; + discountedPrice: ProductVariant_product_channelListings_discountedPrice | null; +} + export interface ProductVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -106,9 +113,36 @@ export interface ProductVariant_product { images: (ProductVariant_product_images | null)[] | null; name: string; thumbnail: ProductVariant_product_thumbnail | null; + channelListings: ProductVariant_product_channelListings[] | null; variants: (ProductVariant_product_variants | null)[] | null; } +export interface ProductVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductVariant_channelListings_channel; + price: ProductVariant_channelListings_price | null; + costPrice: ProductVariant_channelListings_costPrice | null; +} + export interface ProductVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -135,11 +169,10 @@ export interface ProductVariant { metadata: (ProductVariant_metadata | null)[]; privateMetadata: (ProductVariant_privateMetadata | null)[]; attributes: ProductVariant_attributes[]; - costPrice: ProductVariant_costPrice | null; images: (ProductVariant_images | null)[] | null; name: string; - price: ProductVariant_price | null; product: ProductVariant_product; + channelListings: ProductVariant_channelListings[] | null; sku: string; stocks: (ProductVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index 0349fd386..f2e0dbbcb 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -58,37 +58,23 @@ export interface ProductVariantAttributesFragment_productType { variantAttributes: (ProductVariantAttributesFragment_productType_variantAttributes | null)[] | null; } -export interface ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_start_gross { +export interface ProductVariantAttributesFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantAttributesFragment_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductVariantAttributesFragment_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_start | null; - stop: ProductVariantAttributesFragment_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductVariantAttributesFragment_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductVariantAttributesFragment_pricing_priceRangeUndiscounted | null; +export interface ProductVariantAttributesFragment_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantAttributesFragment_channelListings_channel; + discountedPrice: ProductVariantAttributesFragment_channelListings_discountedPrice | null; } export interface ProductVariantAttributesFragment { @@ -96,5 +82,5 @@ export interface ProductVariantAttributesFragment { id: string; attributes: ProductVariantAttributesFragment_attributes[]; productType: ProductVariantAttributesFragment_productType; - pricing: ProductVariantAttributesFragment_pricing | null; + channelListings: ProductVariantAttributesFragment_channelListings[] | null; } diff --git a/src/fragments/types/SaleDetailsFragment.ts b/src/fragments/types/SaleDetailsFragment.ts index be8362a70..728593a8e 100644 --- a/src/fragments/types/SaleDetailsFragment.ts +++ b/src/fragments/types/SaleDetailsFragment.ts @@ -8,6 +8,21 @@ import { SaleType } from "./../../types/globalTypes"; // GraphQL fragment: SaleDetailsFragment // ==================================================== +export interface SaleDetailsFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SaleDetailsFragment_channelListings { + __typename: "SaleChannelListing"; + id: string; + channel: SaleDetailsFragment_channelListings_channel; + discountValue: number; + currency: string; +} + export interface SaleDetailsFragment_products_edges_node_productType { __typename: "ProductType"; id: string; @@ -19,13 +34,37 @@ export interface SaleDetailsFragment_products_edges_node_thumbnail { url: string; } +export interface SaleDetailsFragment_products_edges_node_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SaleDetailsFragment_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SaleDetailsFragment_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: SaleDetailsFragment_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: SaleDetailsFragment_products_edges_node_channelListings_channel; +} + export interface SaleDetailsFragment_products_edges_node { __typename: "Product"; id: string; name: string; - isPublished: boolean; productType: SaleDetailsFragment_products_edges_node_productType; thumbnail: SaleDetailsFragment_products_edges_node_thumbnail | null; + channelListings: SaleDetailsFragment_products_edges_node_channelListings[] | null; } export interface SaleDetailsFragment_products_edges { @@ -119,7 +158,7 @@ export interface SaleDetailsFragment { type: SaleType; startDate: any; endDate: any | null; - value: number; + channelListings: SaleDetailsFragment_channelListings[] | null; products: SaleDetailsFragment_products | null; categories: SaleDetailsFragment_categories | null; collections: SaleDetailsFragment_collections | null; diff --git a/src/fragments/types/SaleFragment.ts b/src/fragments/types/SaleFragment.ts index ef6a4ff0e..1a21e9e85 100644 --- a/src/fragments/types/SaleFragment.ts +++ b/src/fragments/types/SaleFragment.ts @@ -8,6 +8,21 @@ import { SaleType } from "./../../types/globalTypes"; // GraphQL fragment: SaleFragment // ==================================================== +export interface SaleFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SaleFragment_channelListings { + __typename: "SaleChannelListing"; + id: string; + channel: SaleFragment_channelListings_channel; + discountValue: number; + currency: string; +} + export interface SaleFragment { __typename: "Sale"; id: string; @@ -15,5 +30,5 @@ export interface SaleFragment { type: SaleType; startDate: any; endDate: any | null; - value: number; + channelListings: SaleFragment_channelListings[] | null; } diff --git a/src/fragments/types/SaleTranslationFragment.ts b/src/fragments/types/SaleTranslationFragment.ts index 3978bf69e..0001ed65c 100644 --- a/src/fragments/types/SaleTranslationFragment.ts +++ b/src/fragments/types/SaleTranslationFragment.ts @@ -8,6 +8,12 @@ import { LanguageCodeEnum } from "./../../types/globalTypes"; // GraphQL fragment: SaleTranslationFragment // ==================================================== +export interface SaleTranslationFragment_sale { + __typename: "Sale"; + id: string; + name: string; +} + export interface SaleTranslationFragment_translation_language { __typename: "LanguageDisplay"; code: LanguageCodeEnum; @@ -22,8 +28,7 @@ export interface SaleTranslationFragment_translation { } export interface SaleTranslationFragment { - __typename: "Sale"; - id: string; - name: string; + __typename: "SaleTranslatableContent"; + sale: SaleTranslationFragment_sale | null; translation: SaleTranslationFragment_translation | null; } diff --git a/src/fragments/types/SaleTranslationUpdateFragment.ts b/src/fragments/types/SaleTranslationUpdateFragment.ts new file mode 100644 index 000000000..1ebf86144 --- /dev/null +++ b/src/fragments/types/SaleTranslationUpdateFragment.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { LanguageCodeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: SaleTranslationUpdateFragment +// ==================================================== + +export interface SaleTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + code: LanguageCodeEnum; + language: string; +} + +export interface SaleTranslationUpdateFragment_translation { + __typename: "SaleTranslation"; + id: string; + language: SaleTranslationUpdateFragment_translation_language; + name: string | null; +} + +export interface SaleTranslationUpdateFragment { + __typename: "Sale"; + id: string; + name: string; + translation: SaleTranslationUpdateFragment_translation | null; +} diff --git a/src/fragments/types/ShippingChannelsErrorFragment.ts b/src/fragments/types/ShippingChannelsErrorFragment.ts new file mode 100644 index 000000000..401d1c1f6 --- /dev/null +++ b/src/fragments/types/ShippingChannelsErrorFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ShippingErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ShippingChannelsErrorFragment +// ==================================================== + +export interface ShippingChannelsErrorFragment { + __typename: "ShippingError"; + code: ShippingErrorCode; + field: string | null; + channels: string[] | null; +} diff --git a/src/fragments/types/ShippingMethodFragment.ts b/src/fragments/types/ShippingMethodFragment.ts index fb3c79e9d..de7a4692e 100644 --- a/src/fragments/types/ShippingMethodFragment.ts +++ b/src/fragments/types/ShippingMethodFragment.ts @@ -8,44 +8,58 @@ import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTyp // GraphQL fragment: ShippingMethodFragment // ==================================================== -export interface ShippingMethodFragment_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingMethodFragment_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingMethodFragment_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingMethodFragment_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingMethodFragment_price { +export interface ShippingMethodFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ShippingMethodFragment_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface ShippingMethodFragment_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodFragment_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodFragment_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: ShippingMethodFragment_channelListings_channel; + price: ShippingMethodFragment_channelListings_price | null; + minimumOrderPrice: ShippingMethodFragment_channelListings_minimumOrderPrice | null; + maximumOrderPrice: ShippingMethodFragment_channelListings_maximumOrderPrice | null; +} + export interface ShippingMethodFragment { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: ShippingMethodFragment_minimumOrderPrice | null; minimumOrderWeight: ShippingMethodFragment_minimumOrderWeight | null; - maximumOrderPrice: ShippingMethodFragment_maximumOrderPrice | null; maximumOrderWeight: ShippingMethodFragment_maximumOrderWeight | null; name: string; - price: ShippingMethodFragment_price | null; type: ShippingMethodTypeEnum | null; + channelListings: ShippingMethodFragment_channelListings[] | null; } diff --git a/src/fragments/types/ShippingMethodTranslationFragment.ts b/src/fragments/types/ShippingMethodTranslationFragment.ts index 565d2b42b..803e93815 100644 --- a/src/fragments/types/ShippingMethodTranslationFragment.ts +++ b/src/fragments/types/ShippingMethodTranslationFragment.ts @@ -22,7 +22,7 @@ export interface ShippingMethodTranslationFragment_translation { } export interface ShippingMethodTranslationFragment { - __typename: "ShippingMethod"; + __typename: "ShippingMethodTranslatableContent"; id: string; name: string; translation: ShippingMethodTranslationFragment_translation | null; diff --git a/src/fragments/types/ShippingZoneDetailsFragment.ts b/src/fragments/types/ShippingZoneDetailsFragment.ts index c55177fa9..5f941c4a6 100644 --- a/src/fragments/types/ShippingZoneDetailsFragment.ts +++ b/src/fragments/types/ShippingZoneDetailsFragment.ts @@ -14,46 +14,60 @@ export interface ShippingZoneDetailsFragment_countries { country: string; } -export interface ShippingZoneDetailsFragment_shippingMethods_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingZoneDetailsFragment_shippingMethods_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingZoneDetailsFragment_shippingMethods_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingZoneDetailsFragment_shippingMethods_price { +export interface ShippingZoneDetailsFragment_shippingMethods_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ShippingZoneDetailsFragment_shippingMethods_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface ShippingZoneDetailsFragment_shippingMethods_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingZoneDetailsFragment_shippingMethods_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingZoneDetailsFragment_shippingMethods_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: ShippingZoneDetailsFragment_shippingMethods_channelListings_channel; + price: ShippingZoneDetailsFragment_shippingMethods_channelListings_price | null; + minimumOrderPrice: ShippingZoneDetailsFragment_shippingMethods_channelListings_minimumOrderPrice | null; + maximumOrderPrice: ShippingZoneDetailsFragment_shippingMethods_channelListings_maximumOrderPrice | null; +} + export interface ShippingZoneDetailsFragment_shippingMethods { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: ShippingZoneDetailsFragment_shippingMethods_minimumOrderPrice | null; minimumOrderWeight: ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight | null; - maximumOrderPrice: ShippingZoneDetailsFragment_shippingMethods_maximumOrderPrice | null; maximumOrderWeight: ShippingZoneDetailsFragment_shippingMethods_maximumOrderWeight | null; name: string; - price: ShippingZoneDetailsFragment_shippingMethods_price | null; type: ShippingMethodTypeEnum | null; + channelListings: ShippingZoneDetailsFragment_shippingMethods_channelListings[] | null; } export interface ShippingZoneDetailsFragment_warehouses { diff --git a/src/fragments/types/VoucherDetailsFragment.ts b/src/fragments/types/VoucherDetailsFragment.ts index c25c7a7dd..b92280532 100644 --- a/src/fragments/types/VoucherDetailsFragment.ts +++ b/src/fragments/types/VoucherDetailsFragment.ts @@ -14,10 +14,26 @@ export interface VoucherDetailsFragment_countries { country: string; } -export interface VoucherDetailsFragment_minSpent { +export interface VoucherDetailsFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VoucherDetailsFragment_channelListings_minSpent { __typename: "Money"; - currency: string; amount: number; + currency: string; +} + +export interface VoucherDetailsFragment_channelListings { + __typename: "VoucherChannelListing"; + id: string; + channel: VoucherDetailsFragment_channelListings_channel; + discountValue: number; + currency: string; + minSpent: VoucherDetailsFragment_channelListings_minSpent | null; } export interface VoucherDetailsFragment_products_edges_node_productType { @@ -31,13 +47,37 @@ export interface VoucherDetailsFragment_products_edges_node_thumbnail { url: string; } +export interface VoucherDetailsFragment_products_edges_node_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VoucherDetailsFragment_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VoucherDetailsFragment_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: VoucherDetailsFragment_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: VoucherDetailsFragment_products_edges_node_channelListings_channel; +} + export interface VoucherDetailsFragment_products_edges_node { __typename: "Product"; id: string; name: string; productType: VoucherDetailsFragment_products_edges_node_productType; - isPublished: boolean; thumbnail: VoucherDetailsFragment_products_edges_node_thumbnail | null; + channelListings: VoucherDetailsFragment_products_edges_node_channelListings[] | null; } export interface VoucherDetailsFragment_products_edges { @@ -132,10 +172,9 @@ export interface VoucherDetailsFragment { endDate: any | null; usageLimit: number | null; discountValueType: DiscountValueTypeEnum; - discountValue: number; countries: (VoucherDetailsFragment_countries | null)[] | null; - minSpent: VoucherDetailsFragment_minSpent | null; minCheckoutItemsQuantity: number | null; + channelListings: VoucherDetailsFragment_channelListings[] | null; type: VoucherTypeEnum; used: number; applyOncePerOrder: boolean; diff --git a/src/fragments/types/VoucherFragment.ts b/src/fragments/types/VoucherFragment.ts index a41f22e8c..0ecefa877 100644 --- a/src/fragments/types/VoucherFragment.ts +++ b/src/fragments/types/VoucherFragment.ts @@ -14,10 +14,26 @@ export interface VoucherFragment_countries { country: string; } -export interface VoucherFragment_minSpent { +export interface VoucherFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VoucherFragment_channelListings_minSpent { __typename: "Money"; - currency: string; amount: number; + currency: string; +} + +export interface VoucherFragment_channelListings { + __typename: "VoucherChannelListing"; + id: string; + channel: VoucherFragment_channelListings_channel; + discountValue: number; + currency: string; + minSpent: VoucherFragment_channelListings_minSpent | null; } export interface VoucherFragment { @@ -28,8 +44,7 @@ export interface VoucherFragment { endDate: any | null; usageLimit: number | null; discountValueType: DiscountValueTypeEnum; - discountValue: number; countries: (VoucherFragment_countries | null)[] | null; - minSpent: VoucherFragment_minSpent | null; minCheckoutItemsQuantity: number | null; + channelListings: VoucherFragment_channelListings[] | null; } diff --git a/src/fragments/types/VoucherTranslationFragment.ts b/src/fragments/types/VoucherTranslationFragment.ts index 080d3489c..3b06fafe3 100644 --- a/src/fragments/types/VoucherTranslationFragment.ts +++ b/src/fragments/types/VoucherTranslationFragment.ts @@ -8,6 +8,12 @@ import { LanguageCodeEnum } from "./../../types/globalTypes"; // GraphQL fragment: VoucherTranslationFragment // ==================================================== +export interface VoucherTranslationFragment_voucher { + __typename: "Voucher"; + id: string; + name: string | null; +} + export interface VoucherTranslationFragment_translation_language { __typename: "LanguageDisplay"; code: LanguageCodeEnum; @@ -22,8 +28,7 @@ export interface VoucherTranslationFragment_translation { } export interface VoucherTranslationFragment { - __typename: "Voucher"; - id: string; - name: string | null; + __typename: "VoucherTranslatableContent"; + voucher: VoucherTranslationFragment_voucher | null; translation: VoucherTranslationFragment_translation | null; } diff --git a/src/fragments/types/VoucherTranslationUpdateFragment.ts b/src/fragments/types/VoucherTranslationUpdateFragment.ts new file mode 100644 index 000000000..c4a3f7341 --- /dev/null +++ b/src/fragments/types/VoucherTranslationUpdateFragment.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { LanguageCodeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: VoucherTranslationUpdateFragment +// ==================================================== + +export interface VoucherTranslationUpdateFragment_translation_language { + __typename: "LanguageDisplay"; + code: LanguageCodeEnum; + language: string; +} + +export interface VoucherTranslationUpdateFragment_translation { + __typename: "VoucherTranslation"; + id: string; + language: VoucherTranslationUpdateFragment_translation_language; + name: string | null; +} + +export interface VoucherTranslationUpdateFragment { + __typename: "Voucher"; + id: string; + name: string | null; + translation: VoucherTranslationUpdateFragment_translation | null; +} diff --git a/src/home/components/HomeHeader/HomeHeader.tsx b/src/home/components/HomeHeader/HomeHeader.tsx index 4a1ef43f9..de2cf15b8 100644 --- a/src/home/components/HomeHeader/HomeHeader.tsx +++ b/src/home/components/HomeHeader/HomeHeader.tsx @@ -1,13 +1,19 @@ import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; +import SingleSelectField, { + Choices +} from "@saleor/components/SingleSelectField"; import Skeleton from "@saleor/components/Skeleton"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; const useStyles = makeStyles( theme => ({ headerContainer: { - marginBottom: theme.spacing(3) + alignItems: "flex-end", + display: "flex", + justifyContent: "space-between", + marginBottom: theme.spacing(6) }, pageHeader: { fontWeight: 600 as 600 @@ -21,44 +27,64 @@ const useStyles = makeStyles( interface HomeOrdersCardProps { userName: string; + channelChoices: Choices; + channelValue: string; + onChannelChange: (value: string) => void; } const HomeOrdersCard: React.FC = props => { - const { userName } = props; + const { userName, channelChoices, channelValue, onChannelChange } = props; const classes = useStyles(props); + const intl = useIntl(); return (
- - {userName ? ( - + + {userName ? ( + + ) : ( + + )} + + + {userName ? ( + + ) : ( + + )} + +
+
+ {!!channelChoices?.length && ( + onChannelChange(e.target.value)} + data-test="channel-select" /> - ) : ( - )} - - - {userName ? ( - - ) : ( - - )} - +
); }; diff --git a/src/home/components/HomePage/HomePage.tsx b/src/home/components/HomePage/HomePage.tsx index 072173bcd..8291e6eb3 100644 --- a/src/home/components/HomePage/HomePage.tsx +++ b/src/home/components/HomePage/HomePage.tsx @@ -4,6 +4,7 @@ import Container from "@saleor/components/Container"; import Grid from "@saleor/components/Grid"; import Money from "@saleor/components/Money"; import RequirePermissions from "@saleor/components/RequirePermissions"; +import { Choices } from "@saleor/components/SingleSelectField"; import Skeleton from "@saleor/components/Skeleton"; import { UserPermissionProps } from "@saleor/types"; import { PermissionEnum } from "@saleor/types/globalTypes"; @@ -53,6 +54,9 @@ export interface HomePageProps extends UserPermissionProps { sales: Home_salesToday_gross; topProducts: Home_productTopToday_edges_node[]; userName: string; + channelChoices: Choices; + channelValue: string; + onChannelChange: (value: string) => void; onOrdersToCaptureClick: () => void; onOrdersToFulfillClick: () => void; onProductClick: (productId: string, variantId: string) => void; @@ -61,6 +65,8 @@ export interface HomePageProps extends UserPermissionProps { const HomePage: React.FC = props => { const { + channelChoices, + channelValue, userName, orders, sales, @@ -72,6 +78,7 @@ const HomePage: React.FC = props => { onProductsOutOfStockClick, ordersToCapture, ordersToFulfill, + onChannelChange, productsOutOfStock, userPermissions } = props; @@ -80,7 +87,12 @@ const HomePage: React.FC = props => { return ( - +
diff --git a/src/home/queries.ts b/src/home/queries.ts index c70a51d60..a286a72f2 100644 --- a/src/home/queries.ts +++ b/src/home/queries.ts @@ -4,26 +4,33 @@ import { TypedQuery } from "../queries"; import { Home } from "./types/Home"; const home = gql` - query Home { - salesToday: ordersTotal(period: TODAY) { + query Home($channel: String!) { + salesToday: ordersTotal(period: TODAY, channel: $channel) { gross { amount currency } } - ordersToday: orders(created: TODAY) { + ordersToday: orders(created: TODAY, channel: $channel) { totalCount } - ordersToFulfill: orders(status: READY_TO_FULFILL) { + ordersToFulfill: orders(status: READY_TO_FULFILL, channel: $channel) { totalCount } - ordersToCapture: orders(status: READY_TO_CAPTURE) { + ordersToCapture: orders(status: READY_TO_CAPTURE, channel: $channel) { totalCount } - productsOutOfStock: products(stockAvailability: OUT_OF_STOCK) { + productsOutOfStock: products( + stockAvailability: OUT_OF_STOCK + channel: $channel + ) { totalCount } - productTopToday: reportProductSales(period: TODAY, first: 5) { + productTopToday: reportProductSales( + period: TODAY + first: 5 + channel: $channel + ) { edges { node { id diff --git a/src/home/types/Home.ts b/src/home/types/Home.ts index 6e2fa0a9f..0fd47e85b 100644 --- a/src/home/types/Home.ts +++ b/src/home/types/Home.ts @@ -133,3 +133,7 @@ export interface Home { productTopToday: Home_productTopToday | null; activities: Home_activities | null; } + +export interface HomeVariables { + channel: string; +} diff --git a/src/home/views/index.tsx b/src/home/views/index.tsx index 2c1016991..0102a8fb4 100644 --- a/src/home/views/index.tsx +++ b/src/home/views/index.tsx @@ -1,6 +1,8 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; -import React from "react"; +import React, { useCallback, useEffect } from "react"; import { getUserName, maybe } from "../../misc"; import { orderListUrl } from "../../orders/urls"; @@ -12,9 +14,29 @@ import { HomePageQuery } from "../queries"; const HomeSection = () => { const navigate = useNavigator(); const { user } = useUser(); + const { data: channelsData } = useChannelsList({}); + + const channelChoices = + channelsData?.channels?.map(channel => ({ + label: channel.name, + value: channel.slug + })) || []; + + const [channelChoice, setChannelChoice] = useLocalStorage( + "homepageChannelChoice", + channelChoices?.length ? channelChoices[0]?.value : "" + ); + + useEffect(() => { + if (!channelChoice && channelChoices[0]) { + setChannelChoice(channelChoices[0].value); + } + }, [channelChoices]); + + const handleChannelChange = useCallback(value => setChannelChoice(value), []); return ( - + {({ data }) => ( @@ -25,6 +47,9 @@ const HomeSection = () => { topProducts={maybe(() => data.productTopToday.edges.map(edge => edge.node) )} + channelChoices={channelChoices} + channelValue={channelChoice} + onChannelChange={handleChannelChange} onProductClick={(productId, variantId) => navigate(productVariantEditUrl(productId, variantId)) } diff --git a/src/hooks/useChannels.ts b/src/hooks/useChannels.ts new file mode 100644 index 000000000..a041bc252 --- /dev/null +++ b/src/hooks/useChannels.ts @@ -0,0 +1,52 @@ +import { Channel } from "@saleor/channels/utils"; +import useListActions from "@saleor/hooks/useListActions"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { useState } from "react"; + +function useChannels(channels: T[]) { + const [isChannelsModalOpen, setChannelsModalOpen] = useState(false); + + const [currentChannels, setCurrentChannels] = useStateFromProps(channels); + + const { + isSelected: isChannelSelected, + listElements: channelListElements, + set: setChannels, + toggle: channelsToggle + } = useListActions(currentChannels, (a, b) => a.id === b.id); + + const handleChannelsModalClose = () => { + setChannelsModalOpen(false); + setChannels(currentChannels); + }; + + const handleChannelsModalOpen = () => setChannelsModalOpen(true); + + const handleChannelsConfirm = () => { + setCurrentChannels(channelListElements); + setChannelsModalOpen(false); + }; + + const toggleAllChannels = (items: T[], selected: number) => { + if (selected !== items.length) { + setChannels(items); + } else { + setChannels([]); + } + }; + + return { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + }; +} + +export default useChannels; diff --git a/src/hooks/useChannelsSettings.tsx b/src/hooks/useChannelsSettings.tsx new file mode 100644 index 000000000..494c21cf8 --- /dev/null +++ b/src/hooks/useChannelsSettings.tsx @@ -0,0 +1,44 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import { useEffect } from "react"; + +interface Actions { + openModal: (value: string) => void; + closeModal: () => void; +} + +function useChannelsSettings(key: string, { openModal, closeModal }: Actions) { + const { data: channelsData } = useChannelsList({}); + const channelChoices = channelsData?.channels?.map(channel => ({ + label: channel.name, + value: channel.id + })); + + const [selectedChannel, setSelectedChannel] = useLocalStorage(key, ""); + + const handleChannelSelectConfirm = (channel: string) => { + setSelectedChannel(channel); + closeModal(); + }; + + useEffect(() => { + if (!selectedChannel) { + openModal("settings"); + } + }, [selectedChannel]); + + const channel = channelsData?.channels?.find( + channel => channel.id === selectedChannel + ); + + return { + channel, + channelChoices, + channels: channelsData?.channels, + handleChannelSelectConfirm, + selectedChannel, + slug: channel?.slug + }; +} + +export default useChannelsSettings; diff --git a/src/hooks/useInterval.ts b/src/hooks/useInterval.ts new file mode 100644 index 000000000..8bd21aef1 --- /dev/null +++ b/src/hooks/useInterval.ts @@ -0,0 +1,20 @@ +import { useEffect, useRef } from "react"; + +function useInterval(callback: () => void, delay: number | null) { + const savedCallback = useRef<() => void>(); + + useEffect(() => { + savedCallback.current = callback; + }); + + useEffect(() => { + function tick() { + savedCallback.current(); + } + + const id = setInterval(tick, delay); + return () => clearInterval(id); + }, []); +} + +export default useInterval; diff --git a/src/icons/Channels.tsx b/src/icons/Channels.tsx new file mode 100644 index 000000000..182880b4f --- /dev/null +++ b/src/icons/Channels.tsx @@ -0,0 +1,16 @@ +import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; +import React from "react"; + +const Channels = createSvgIcon( + <> + + , + "StaffMembers" +); + +export default Channels; diff --git a/src/index.tsx b/src/index.tsx index cba960793..fb59f83e8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,6 +23,8 @@ import SectionRoute from "./auth/components/SectionRoute"; import authLink from "./auth/link"; import { hasPermission } from "./auth/misc"; import CategorySection from "./categories"; +import ChannelsSection from "./channels"; +import { channelsSection } from "./channels/urls"; import CollectionSection from "./collections"; import AppLayout from "./components/AppLayout"; import { DateProvider } from "./components/Date"; @@ -191,9 +193,7 @@ const Routes: React.FC = () => { component={ProductSection} /> @@ -233,9 +233,7 @@ const Routes: React.FC = () => { component={NavigationSection} /> @@ -249,6 +247,11 @@ const Routes: React.FC = () => { path={warehouseSection} component={WarehouseSection} /> + {createConfigurationMenu(intl).filter(menu => menu.menuItems.map(item => hasPermission(item.permission, user)) ).length > 0 && ( diff --git a/src/intl.ts b/src/intl.ts index 6fe6a3e68..8b9d16f2c 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -179,6 +179,10 @@ export const sectionNames = defineMessages({ defaultMessage: "Categories", description: "categories section name" }, + channels: { + defaultMessage: "Channels", + description: "channels section name" + }, collections: { defaultMessage: "Collections", description: "collections section name" @@ -195,6 +199,10 @@ export const sectionNames = defineMessages({ defaultMessage: "Draft Orders", description: "draft orders section name" }, + exchangeRates: { + defaultMessage: "Exchange Rates", + description: "Manage and Update your warehouse information" + }, home: { defaultMessage: "Home", description: "home section name" diff --git a/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.stories.tsx b/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.stories.tsx new file mode 100644 index 000000000..a205b4805 --- /dev/null +++ b/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.stories.tsx @@ -0,0 +1,20 @@ +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import DraftOrderChannelSectionCard, { + DraftOrderChannelSectionCardProps +} from "."; + +const props: DraftOrderChannelSectionCardProps = { + channelName: "Default Channel" +}; + +storiesOf("Orders / Draft order channel section", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )); diff --git a/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.tsx b/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.tsx new file mode 100644 index 000000000..632643aad --- /dev/null +++ b/src/orders/components/DraftOrderChannelSectionCard/DraftOrderChannelSectionCard.tsx @@ -0,0 +1,31 @@ +import { Card, CardContent, Typography } from "@material-ui/core"; +import CardTitle from "@saleor/components/CardTitle"; +import Skeleton from "@saleor/components/Skeleton"; +import React from "react"; +import { useIntl } from "react-intl"; + +export interface DraftOrderChannelSectionCardProps { + channelName: string; +} + +export const DraftOrderChannelSectionCard: React.FC = ({ + channelName +}) => { + const intl = useIntl(); + + return ( + + + + {!channelName ? : {channelName}} + + + ); +}; +DraftOrderChannelSectionCard.displayName = "DraftOrderChannelSectionCard"; +export default DraftOrderChannelSectionCard; diff --git a/src/orders/components/DraftOrderChannelSectionCard/index.ts b/src/orders/components/DraftOrderChannelSectionCard/index.ts new file mode 100644 index 000000000..f1ee85f97 --- /dev/null +++ b/src/orders/components/DraftOrderChannelSectionCard/index.ts @@ -0,0 +1,2 @@ +export { default } from "./DraftOrderChannelSectionCard"; +export * from "./DraftOrderChannelSectionCard"; diff --git a/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.stories.tsx b/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.stories.tsx new file mode 100644 index 000000000..d48ceb792 --- /dev/null +++ b/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.stories.tsx @@ -0,0 +1,18 @@ +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import OrderChannelSectionCard, { OrderChannelSectionCardProps } from "."; + +const props: OrderChannelSectionCardProps = { + selectedChannelName: "International store" +}; + +storiesOf("Orders / Order details channel section", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )); diff --git a/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.tsx b/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.tsx new file mode 100644 index 000000000..5e6887713 --- /dev/null +++ b/src/orders/components/OrderChannelSectionCard/OrderChannelSectionCard.tsx @@ -0,0 +1,35 @@ +import { Card, CardContent, Typography } from "@material-ui/core"; +import CardTitle from "@saleor/components/CardTitle"; +import Skeleton from "@saleor/components/Skeleton"; +import React from "react"; +import { useIntl } from "react-intl"; + +export interface OrderChannelSectionCardProps { + selectedChannelName: string; +} + +export const OrderChannelSectionCard: React.FC = ({ + selectedChannelName +}) => { + const intl = useIntl(); + + return ( + + + + {selectedChannelName === undefined ? ( + + ) : ( + {selectedChannelName} + )} + + + ); +}; +OrderChannelSectionCard.displayName = "OrderChannelSectionCard"; +export default OrderChannelSectionCard; diff --git a/src/orders/components/OrderChannelSectionCard/index.ts b/src/orders/components/OrderChannelSectionCard/index.ts new file mode 100644 index 000000000..dda1d3aa5 --- /dev/null +++ b/src/orders/components/OrderChannelSectionCard/index.ts @@ -0,0 +1,2 @@ +export { default } from "./OrderChannelSectionCard"; +export * from "./OrderChannelSectionCard"; diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index 732e3970f..971ae6ada 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -14,6 +14,7 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar"; import Skeleton from "@saleor/components/Skeleton"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; +import OrderChannelSectionCard from "@saleor/orders/components/OrderChannelSectionCard"; import { UserPermissionProps } from "@saleor/types"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -212,7 +213,6 @@ const OrderDetailsPage: React.FC = props => { onRefund={onPaymentRefund} onVoid={onPaymentVoid} /> - order.events)} @@ -230,6 +230,10 @@ const OrderDetailsPage: React.FC = props => { onProfileView={onProfileView} /> + + void; onOrderLineChange: ( @@ -24,6 +25,7 @@ interface OrderDraftDetailsProps { } const OrderDraftDetails: React.FC = ({ + disabled, order, onOrderLineAdd, onOrderLineChange, @@ -40,12 +42,15 @@ const OrderDraftDetails: React.FC = ({ description: "section header" })} toolbar={ - + !disabled && + order?.channel?.isActive && ( + + ) } /> = ({ {maybe(() => order.lines.length) !== 0 && ( diff --git a/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx b/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx index 3b73772e9..1c39bb069 100644 --- a/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx +++ b/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx @@ -128,32 +128,14 @@ const OrderDraftDetailsProducts: React.FC = prop > {maybe(() => line.productName && line.productSku) ? ( <> - {line.productName} - {line.productSku} - {!line.variant.quantityAvailable ? ( - - + <> + + {line.productName} - ) : !line.variant.product.isAvailableForPurchase ? ( - - + + {line.productSku} - ) : ( - !line.variant.product.isPublished && ( - - - - ) - )} + ) : ( diff --git a/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx b/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx index 4bfe8e8c3..6710a75c4 100644 --- a/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx +++ b/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx @@ -23,12 +23,13 @@ const useStyles = makeStyles( ); interface OrderDraftDetailsSummaryProps { + disabled?: boolean; order: OrderDetails_order; onShippingMethodEdit: () => void; } const OrderDraftDetailsSummary: React.FC = props => { - const { order, onShippingMethodEdit } = props; + const { disabled, order, onShippingMethodEdit } = props; const classes = useStyles(props); @@ -60,7 +61,8 @@ const OrderDraftDetailsSummary: React.FC = props order.shippingMethodName !== undefined ? ( order.shippingMethod === null ? ( order.availableShippingMethods && - order.availableShippingMethods.length > 0 ? ( + order.availableShippingMethods.length > 0 && + !disabled ? ( = props => { > - onSort(OrderDraftListUrlSortField.total)} - className={classes.colTotal} - > + , TabPageProps { orders: OrderDraftList_draftOrders_edges_node[]; + onSettingsOpen?: () => void; } +const useStyles = makeStyles( + theme => ({ + settings: { + marginRight: theme.spacing(2) + } + }), + { name: "OrderDraftListPage" } +); + const OrderDraftListPage: React.FC = ({ - currencySymbol, currentTab, disabled, filterOpts, @@ -42,6 +53,7 @@ const OrderDraftListPage: React.FC = ({ onAll, onFilterChange, onSearchChange, + onSettingsOpen, onTabChange, onTabDelete, onTabSave, @@ -49,12 +61,26 @@ const OrderDraftListPage: React.FC = ({ ...listProps }) => { const intl = useIntl(); - + const classes = useStyles({}); const structure = createFilterStructure(intl, filterOpts); return ( + {!!onSettingsOpen && ( + + )}
-
- -
+ + {channelListings.map(listing => ( +
+ +
+ ))} + {data.warehouses.map(warehouseId => (
["price", "quantity", "sku"], variantErrors ); - return ( ) )}
-
- - onVariantDataChange( - variantIndex, - "price", - event.target.value - ) - } - /> -
+ {channelListings.map(listing => { + const error = variantFormErrors.price?.channels?.find( + id => id === listing.id + ); + return ( +
+ channel.channelId === listing.id + )?.price + } + required + onChange={event => + onVariantPriceDataChange(variantIndex, { + channelId: listing.id, + price: event.target.value + }) + } + /> +
+ ); + })} {variant.stocks.map(stock => (
fullWidth value={variant.sku} onChange={event => - onVariantDataChange(variantIndex, "sku", event.target.value) + onVariantSkuChange(variantIndex, event.target.value) } />
diff --git a/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap index 23447aca2..1609ff601 100644 --- a/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap +++ b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap @@ -27,16 +27,55 @@ Object { ], "price": Object { "attribute": "attr-2", + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "mode": "attribute", - "value": "", "values": Array [ Object { "slug": "val-2-2", - "value": "24.99", + "value": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], }, Object { "slug": "val-2-4", - "value": "26.99", + "value": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], }, ], }, @@ -87,7 +126,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -129,7 +181,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -171,7 +236,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -213,7 +291,20 @@ Object { ], }, ], - "price": "49.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "7", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -255,7 +346,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -297,7 +401,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -339,7 +456,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -381,7 +511,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -439,16 +582,55 @@ Object { ], "price": Object { "attribute": "attr-2", + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "mode": "attribute", - "value": "", "values": Array [ Object { "slug": "val-2-2", - "value": "24.99", + "value": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], }, Object { "slug": "val-2-4", - "value": "26.99", + "value": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], }, ], }, @@ -499,7 +681,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -541,7 +736,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -583,7 +791,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -625,7 +846,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -667,7 +901,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -709,7 +956,20 @@ Object { ], }, ], - "price": "24.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -751,7 +1011,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -793,7 +1066,20 @@ Object { ], }, ], - "price": "26.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "0", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "2", + }, + ], "sku": "", "stocks": Array [ Object { @@ -851,8 +1137,21 @@ Object { ], "price": Object { "attribute": undefined, + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "all", - "value": "10.99", "values": Array [], }, "stock": Object { @@ -893,8 +1192,21 @@ Object { ], "price": Object { "attribute": undefined, + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "all", - "value": "45.99", "values": Array [], }, "stock": Object { @@ -930,7 +1242,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -972,7 +1297,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1014,7 +1352,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1056,7 +1407,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1098,7 +1462,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1140,7 +1517,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1182,7 +1572,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1224,7 +1627,20 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "22.99", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1282,16 +1698,39 @@ Object { ], "price": Object { "attribute": "attr-1", + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "attribute", - "value": "10.99", "values": Array [ Object { "slug": "val-1-1", - "value": "45.99", + "value": Array [ + Object { + "channelId": "channel-1", + "price": "45.99", + }, + ], }, Object { "slug": "val-1-7", - "value": "51.99", + "value": Array [ + Object { + "channelId": "channel-2", + "price": "51.99", + }, + ], }, ], }, @@ -1328,7 +1767,12 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "45.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1370,7 +1814,12 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "45.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1412,7 +1861,12 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "45.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1454,7 +1908,12 @@ Object { ], }, ], - "price": "45.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "45.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1496,7 +1955,12 @@ Object { ], }, ], - "price": "51.99", + "channelListings": Array [ + Object { + "channelId": "channel-2", + "price": "51.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1538,7 +2002,12 @@ Object { ], }, ], - "price": "51.99", + "channelListings": Array [ + Object { + "channelId": "channel-2", + "price": "51.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1580,7 +2049,12 @@ Object { ], }, ], - "price": "51.99", + "channelListings": Array [ + Object { + "channelId": "channel-2", + "price": "51.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1622,7 +2096,12 @@ Object { ], }, ], - "price": "51.99", + "channelListings": Array [ + Object { + "channelId": "channel-2", + "price": "51.99", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1680,8 +2159,21 @@ Object { ], "price": Object { "attribute": undefined, + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "all", - "value": "10.99", "values": Array [], }, "stock": Object { @@ -1717,7 +2209,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1759,7 +2264,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1801,7 +2319,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1843,7 +2374,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1885,7 +2429,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1927,7 +2484,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -1969,7 +2539,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2011,7 +2594,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2069,8 +2665,21 @@ Object { ], "price": Object { "attribute": undefined, + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "all", - "value": "10.99", "values": Array [], }, "stock": Object { @@ -2119,7 +2728,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2149,7 +2771,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2179,7 +2814,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2209,7 +2857,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2239,7 +2900,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2269,7 +2943,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2299,7 +2986,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2329,7 +3029,20 @@ Object { ], }, ], - "price": "10.99", + "channelListings": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "sku": "", "stocks": Array [ Object { @@ -2375,8 +3088,21 @@ Object { ], "price": Object { "attribute": undefined, + "channels": Array [ + Object { + "channelId": "channel-1", + "price": "1", + }, + Object { + "channelId": "channel-2", + "price": "2", + }, + Object { + "channelId": "channel-3", + "price": "3", + }, + ], "mode": "all", - "value": "10.99", "values": Array [], }, "stock": Object { diff --git a/src/products/components/ProductVariantCreatorPage/createVariants.test.ts b/src/products/components/ProductVariantCreatorPage/createVariants.test.ts index e0319c1ab..6eed8d71d 100644 --- a/src/products/components/ProductVariantCreatorPage/createVariants.test.ts +++ b/src/products/components/ProductVariantCreatorPage/createVariants.test.ts @@ -2,8 +2,8 @@ import { createVariantFlatMatrixDimension, createVariants } from "./createVariants"; -import { attributes, thirdStep } from "./fixtures"; -import { ProductVariantCreateFormData } from "./form"; +import { attributes, channels, thirdStep } from "./fixtures"; +import { ChannelPrice, ProductVariantCreateFormData } from "./form"; describe("Creates variant matrix", () => { it("with proper size", () => { @@ -17,15 +17,15 @@ describe("Creates variant matrix", () => { }); it("with constant price and stock", () => { - const price = "49.99"; + const channels: ChannelPrice[] = [{ channelId: "1", price: "2" }]; const stock = [80, 40, 30]; const data: ProductVariantCreateFormData = { ...thirdStep, price: { ...thirdStep.price, - mode: "all", - value: price + channels, + mode: "all" }, stock: { ...thirdStep.stock, @@ -43,7 +43,7 @@ describe("Creates variant matrix", () => { ); variants.forEach(variant => { - expect(variant.price).toBe(price); + expect(variant.channelListings[0].price).toBe(channels[0].price); variant.stocks.forEach((_, stockIndex) => { expect(variant.stocks[stockIndex].quantity).toBe(stock[stockIndex]); }); @@ -51,7 +51,6 @@ describe("Creates variant matrix", () => { }); it("with constant stock and attribute dependent price", () => { - const price = 49.99; const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id @@ -63,9 +62,12 @@ describe("Creates variant matrix", () => { ...thirdStep.price, attribute: attribute.id, mode: "attribute", - values: attribute.values.map((attributeValue, attributeValueIndex) => ({ + values: attribute.values.map((attributeValue, index) => ({ slug: attributeValue, - value: (price * (attributeValueIndex + 1)).toString() + value: channels.map(channel => ({ + channelId: channel.id, + price: (channel.price + index).toString() + })) })) }, stock: { @@ -98,15 +100,17 @@ describe("Creates variant matrix", () => { ).values[0] === attributeValue ) .forEach(variant => { - expect(variant.price).toBe( - (price * (attributeValueIndex + 1)).toString() - ); + variant.channelListings.map((channel, index) => { + expect(channel.price).toBe( + (channels[index].price + attributeValueIndex).toString() + ); + }); }); }); }); it("with constant price and attribute dependent stock", () => { - const price = "49.99"; + const price: ChannelPrice[] = [{ channelId: "1", price: "2" }]; const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id @@ -116,8 +120,8 @@ describe("Creates variant matrix", () => { ...thirdStep, price: { ...thirdStep.price, - mode: "all", - value: price + channels: price, + mode: "all" }, stock: { ...thirdStep.stock, @@ -141,7 +145,7 @@ describe("Creates variant matrix", () => { ); variants.forEach(variant => { - expect(variant.price).toBe(price); + expect(variant.channelListings).toBe(price); }); attribute.values.forEach((attributeValue, attributeValueIndex) => { @@ -163,7 +167,6 @@ describe("Creates variant matrix", () => { }); it("with attribute dependent price and stock", () => { - const price = 49.99; const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id @@ -175,9 +178,12 @@ describe("Creates variant matrix", () => { ...thirdStep.price, attribute: attribute.id, mode: "attribute", - values: attribute.values.map((attributeValue, attributeValueIndex) => ({ + values: attribute.values.map((attributeValue, index) => ({ slug: attributeValue, - value: (price * (attributeValueIndex + 1)).toString() + value: channels.map(channel => ({ + channelId: channel.id, + price: (channel.price + index).toString() + })) })) }, stock: { @@ -210,9 +216,11 @@ describe("Creates variant matrix", () => { ).values[0] === attributeValue ) .forEach(variant => { - expect(variant.price).toBe( - (price * (attributeValueIndex + 1)).toString() - ); + variant.channelListings.map((channel, index) => { + expect(channel.price).toBe( + (channels[index].price + attributeValueIndex).toString() + ); + }); }); }); diff --git a/src/products/components/ProductVariantCreatorPage/createVariants.ts b/src/products/components/ProductVariantCreatorPage/createVariants.ts index 462be5760..5d51db996 100644 --- a/src/products/components/ProductVariantCreatorPage/createVariants.ts +++ b/src/products/components/ProductVariantCreatorPage/createVariants.ts @@ -1,9 +1,11 @@ import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes"; import { - AllOrAttribute, Attribute, - ProductVariantCreateFormData + ChannelPrice, + Price, + ProductVariantCreateFormData, + Stock } from "./form"; interface CreateVariantAttributeValueInput { @@ -12,31 +14,66 @@ interface CreateVariantAttributeValueInput { } type CreateVariantInput = CreateVariantAttributeValueInput[]; -function getAttributeValuePriceOrStock( +function findAttribute( attributes: CreateVariantInput, - priceOrStock: AllOrAttribute -): T { - const attribute = attributes.find( - attribute => attribute.attributeId === priceOrStock.attribute + stockOrPrice: Stock | Price +) { + return attributes.find( + attribute => attribute.attributeId === stockOrPrice.attribute ); +} - const attributeValue = priceOrStock.values.find( +function getAttributeValueStock( + attributes: CreateVariantInput, + stock: Stock +): number[] { + const attribute = findAttribute(attributes, stock); + + const attributeValue = stock.values.find( attributeValue => attribute.attributeValueSlug === attributeValue.slug ); return attributeValue.value; } -function getValueFromMode( +function getAttributeValuePrice( attributes: CreateVariantInput, - priceOrStock: AllOrAttribute, - skipValue: T -): T { - switch (priceOrStock.mode) { + price: Price +): ChannelPrice[] { + const attribute = findAttribute(attributes, price); + + const attributeValue = price.values.find( + attributeValue => attribute.attributeValueSlug === attributeValue.slug + ); + + return attributeValue.value; +} + +function getStockFromMode( + attributes: CreateVariantInput, + stock: Stock, + skipValue: number[] +): number[] { + switch (stock.mode) { case "all": - return priceOrStock.value; + return stock.value; case "attribute": - return getAttributeValuePriceOrStock(attributes, priceOrStock); + return getAttributeValueStock(attributes, stock); + case "skip": + return skipValue; + } +} + +function getPriceFromMode( + attributes: CreateVariantInput, + price: Price, + skipValue: ChannelPrice[] +): ChannelPrice[] { + switch (price.mode) { + case "all": + return price.channels; + case "attribute": + return getAttributeValuePrice(attributes, price); case "skip": return skipValue; } @@ -46,8 +83,12 @@ function createVariant( data: ProductVariantCreateFormData, attributes: CreateVariantInput ): ProductVariantBulkCreateInput { - const price = getValueFromMode(attributes, data.price, "0"); - const stocks = getValueFromMode( + const price = getPriceFromMode( + attributes, + data.price, + data.price.channels.map(channel => ({ ...channel, price: "" })) + ); + const stocks = getStockFromMode( attributes, data.stock, data.warehouses.map(() => 0) @@ -58,7 +99,7 @@ function createVariant( id: attribute.attributeId, values: [attribute.attributeValueSlug] })), - price, + channelListings: price, sku: "", stocks: stocks.map((quantity, stockIndex) => ({ quantity, diff --git a/src/products/components/ProductVariantCreatorPage/fixtures.ts b/src/products/components/ProductVariantCreatorPage/fixtures.ts index c0878f527..8dcde4aa9 100644 --- a/src/products/components/ProductVariantCreatorPage/fixtures.ts +++ b/src/products/components/ProductVariantCreatorPage/fixtures.ts @@ -1,12 +1,20 @@ +import { ChannelPriceData } from "@saleor/channels/utils"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { createVariants } from "./createVariants"; import { - AllOrAttribute, createInitialForm, - ProductVariantCreateFormData + Price, + ProductVariantCreateFormData, + Stock } from "./form"; +export const channels: ChannelPriceData[] = [ + { currency: "USD", id: "channel-1", name: "Channel1", price: "1" }, + { currency: "USD", id: "channel-2", name: "Channel2", price: "2" }, + { currency: "USD", id: "channel-3", name: "Channel3", price: "3" } +]; + export const attributes = [ { id: "attr-1", @@ -58,7 +66,7 @@ export const warehouses: WarehouseFragment[] = [ ]; export const secondStep: ProductVariantCreateFormData = { - ...createInitialForm([], "10.99", warehouses), + ...createInitialForm([], channels, warehouses), attributes: [ { id: attributes[0].id, @@ -98,22 +106,34 @@ export const thirdStep: ProductVariantCreateFormData = { warehouses: warehouses.map(warehouse => warehouse.id) }; -const price: AllOrAttribute = { +const price: Price = { attribute: thirdStep.attributes[1].id, + channels: [ + { channelId: channels[0].id, price: "0" }, + { channelId: channels[1].id, price: "2" }, + { channelId: channels[2].id, price: "2" } + ], mode: "attribute", - value: "", values: [ { slug: thirdStep.attributes[1].values[0], - value: "24.99" + value: [ + { channelId: channels[0].id, price: "0" }, + { channelId: channels[1].id, price: "2" }, + { channelId: channels[2].id, price: "2" } + ] }, { slug: thirdStep.attributes[1].values[1], - value: "26.99" + value: [ + { channelId: channels[0].id, price: "0" }, + { channelId: channels[1].id, price: "2" }, + { channelId: channels[2].id, price: "2" } + ] } ] }; -const stock: AllOrAttribute = { +const stock: Stock = { attribute: thirdStep.attributes[2].id, mode: "attribute", value: [], diff --git a/src/products/components/ProductVariantCreatorPage/form.ts b/src/products/components/ProductVariantCreatorPage/form.ts index 1622dd86e..2e3fbf8a5 100644 --- a/src/products/components/ProductVariantCreatorPage/form.ts +++ b/src/products/components/ProductVariantCreatorPage/form.ts @@ -1,18 +1,30 @@ +import { ChannelPriceData } from "@saleor/channels/utils"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; import { ProductVariantBulkCreateInput } from "../../../types/globalTypes"; +export interface ChannelPrice { + channelId: string; + price: string; +} + export interface AttributeValue { slug: string; value: T; } export type VariantCreatorPricesAndSkuMode = "all" | "attribute" | "skip"; -export interface AllOrAttribute { +export interface Price { mode: VariantCreatorPricesAndSkuMode; attribute: string; - value: T; - values: Array>; + channels: ChannelPrice[]; + values: Array>; +} +export interface Stock { + mode: VariantCreatorPricesAndSkuMode; + attribute: string; + value: number[]; + values: Array>; } export interface Attribute { id: string; @@ -20,33 +32,40 @@ export interface Attribute { } export interface ProductVariantCreateFormData { attributes: Attribute[]; - price: AllOrAttribute; - stock: AllOrAttribute; + price: Price; + stock: Stock; variants: ProductVariantBulkCreateInput[]; warehouses: string[]; } export const createInitialForm = ( attributes: ProductDetails_product_productType_variantAttributes[], - price: string, + channels: ChannelPriceData[], warehouses: WarehouseFragment[] -): ProductVariantCreateFormData => ({ - attributes: attributes.map(attribute => ({ - id: attribute.id, - values: [] - })), - price: { - attribute: undefined, - mode: "all", - value: price || "", - values: [] - }, - stock: { - attribute: undefined, - mode: "all", - value: warehouses.length === 1 ? [0] : [], - values: [] - }, - variants: [], - warehouses: warehouses.length === 1 ? [warehouses[0].id] : [] -}); +): ProductVariantCreateFormData => { + const channelListings = + channels?.map(channel => ({ + channelId: channel.id, + price: channel.price?.toString() || "" + })) || []; + return { + attributes: attributes.map(attribute => ({ + id: attribute.id, + values: [] + })), + price: { + attribute: undefined, + channels: channelListings, + mode: "all", + values: [] + }, + stock: { + attribute: undefined, + mode: "all", + value: warehouses.length === 1 ? [0] : [], + values: [] + }, + variants: [], + warehouses: warehouses.length === 1 ? [warehouses[0].id] : [] + }; +}; diff --git a/src/products/components/ProductVariantCreatorPage/reducer.test.ts b/src/products/components/ProductVariantCreatorPage/reducer.test.ts index ce6f8debf..42f18d87a 100644 --- a/src/products/components/ProductVariantCreatorPage/reducer.test.ts +++ b/src/products/components/ProductVariantCreatorPage/reducer.test.ts @@ -1,14 +1,13 @@ import { attributes, + channels, fourthStep, secondStep, thirdStep, warehouses } from "./fixtures"; -import reducer, { - ProductVariantCreateReducerActionType, - VariantField -} from "./reducer"; +import { ChannelPrice } from "./form"; +import reducer, { ProductVariantCreateReducerActionType } from "./reducer"; function execActions( initialState: TState, @@ -73,7 +72,7 @@ describe("Reducer is able to", () => { }); it("select price for all variants", () => { - const price = "45.99"; + const price = "22.99"; const state = execActions(thirdStep, reducer, [ { applyPriceOrStockToAll: { @@ -83,6 +82,7 @@ describe("Reducer is able to", () => { }, { changeApplyPriceToAllValue: { + channelId: channels[0].id, price }, type: ProductVariantCreateReducerActionType.changeApplyPriceToAllValue @@ -91,9 +91,8 @@ describe("Reducer is able to", () => { type: ProductVariantCreateReducerActionType.reload } ]); - expect(state.price.mode).toBe("all"); - expect(state.price.value).toBe(price); + expect(state.price.channels[0].price).toBe(price); expect(state).toMatchSnapshot(); }); @@ -163,6 +162,7 @@ describe("Reducer is able to", () => { }, { changeAttributeValuePrice: { + channelId: channels[0].id, price: value.toString(), valueId: attribute.values[0] }, @@ -170,6 +170,7 @@ describe("Reducer is able to", () => { }, { changeAttributeValuePrice: { + channelId: channels[1].id, price: (value + 6).toString(), valueId: attribute.values[1] }, @@ -235,24 +236,24 @@ describe("Reducer is able to", () => { }); it("modify individual variant price", () => { - const field: VariantField = "price"; - const value = "49.99"; + const value: ChannelPrice = { channelId: channels[0].id, price: "7" }; const variantIndex = 3; const state = execActions(fourthStep, reducer, [ { - changeVariantData: { - field, + changeVariantPriceData: { value, variantIndex }, - type: ProductVariantCreateReducerActionType.changeVariantData + type: ProductVariantCreateReducerActionType.changeVariantPriceData } ]); - expect(state.variants[variantIndex].price).toBe(value); - expect(state.variants[variantIndex - 1].price).toBe( - fourthStep.variants[variantIndex - 1].price + expect(state.variants[variantIndex].channelListings[0].price).toBe( + value.price + ); + expect(state.variants[variantIndex - 1].channelListings).toBe( + fourthStep.variants[variantIndex - 1].channelListings ); expect(state).toMatchSnapshot(); }); diff --git a/src/products/components/ProductVariantCreatorPage/reducer.ts b/src/products/components/ProductVariantCreatorPage/reducer.ts index 5f702218c..bc312763f 100644 --- a/src/products/components/ProductVariantCreatorPage/reducer.ts +++ b/src/products/components/ProductVariantCreatorPage/reducer.ts @@ -25,36 +25,40 @@ export enum ProductVariantCreateReducerActionType { changeApplyStockToAttributeId, changeAttributeValuePrice, changeAttributeValueStock, - changeVariantData, + changeVariantSku, + changeVariantPriceData, changeVariantStockData, changeWarehouses, deleteVariant, reload, selectValue } -export type VariantField = "price" | "sku"; export interface ProductVariantCreateReducerAction { applyPriceOrStockToAll?: { mode: VariantCreatorPricesAndSkuMode; }; changeApplyPriceToAllValue?: { + channelId: string; price: string; }; changeApplyPriceOrStockToAttributeId?: { attributeId: string; }; changeApplyStockToAllValue?: Record<"quantity" | "warehouseIndex", number>; - changeAttributeValuePrice?: Record<"valueId" | "price", string>; + changeAttributeValuePrice?: Record<"valueId" | "price" | "channelId", string>; changeAttributeValueStock?: { valueId: string; quantity: number; warehouseIndex: number; }; - changeVariantData?: { - field: VariantField; + changeVariantSku?: { value: string; variantIndex: number; }; + changeVariantPriceData?: { + value: { channelId: string; price: string }; + variantIndex: number; + }; changeVariantStockData?: { stock: StockInput; variantIndex: number; @@ -94,7 +98,7 @@ function selectValue( ? toggle( { slug: valueSlug, - value: "" + value: [] }, prevState.price.values, (a, b) => a.slug === b.slug @@ -156,7 +160,8 @@ function applyStockToAll( function changeAttributeValuePrice( state: ProductVariantCreateFormData, attributeValueSlug: string, - price: string + price: string, + channelId: string ): ProductVariantCreateFormData { const index = state.price.values.findIndex( value => value.slug === attributeValueSlug @@ -166,10 +171,15 @@ function changeAttributeValuePrice( throw new Error(`Value with id ${attributeValueSlug} not found`); } + const channels = state.price.values[index].value; + const channelIndex = channels.findIndex( + channel => channel.channelId === channelId + ); + const values = updateAtIndex( { slug: attributeValueSlug, - value: price + value: updateAtIndex({ channelId, price }, channels, channelIndex) }, state.price.values, index @@ -229,7 +239,7 @@ function changeApplyPriceToAttributeId( ); const values = attribute.values.map(slug => ({ slug, - value: "" + value: [] })); return { @@ -266,13 +276,19 @@ function changeApplyStockToAttributeId( function changeApplyPriceToAllValue( state: ProductVariantCreateFormData, - value: string + channelId: string, + price: string ): ProductVariantCreateFormData { + const prevChannels = [...state.price.channels]; + const channelIndex = prevChannels?.findIndex( + channel => channelId === channel.channelId + ); + prevChannels[channelIndex] = { channelId, price }; return { ...state, price: { ...state.price, - value + channels: prevChannels } }; } @@ -291,20 +307,15 @@ function changeApplyStockToAllValue( }; } -function changeVariantData( +function changeVariantSku( state: ProductVariantCreateFormData, - field: VariantField, value: string, variantIndex: number ): ProductVariantCreateFormData { const variant = { ...state.variants[variantIndex] }; - if (field === "price") { - variant.price = value; - } else if (field === "sku") { - variant.sku = value; - } + variant.sku = value; return { ...state, @@ -332,6 +343,32 @@ function changeVariantStockData( }; } +function changeVariantPriceData( + state: ProductVariantCreateFormData, + value: { channelId: string; price: string }, + variantIndex: number +): ProductVariantCreateFormData { + const { channelId, price } = value; + const variant = { + ...state.variants[variantIndex] + }; + const channelIndex = variant.channelListings.findIndex( + listing => listing.channelId === channelId + ); + const updatedVariant = { + ...variant, + channelListings: updateAtIndex( + { channelId, price }, + [...variant.channelListings], + channelIndex + ) + }; + return { + ...state, + variants: updateAtIndex(updatedVariant, [...state.variants], variantIndex) + }; +} + function changeWarehouses( state: ProductVariantCreateFormData, warehouseId: string @@ -408,7 +445,8 @@ function reduceProductVariantCreateFormData( return changeAttributeValuePrice( prevState, action.changeAttributeValuePrice.valueId, - action.changeAttributeValuePrice.price + action.changeAttributeValuePrice.price, + action.changeAttributeValuePrice.channelId ); case ProductVariantCreateReducerActionType.changeAttributeValueStock: return changeAttributeValueStock( @@ -430,6 +468,7 @@ function reduceProductVariantCreateFormData( case ProductVariantCreateReducerActionType.changeApplyPriceToAllValue: return changeApplyPriceToAllValue( prevState, + action.changeApplyPriceToAllValue.channelId, action.changeApplyPriceToAllValue.price ); case ProductVariantCreateReducerActionType.changeApplyStockToAllValue: @@ -438,12 +477,17 @@ function reduceProductVariantCreateFormData( action.changeApplyStockToAllValue.warehouseIndex, action.changeApplyStockToAllValue.quantity ); - case ProductVariantCreateReducerActionType.changeVariantData: - return changeVariantData( + case ProductVariantCreateReducerActionType.changeVariantSku: + return changeVariantSku( prevState, - action.changeVariantData.field, - action.changeVariantData.value, - action.changeVariantData.variantIndex + action.changeVariantSku.value, + action.changeVariantSku.variantIndex + ); + case ProductVariantCreateReducerActionType.changeVariantPriceData: + return changeVariantPriceData( + prevState, + action.changeVariantPriceData.value, + action.changeVariantPriceData.variantIndex ); case ProductVariantCreateReducerActionType.changeVariantStockData: return changeVariantStockData( diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 09b9d4497..8fc69efd9 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -1,48 +1,32 @@ +import { ChannelPriceData } from "@saleor/channels/utils"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Grid from "@saleor/components/Grid"; -import { MetadataFormData } from "@saleor/components/Metadata"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; -import { FormsetData } from "@saleor/hooks/useFormset"; import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate"; import { ReorderAction } from "@saleor/types"; import React from "react"; import { maybe } from "../../../misc"; import ProductShipping from "../ProductShipping/ProductShipping"; -import ProductStocks, { ProductStockInput } from "../ProductStocks"; -import ProductVariantAttributes, { - VariantAttributeInputData -} from "../ProductVariantAttributes"; +import ProductStocks from "../ProductStocks"; +import ProductVariantAttributes from "../ProductVariantAttributes"; import ProductVariantImages from "../ProductVariantImages"; import ProductVariantImageSelectDialog from "../ProductVariantImageSelectDialog"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; import ProductVariantSetDefault from "../ProductVariantSetDefault"; -import ProductVariantUpdateForm from "./form"; - -export interface ProductVariantPageFormData extends MetadataFormData { - costPrice: string; - price: string; - sku: string; - trackInventory: boolean; - weight: string; -} - -export interface ProductVariantPageSubmitData - extends ProductVariantPageFormData { - attributes: FormsetData; - addStocks: ProductStockInput[]; - updateStocks: ProductStockInput[]; - removeStocks: string[]; -} +import ProductVariantUpdateForm, { + ProductVariantUpdateSubmitData +} from "./form"; interface ProductVariantPageProps { defaultVariantId?: string; @@ -51,6 +35,8 @@ interface ProductVariantPageProps { | ProductErrorWithAttributesFragment[] | VariantUpdate_productVariantUpdate_errors[]; header: string; + channels: ChannelPriceData[]; + channelErrors: ProductChannelListingErrorFragment[]; loading?: boolean; placeholderImage?: string; saveButtonBarState: ConfirmButtonTransitionState; @@ -60,7 +46,7 @@ interface ProductVariantPageProps { onAdd(); onBack(); onDelete(); - onSubmit(data: ProductVariantPageSubmitData); + onSubmit(data: ProductVariantUpdateSubmitData); onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); @@ -68,6 +54,8 @@ interface ProductVariantPageProps { } const ProductVariantPage: React.FC = ({ + channels, + channelErrors, defaultVariantId, defaultWeightUnit, errors, @@ -101,9 +89,7 @@ const ProductVariantPage: React.FC = ({ return ( <> - - {maybe(() => variant.product.name)} - + {variant?.product?.name} {variant?.product?.defaultVariant?.id !== variant?.id && ( = ({ variant={variant} onSubmit={onSubmit} warehouses={warehouses} + currentChannels={channels} > - {({ change, data, handlers, hasChanged, submit }) => ( + {({ + change, + data, + disabled: formDisabled, + handlers, + hasChanged, + submit + }) => ( <>
@@ -152,17 +146,15 @@ const ProductVariantPage: React.FC = ({ /> ({ + ...channel.data, + ...channel.value + }) + )} + errors={channelErrors} loading={loading} - onChange={change} + onChange={handlers.changeChannels} /> = ({
; + channelListings: FormsetData; stocks: ProductStockInput[]; } export interface ProductVariantUpdateSubmitData extends ProductVariantUpdateFormData { attributes: FormsetData; addStocks: ProductStockInput[]; + channelListings: FormsetData; updateStocks: ProductStockInput[]; removeStocks: string[]; } export interface UseProductVariantUpdateFormOpts { warehouses: SearchWarehouses_search_edges_node[]; + currentChannels: ChannelPriceData[]; } export interface UseProductVariantUpdateFormResult { change: FormChange; data: ProductVariantUpdateData; - handlers: Record<"changeStock" | "selectAttribute", FormsetChange> & + disabled: boolean; + handlers: Record< + "changeStock" | "selectAttribute" | "changeChannels", + FormsetChange + > & Record<"addStock" | "deleteStock", (id: string) => void> & { changeMetadata: FormChange; }; @@ -71,11 +82,10 @@ function useProductVariantUpdateForm( const attributeInput = getAttributeInputFromVariant(variant); const stockInput = getStockInputFromVariant(variant); + const channelsInput = getChannelsInput(opts.currentChannels); const initial: ProductVariantUpdateFormData = { - costPrice: variant?.costPrice?.amount.toString() || "", metadata: variant?.metadata?.map(mapMetadataItemToInput), - price: variant?.price?.amount.toString() || "", privateMetadata: variant?.privateMetadata?.map(mapMetadataItemToInput), sku: variant?.sku || "", trackInventory: variant?.trackInventory, @@ -85,6 +95,7 @@ function useProductVariantUpdateForm( const form = useForm(initial); const attributes = useFormset(attributeInput); const stocks = useFormset(stockInput); + const channels = useFormset(channelsInput); const { isMetadataModified, isPrivateMetadataModified, @@ -117,6 +128,10 @@ function useProductVariantUpdateForm( triggerChange(); stocks.remove(id); }; + const handleChannelChange: FormsetChange = (id, value) => { + channels.change(id, value); + triggerChange(); + }; const dataStocks = stocks.data.map(stock => stock.id); const variantStocks = variant?.stocks.map(stock => stock.warehouse.id) || []; @@ -129,9 +144,15 @@ function useProductVariantUpdateForm( stock => !stockDiff.added.some(addedStock => addedStock === stock.id) ); + const disabled = channels?.data.some( + channelData => + validatePrice(channelData.value.price) || + validateCostPrice(channelData.value.costPrice) + ); const data: ProductVariantUpdateData = { ...form.data, attributes: attributes.data, + channelListings: channels.data, stocks: stocks.data }; const submitData: ProductVariantUpdateSubmitData = { @@ -139,6 +160,7 @@ function useProductVariantUpdateForm( ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified), addStocks, attributes: attributes.data, + channelListings: channels.data, removeStocks: stockDiff.removed, updateStocks }; @@ -148,8 +170,10 @@ function useProductVariantUpdateForm( return { change: handleChange, data, + disabled, handlers: { addStock: handleStockAdd, + changeChannels: handleChannelChange, changeMetadata, changeStock: handleStockChange, deleteStock: handleStockDelete, diff --git a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx index ff812d36e..2efbb9d60 100644 --- a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx +++ b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx @@ -1,42 +1,85 @@ +import { + TableBody, + TableCell, + TableHead, + TableRow, + Typography +} from "@material-ui/core"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import { makeStyles } from "@material-ui/core/styles"; +import { ChannelPriceArgs, ChannelPriceData } from "@saleor/channels/utils"; import CardTitle from "@saleor/components/CardTitle"; import PriceField from "@saleor/components/PriceField"; -import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; -import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; -import createNonNegativeValueChangeHandler from "@saleor/utils/handlers/nonNegativeValueChangeHandler"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; +import { renderCollection } from "@saleor/misc"; +import { + getFormChannelError, + getFormChannelErrors +} from "@saleor/utils/errors"; +import getProductErrorMessage from "@saleor/utils/errors/product"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; const useStyles = makeStyles( theme => ({ - grid: { - display: "grid", - gridColumnGap: theme.spacing(2), - gridTemplateColumns: "1fr 1fr" + caption: { + fontSize: 14, + padding: theme.spacing(0, 3, 2, 3) + }, + colName: { + fontSize: 14, + paddingLeft: 0, + width: "auto" + }, + colPrice: { + textAlign: "right", + verticalAlign: "top", + width: 200 + }, + colType: { + fontSize: 14, + textAlign: "right", + width: 200 + }, + input: { + textAlign: "left" + }, + pricingContent: { + "&:last-child": { + paddingBottom: 0 + }, + paddingLeft: 0, + paddingRight: 0 + }, + table: { + tableLayout: "fixed" } }), { name: "ProductVariantPrice" } ); interface ProductVariantPriceProps { - currencySymbol?: string; - data: Record<"price" | "costPrice", string>; - errors: ProductErrorFragment[]; + ProductVariantChannelListings: ChannelPriceData[]; + errors: ProductChannelListingErrorFragment[]; loading?: boolean; - onChange(event: any); + onChange: (id: string, data: ChannelPriceArgs) => void; } -const ProductVariantPrice: React.FC = props => { - const { currencySymbol, data, errors, loading, onChange } = props; +const numberOfColumns = 2; +const ProductVariantPrice: React.FC = props => { + const { + errors = [], + ProductVariantChannelListings, + loading, + onChange + } = props; const classes = useStyles(props); const intl = useIntl(); - - const formErrors = getFormErrors(["price", "costPrice"], errors); - - const handlePriceChange = createNonNegativeValueChangeHandler(onChange); + const formErrors = getFormChannelErrors(["price", "costPrice"], errors); return ( @@ -46,54 +89,123 @@ const ProductVariantPrice: React.FC = props => { description: "product pricing, section header" })} /> - -
-
- -
-
- -
-
+ + + {intl.formatMessage({ + defaultMessage: + "Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency", + description: "info text" + })} + + + + + + + + + + + + + + + + + {renderCollection( + ProductVariantChannelListings, + (listing, index) => { + const priceError = getFormChannelError( + formErrors.price, + listing.id + ); + const costPriceError = getFormChannelError( + formErrors.costPrice, + listing.id + ); + + return ( + + {listing?.name || } + + {listing ? ( + + onChange(listing.id, { + costPrice: listing.costPrice, + price: e.target.value + }) + } + disabled={loading} + required + hint={ + priceError && + getProductErrorMessage(priceError, intl) + } + /> + ) : ( + + )} + + + {listing ? ( + + onChange(listing.id, { + costPrice: e.target.value, + price: listing.price + }) + } + disabled={loading} + hint={ + costPriceError + ? getProductErrorMessage(costPriceError, intl) + : "" + } + /> + ) : ( + + )} + + + ); + }, + () => ( + + + + + + ) + )} + +
); diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index cb533e8a1..b104ae2ec 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -7,6 +7,7 @@ import { fade } from "@material-ui/core/styles/colorManipulator"; import TableCell from "@material-ui/core/TableCell"; import Typography from "@material-ui/core/Typography"; import CardTitle from "@saleor/components/CardTitle"; +import { ChannelsSelect } from "@saleor/components/ChannelsSelect"; import Checkbox from "@saleor/components/Checkbox"; import LinkChoice from "@saleor/components/LinkChoice"; import Money from "@saleor/components/Money"; @@ -18,7 +19,7 @@ import { SortableTableRow } from "@saleor/components/SortableTable"; import TableHead from "@saleor/components/TableHead"; -import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; import React from "react"; import { FormattedMessage, IntlShape, useIntl } from "react-intl"; @@ -83,6 +84,9 @@ const useStyles = makeStyles( width: 200 } }, + channelSelect: { + marginRight: theme.spacing(1) + }, colGrab: { width: 60 }, @@ -183,8 +187,8 @@ interface ProductVariantsProps extends ListActions { disabled: boolean; product: ProductDetails_product; variants: ProductDetails_product_variants[]; - fallbackPrice?: ProductVariant_costPrice; onVariantReorder: ReorderAction; + channelChoices: SingleAutocompleteChoiceType[]; onRowClick: (id: string) => () => void; onSetDefaultVariant(variant: ProductDetails_product_variants); onVariantAdd?(); @@ -195,10 +199,10 @@ const numberOfColumns = 7; export const ProductVariants: React.FC = props => { const { + channelChoices, disabled, variants, product, - fallbackPrice, onRowClick, onVariantAdd, onVariantsAdd, @@ -214,6 +218,9 @@ export const ProductVariants: React.FC = props => { const intl = useIntl(); const [warehouse, setWarehouse] = React.useState(null); + const [channelChoice, setChannelChoice] = useStateFromProps( + channelChoices[0]?.value + ); const hasVariants = maybe(() => variants.length > 0, true); return ( @@ -254,6 +261,11 @@ export const ProductVariants: React.FC = props => { {variants.length > 0 ? ( + = props => { 0 ) : null; + const channel = variant.channelListings.find( + listing => listing.channel.id === channelChoice + ); return ( = props => { {variant ? ( - variant.price ? ( - - ) : fallbackPrice ? ( - - ) : ( - - ) + ) : ( )} diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 734843360..39580d37a 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -109,6 +109,46 @@ export const product: ( id: "Q2F0ZWdvcnk6MQ==", name: "Apparel" }, + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], chargeTaxes: true, collections: [ { @@ -160,7 +200,6 @@ export const product: ( isAvailable: false, isAvailableForPurchase: false, isFeatured: false, - isPublished: true, margin: { __typename: "Margin", start: 2, stop: 7 }, metadata: [ { @@ -170,38 +209,6 @@ export const product: ( } ], name: "Ergonomic Plastic Bacon", - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 12.3, - currency: "USD" - }, - net: { - __typename: "Money", - amount: 10, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 24.6, - currency: "USD" - }, - net: { - __typename: "Money", - amount: 20, - currency: "USD" - } - } - } - }, privateMetadata: [], productType: { __typename: "ProductType", @@ -270,6 +277,7 @@ export const product: ( variants: [ { __typename: "ProductVariant", + channelListings: [], id: "pv75934", images: [ { @@ -285,11 +293,6 @@ export const product: ( ], margin: 2, name: "Cordoba Oro", - price: { - __typename: "Money", - amount: 678.78, - currency: "USD" - }, sku: "87192-94370", stocks: [ { @@ -316,6 +319,46 @@ export const product: ( }, { __typename: "ProductVariant", + channelListings: [ + { + __typename: "ProductVariantChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + costPrice: { + __typename: "Money", + amount: 10, + currency: "USD" + }, + price: { + __typename: "Money", + amount: 1, + currency: "USD" + } + }, + { + __typename: "ProductVariantChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + costPrice: { + __typename: "Money", + amount: 10, + currency: "USD" + }, + price: { + __typename: "Money", + amount: 1, + currency: "USD" + } + } + ], id: "pv68615", images: [ { @@ -331,7 +374,6 @@ export const product: ( ], margin: 7, name: "silver", - price: null, sku: "69055-15190", stocks: [ { @@ -363,32 +405,48 @@ export const products = ( { __typename: "Product", attributes: [], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDo2MQ==", - isAvailable: true, - isPublished: true, name: "Nebula Night Sky Paint", - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -403,32 +461,48 @@ export const products = ( { __typename: "Product", attributes: [], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDo2NA==", - isAvailable: true, - isPublished: false, name: "Light Speed Yellow Paint", - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -443,33 +517,48 @@ export const products = ( { __typename: "Product", attributes: [], - id: "UHJvZHVjdDo2NQ==", - isAvailable: true, - isPublished: false, - name: "Hyperspace Turquoise Paint", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo2NQ==", + name: "Hyperspace Turquoise Paint", productType: { __typename: "ProductType", hasVariants: true, @@ -499,33 +588,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3NQ==", - isAvailable: true, - isPublished: true, - name: "Pineapple Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3NQ==", + name: "Pineapple Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -555,33 +659,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3Ng==", - isAvailable: true, - isPublished: true, - name: "Coconut Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3Ng==", + name: "Coconut Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -611,33 +730,49 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3Mg==", - isAvailable: true, - isPublished: true, - name: "Apple Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3Mg==", + name: "Apple Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -667,33 +802,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3MQ==", - isAvailable: true, - isPublished: true, - name: "Orange Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3MQ==", + name: "Orange Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -723,33 +873,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3NA==", - isAvailable: true, - isPublished: true, - name: "Banana Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3NA==", + name: "Banana Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -779,33 +944,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3OQ==", - isAvailable: true, - isPublished: false, - name: "Bean Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true } - }, + ], + id: "UHJvZHVjdDo3OQ==", + name: "Bean Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -835,33 +1015,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3Mw==", - isAvailable: true, - isPublished: true, - name: "Carrot Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false } - }, + ], + id: "UHJvZHVjdDo3Mw==", + name: "Carrot Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -891,33 +1086,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo3OA==", - isAvailable: true, - isPublished: true, - name: "Green Juice", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true } - }, + ], + id: "UHJvZHVjdDo3OA==", + name: "Green Juice", productType: { __typename: "ProductType", hasVariants: true, @@ -947,33 +1157,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo4OQ==", - isAvailable: true, - isPublished: true, - name: "Code Division T-shirt", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true } - }, + ], + id: "UHJvZHVjdDo4OQ==", + name: "Code Division T-shirt", productType: { __typename: "ProductType", hasVariants: true, @@ -1003,33 +1228,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDoxMDc=", - isAvailable: true, - isPublished: true, name: "Polo Shirt", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1059,33 +1299,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDoxMDg=", - isAvailable: true, - isPublished: true, name: "Polo Shirt", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1115,33 +1370,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDoxMDk=", - isAvailable: true, - isPublished: true, name: "Polo Shirt", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1171,33 +1441,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDoxMTA=", - isAvailable: true, - isPublished: true, name: "Polo Shirt", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1227,33 +1512,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: false + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: false + } + ], id: "UHJvZHVjdDoxMTU=", - isAvailable: true, - isPublished: false, name: "Black Hoodie", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1283,33 +1583,48 @@ export const products = ( ] } ], + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 3.99, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true + } + ], id: "UHJvZHVjdDoxMTY=", - isAvailable: true, - isPublished: true, name: "Blue Hoodie", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } - }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } - } - }, productType: { __typename: "ProductType", hasVariants: true, @@ -1339,33 +1654,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDoxMTc=", - isAvailable: true, - isPublished: true, - name: "Mustard Hoodie", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true } - }, + ], + id: "UHJvZHVjdDoxMTc=", + name: "Mustard Hoodie", productType: { __typename: "ProductType", hasVariants: true, @@ -1395,33 +1725,48 @@ export const products = ( ] } ], - id: "UHJvZHVjdDo4NQ==", - isAvailable: true, - isPublished: false, - name: "Colored Parrot Cushion", - - pricing: { - __typename: "ProductPricingInfo", - priceRangeUndiscounted: { - __typename: "TaxedMoneyRange", - start: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 3, - currency: "USD" - } + channelListings: [ + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "123", + name: "Channel1" }, - stop: { - __typename: "TaxedMoney", - gross: { - __typename: "Money", - amount: 8, - currency: "USD" - } - } + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: true, + publicationDate: "2020-07-14", + visibleInListings: true + }, + { + __typename: "ProductChannelListing", + availableForPurchase: null, + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "124", + name: "Channel2" + }, + discountedPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + isAvailableForPurchase: false, + isPublished: false, + publicationDate: "2020-07-30", + visibleInListings: true } - }, + ], + id: "UHJvZHVjdDo4NQ==", + name: "Colored Parrot Cushion", productType: { __typename: "ProductType", hasVariants: true, @@ -1515,11 +1860,46 @@ export const variant = (placeholderImage: string): ProductVariant => ({ ] } ], - costPrice: { - __typename: "Money", - amount: 12, - currency: "USD" - }, + channelListings: [ + { + __typename: "ProductVariantChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "test1", + name: "Test channel" + }, + costPrice: { + __typename: "Money", + amount: 10, + currency: "USD" + }, + price: { + __typename: "Money", + amount: 10, + currency: "USD" + } + }, + { + __typename: "ProductVariantChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "test2", + name: "Test channel other" + }, + costPrice: { + __typename: "Money", + amount: 10, + currency: "USD" + }, + price: { + __typename: "Money", + amount: 20, + currency: "USD" + } + } + ], id: "var1", images: [ { @@ -1551,14 +1931,39 @@ export const variant = (placeholderImage: string): ProductVariant => ({ } ], name: "Extended Hard", - price: { - __typename: "Money", - amount: 100, - currency: "USD" - }, privateMetadata: [], product: { __typename: "Product" as "Product", + channelListings: [ + { + __typename: "ProductChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "test1", + name: "Test channel" + }, + discountedPrice: { + __typename: "Money", + amount: 10, + currency: "USD" + } + }, + { + __typename: "ProductChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "test2", + name: "Test channel other" + }, + discountedPrice: { + __typename: "Money", + amount: 20, + currency: "USD" + } + } + ], defaultVariant: { __typename: "ProductVariant", id: "var1" diff --git a/src/products/mutations.ts b/src/products/mutations.ts index 1b63cf258..f7928dcce 100644 --- a/src/products/mutations.ts +++ b/src/products/mutations.ts @@ -2,6 +2,7 @@ import { bulkProductErrorFragment, bulkStockErrorFragment, exportErrorFragment, + productChannelListingErrorFragment, productErrorFragment, productErrorWithAttributesFragment, stockErrorFragment @@ -19,9 +20,9 @@ import { productBulkDeleteVariables } from "./types/productBulkDelete"; import { - productBulkPublish, - productBulkPublishVariables -} from "./types/productBulkPublish"; + ProductChannelListingUpdate, + ProductChannelListingUpdateVariables +} from "./types/ProductChannelListingUpdate"; import { ProductCreate, ProductCreateVariables } from "./types/ProductCreate"; import { ProductDelete, ProductDeleteVariables } from "./types/ProductDelete"; import { ProductExport, ProductExportVariables } from "./types/ProductExport"; @@ -41,10 +42,6 @@ import { ProductImageUpdate, ProductImageUpdateVariables } from "./types/ProductImageUpdate"; -import { - ProductSetAvailabilityForPurchase, - ProductSetAvailabilityForPurchaseVariables -} from "./types/ProductSetAvailabilityForPurchase"; import { ProductUpdate, ProductUpdateVariables } from "./types/ProductUpdate"; import { ProductVariantBulkCreate, @@ -54,6 +51,10 @@ import { ProductVariantBulkDelete, ProductVariantBulkDeleteVariables } from "./types/ProductVariantBulkDelete"; +import { + ProductVariantChannelListingUpdate, + ProductVariantChannelListingUpdateVariables +} from "./types/ProductVariantChannelListingUpdate"; import { ProductVariantReorder, ProductVariantReorderVariables @@ -295,8 +296,6 @@ export const variantUpdateMutation = gql` $removeStocks: [ID!]! $id: ID! $attributes: [AttributeValueInput] - $costPrice: PositiveDecimal - $price: PositiveDecimal $sku: String $trackInventory: Boolean! $stocks: [StockInput!]! @@ -306,8 +305,6 @@ export const variantUpdateMutation = gql` id: $id input: { attributes: $attributes - costPrice: $costPrice - price: $price sku: $sku trackInventory: $trackInventory weight: $weight @@ -470,21 +467,6 @@ export const useProductBulkDeleteMutation = makeMutation< productBulkDeleteVariables >(productBulkDeleteMutation); -export const productBulkPublishMutation = gql` - ${productErrorFragment} - mutation productBulkPublish($ids: [ID!]!, $isPublished: Boolean!) { - productBulkPublish(ids: $ids, isPublished: $isPublished) { - errors: productErrors { - ...ProductErrorFragment - } - } - } -`; -export const useProductBulkPublishMutation = makeMutation< - productBulkPublish, - productBulkPublishVariables ->(productBulkPublishMutation); - export const ProductVariantBulkCreateMutation = gql` ${bulkProductErrorFragment} mutation ProductVariantBulkCreate( @@ -537,36 +519,24 @@ export const useProductExport = makeMutation< ProductExportVariables >(productExportMutation); -const productSetAvailabilityForPurchase = gql` - ${productErrorFragment} - mutation ProductSetAvailabilityForPurchase( - $isAvailable: Boolean! - $productId: ID! - $startDate: Date +export const ProductChannelListingUpdateMutation = gql` + ${productFragmentDetails} + ${productChannelListingErrorFragment} + mutation ProductChannelListingUpdate( + $id: ID! + $input: ProductChannelListingUpdateInput! ) { - productSetAvailabilityForPurchase( - isAvailable: $isAvailable - productId: $productId - startDate: $startDate - ) { + productChannelListingUpdate(id: $id, input: $input) { product { - id - availableForPurchase - isAvailableForPurchase + ...Product } - errors: productErrors { - ...ProductErrorFragment - message + errors: productChannelListingErrors { + ...ProductChannelListingErrorFragment } } } `; -export const useProductSetAvailabilityForPurchase = makeMutation< - ProductSetAvailabilityForPurchase, - ProductSetAvailabilityForPurchaseVariables ->(productSetAvailabilityForPurchase); - const productVariantReorder = gql` ${productErrorFragment} ${productFragmentDetails} @@ -585,3 +555,30 @@ export const useProductVariantReorderMutation = makeMutation< ProductVariantReorder, ProductVariantReorderVariables >(productVariantReorder); +export const useProductChannelListingUpdate = makeMutation< + ProductChannelListingUpdate, + ProductChannelListingUpdateVariables +>(ProductChannelListingUpdateMutation); + +export const ProductVariantChannelListingUpdateMutation = gql` + ${fragmentVariant} + ${productChannelListingErrorFragment} + mutation ProductVariantChannelListingUpdate( + $id: ID! + $input: [ProductVariantChannelListingAddInput!]! + ) { + productVariantChannelListingUpdate(id: $id, input: $input) { + variant { + ...ProductVariant + } + errors: productChannelListingErrors { + ...ProductChannelListingErrorFragment + } + } + } +`; + +export const useProductVariantChannelListingUpdate = makeMutation< + ProductVariantChannelListingUpdate, + ProductVariantChannelListingUpdateVariables +>(ProductVariantChannelListingUpdateMutation); diff --git a/src/products/queries.ts b/src/products/queries.ts index 68ebb6873..895e002e0 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -1,6 +1,5 @@ import { pageInfoFragment } from "@saleor/fragments/pageInfo"; import { - fragmentMoney, fragmentVariant, productFragment, productFragmentDetails, @@ -94,7 +93,6 @@ export const useInitialProductFilterDataQuery = makeQuery< >(initialProductFilterDataQuery); const productListQuery = gql` - ${fragmentMoney} ${productFragment} query ProductList( $first: Int @@ -124,20 +122,6 @@ const productListQuery = gql` name } } - pricing { - priceRangeUndiscounted { - start { - gross { - ...Money - } - } - stop { - gross { - ...Money - } - } - } - } } } pageInfo { @@ -168,8 +152,8 @@ export const useCountAllProducts = makeQuery( const productDetailsQuery = gql` ${productFragmentDetails} ${taxTypeFragment} - query ProductDetails($id: ID!) { - product(id: $id) { + query ProductDetails($id: ID!, $channel: String) { + product(id: $id, channel: $channel) { ...Product } taxTypes { @@ -182,6 +166,11 @@ export const useProductDetails = makeQuery< ProductDetailsVariables >(productDetailsQuery); +export const useProductDetailsQuery = makeQuery< + ProductDetails, + ProductDetailsVariables +>(productDetailsQuery); + const productVariantQuery = gql` ${fragmentVariant} query ProductVariantDetails($id: ID!) { @@ -204,6 +193,13 @@ const productVariantCreateQuery = gql` sortOrder url } + channelListings { + channel { + id + name + currencyCode + } + } name productType { id diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index ea2d1909f..1d3f7a8b5 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -58,37 +58,23 @@ export interface CreateMultipleVariantsData_product_productType { variantAttributes: (CreateMultipleVariantsData_product_productType_variantAttributes | null)[] | null; } -export interface CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_start_gross { +export interface CreateMultipleVariantsData_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CreateMultipleVariantsData_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_start | null; - stop: CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface CreateMultipleVariantsData_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: CreateMultipleVariantsData_product_pricing_priceRangeUndiscounted | null; +export interface CreateMultipleVariantsData_product_channelListings { + __typename: "ProductChannelListing"; + channel: CreateMultipleVariantsData_product_channelListings_channel; + discountedPrice: CreateMultipleVariantsData_product_channelListings_discountedPrice | null; } export interface CreateMultipleVariantsData_product { @@ -96,7 +82,7 @@ export interface CreateMultipleVariantsData_product { id: string; attributes: CreateMultipleVariantsData_product_attributes[]; productType: CreateMultipleVariantsData_product_productType; - pricing: CreateMultipleVariantsData_product_pricing | null; + channelListings: CreateMultipleVariantsData_product_channelListings[] | null; } export interface CreateMultipleVariantsData_warehouses_edges_node { diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts new file mode 100644 index 000000000..1cb1a786d --- /dev/null +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -0,0 +1,239 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ProductChannelListingUpdate +// ==================================================== + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute; + values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_values | null)[]; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductChannelListingUpdate_productChannelListingUpdate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; + taxType: ProductChannelListingUpdate_productChannelListingUpdate_product_productType_taxType | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings_channel; + discountedPrice: ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_defaultVariant { + __typename: "ProductVariant"; + id: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_category { + __typename: "Category"; + id: string; + name: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_collections { + __typename: "Collection"; + id: string; + name: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + quantityAllocated: number; + warehouse: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_stocks_warehouse; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_channel; + price: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_price | null; + costPrice: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_costPrice | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants { + __typename: "ProductVariant"; + id: string; + sku: string; + name: string; + margin: number | null; + stocks: (ProductChannelListingUpdate_productChannelListingUpdate_product_variants_stocks | null)[] | null; + trackInventory: boolean; + channelListings: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings[] | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_weight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_product { + __typename: "Product"; + id: string; + attributes: ProductChannelListingUpdate_productChannelListingUpdate_product_attributes[]; + productType: ProductChannelListingUpdate_productChannelListingUpdate_product_productType; + channelListings: ProductChannelListingUpdate_productChannelListingUpdate_product_channelListings[] | null; + metadata: (ProductChannelListingUpdate_productChannelListingUpdate_product_metadata | null)[]; + privateMetadata: (ProductChannelListingUpdate_productChannelListingUpdate_product_privateMetadata | null)[]; + name: string; + slug: string; + descriptionJson: any; + seoTitle: string | null; + seoDescription: string | null; + defaultVariant: ProductChannelListingUpdate_productChannelListingUpdate_product_defaultVariant | null; + category: ProductChannelListingUpdate_productChannelListingUpdate_product_category | null; + collections: (ProductChannelListingUpdate_productChannelListingUpdate_product_collections | null)[] | null; + chargeTaxes: boolean; + images: (ProductChannelListingUpdate_productChannelListingUpdate_product_images | null)[] | null; + isAvailable: boolean | null; + variants: (ProductChannelListingUpdate_productChannelListingUpdate_product_variants | null)[] | null; + weight: ProductChannelListingUpdate_productChannelListingUpdate_product_weight | null; + taxType: ProductChannelListingUpdate_productChannelListingUpdate_product_taxType | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate_errors { + __typename: "ProductChannelListingError"; + code: ProductErrorCode; + field: string | null; + message: string | null; + channels: string[] | null; +} + +export interface ProductChannelListingUpdate_productChannelListingUpdate { + __typename: "ProductChannelListingUpdate"; + product: ProductChannelListingUpdate_productChannelListingUpdate_product | null; + errors: ProductChannelListingUpdate_productChannelListingUpdate_errors[]; +} + +export interface ProductChannelListingUpdate { + productChannelListingUpdate: ProductChannelListingUpdate_productChannelListingUpdate | null; +} + +export interface ProductChannelListingUpdateVariables { + id: string; + input: ProductChannelListingUpdateInput; +} diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index 6db6a62df..ce83caf3d 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -74,37 +74,28 @@ export interface ProductCreate_productCreate_product_productType { taxType: ProductCreate_productCreate_product_productType_taxType | null; } -export interface ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductCreate_productCreate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductCreate_productCreate_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductCreate_productCreate_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductCreate_productCreate_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductCreate_productCreate_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductCreate_productCreate_product_pricing_priceRangeUndiscounted | null; +export interface ProductCreate_productCreate_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductCreate_productCreate_product_channelListings_channel; + discountedPrice: ProductCreate_productCreate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductCreate_productCreate_product_metadata { @@ -136,30 +127,6 @@ export interface ProductCreate_productCreate_product_collections { name: string; } -export interface ProductCreate_productCreate_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductCreate_productCreate_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductCreate_productCreate_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductCreate_productCreate_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductCreate_productCreate_product_purchaseCost_start | null; - stop: ProductCreate_productCreate_product_purchaseCost_stop | null; -} - export interface ProductCreate_productCreate_product_images { __typename: "ProductImage"; id: string; @@ -168,12 +135,6 @@ export interface ProductCreate_productCreate_product_images { url: string; } -export interface ProductCreate_productCreate_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductCreate_productCreate_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -188,15 +149,41 @@ export interface ProductCreate_productCreate_product_variants_stocks { warehouse: ProductCreate_productCreate_product_variants_stocks_warehouse; } +export interface ProductCreate_productCreate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductCreate_productCreate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductCreate_productCreate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductCreate_productCreate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductCreate_productCreate_product_variants_channelListings_channel; + price: ProductCreate_productCreate_product_variants_channelListings_price | null; + costPrice: ProductCreate_productCreate_product_variants_channelListings_costPrice | null; +} + export interface ProductCreate_productCreate_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductCreate_productCreate_product_variants_price | null; margin: number | null; stocks: (ProductCreate_productCreate_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductCreate_productCreate_product_variants_channelListings[] | null; } export interface ProductCreate_productCreate_product_weight { @@ -216,7 +203,7 @@ export interface ProductCreate_productCreate_product { id: string; attributes: ProductCreate_productCreate_product_attributes[]; productType: ProductCreate_productCreate_product_productType; - pricing: ProductCreate_productCreate_product_pricing | null; + channelListings: ProductCreate_productCreate_product_channelListings[] | null; metadata: (ProductCreate_productCreate_product_metadata | null)[]; privateMetadata: (ProductCreate_productCreate_product_privateMetadata | null)[]; name: string; @@ -227,19 +214,12 @@ export interface ProductCreate_productCreate_product { defaultVariant: ProductCreate_productCreate_product_defaultVariant | null; category: ProductCreate_productCreate_product_category | null; collections: (ProductCreate_productCreate_product_collections | null)[] | null; - margin: ProductCreate_productCreate_product_margin | null; - purchaseCost: ProductCreate_productCreate_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductCreate_productCreate_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductCreate_productCreate_product_variants | null)[] | null; weight: ProductCreate_productCreate_product_weight | null; taxType: ProductCreate_productCreate_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductCreate_productCreate { diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 21a614bc0..0d81e2f8e 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -67,37 +67,28 @@ export interface ProductDetails_product_productType { taxType: ProductDetails_product_productType_taxType | null; } -export interface ProductDetails_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductDetails_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductDetails_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductDetails_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductDetails_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductDetails_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductDetails_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductDetails_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductDetails_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductDetails_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductDetails_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductDetails_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductDetails_product_pricing_priceRangeUndiscounted | null; +export interface ProductDetails_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductDetails_product_channelListings_channel; + discountedPrice: ProductDetails_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductDetails_product_metadata { @@ -129,30 +120,6 @@ export interface ProductDetails_product_collections { name: string; } -export interface ProductDetails_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductDetails_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductDetails_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductDetails_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductDetails_product_purchaseCost_start | null; - stop: ProductDetails_product_purchaseCost_stop | null; -} - export interface ProductDetails_product_images { __typename: "ProductImage"; id: string; @@ -161,12 +128,6 @@ export interface ProductDetails_product_images { url: string; } -export interface ProductDetails_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductDetails_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -181,15 +142,41 @@ export interface ProductDetails_product_variants_stocks { warehouse: ProductDetails_product_variants_stocks_warehouse; } +export interface ProductDetails_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductDetails_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductDetails_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductDetails_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductDetails_product_variants_channelListings_channel; + price: ProductDetails_product_variants_channelListings_price | null; + costPrice: ProductDetails_product_variants_channelListings_costPrice | null; +} + export interface ProductDetails_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductDetails_product_variants_price | null; margin: number | null; stocks: (ProductDetails_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductDetails_product_variants_channelListings[] | null; } export interface ProductDetails_product_weight { @@ -209,7 +196,7 @@ export interface ProductDetails_product { id: string; attributes: ProductDetails_product_attributes[]; productType: ProductDetails_product_productType; - pricing: ProductDetails_product_pricing | null; + channelListings: ProductDetails_product_channelListings[] | null; metadata: (ProductDetails_product_metadata | null)[]; privateMetadata: (ProductDetails_product_privateMetadata | null)[]; name: string; @@ -220,19 +207,12 @@ export interface ProductDetails_product { defaultVariant: ProductDetails_product_defaultVariant | null; category: ProductDetails_product_category | null; collections: (ProductDetails_product_collections | null)[] | null; - margin: ProductDetails_product_margin | null; - purchaseCost: ProductDetails_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductDetails_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductDetails_product_variants | null)[] | null; weight: ProductDetails_product_weight | null; taxType: ProductDetails_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductDetails_taxTypes { @@ -248,4 +228,5 @@ export interface ProductDetails { export interface ProductDetailsVariables { id: string; + channel?: string | null; } diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index c1782b45b..9074d1f99 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -73,37 +73,28 @@ export interface ProductImageCreate_productImageCreate_product_productType { taxType: ProductImageCreate_productImageCreate_product_productType_taxType | null; } -export interface ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductImageCreate_productImageCreate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductImageCreate_productImageCreate_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductImageCreate_productImageCreate_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductImageCreate_productImageCreate_product_pricing_priceRangeUndiscounted | null; +export interface ProductImageCreate_productImageCreate_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductImageCreate_productImageCreate_product_channelListings_channel; + discountedPrice: ProductImageCreate_productImageCreate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductImageCreate_productImageCreate_product_metadata { @@ -135,30 +126,6 @@ export interface ProductImageCreate_productImageCreate_product_collections { name: string; } -export interface ProductImageCreate_productImageCreate_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductImageCreate_productImageCreate_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageCreate_productImageCreate_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageCreate_productImageCreate_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductImageCreate_productImageCreate_product_purchaseCost_start | null; - stop: ProductImageCreate_productImageCreate_product_purchaseCost_stop | null; -} - export interface ProductImageCreate_productImageCreate_product_images { __typename: "ProductImage"; id: string; @@ -167,12 +134,6 @@ export interface ProductImageCreate_productImageCreate_product_images { url: string; } -export interface ProductImageCreate_productImageCreate_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductImageCreate_productImageCreate_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -187,15 +148,41 @@ export interface ProductImageCreate_productImageCreate_product_variants_stocks { warehouse: ProductImageCreate_productImageCreate_product_variants_stocks_warehouse; } +export interface ProductImageCreate_productImageCreate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductImageCreate_productImageCreate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductImageCreate_productImageCreate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductImageCreate_productImageCreate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductImageCreate_productImageCreate_product_variants_channelListings_channel; + price: ProductImageCreate_productImageCreate_product_variants_channelListings_price | null; + costPrice: ProductImageCreate_productImageCreate_product_variants_channelListings_costPrice | null; +} + export interface ProductImageCreate_productImageCreate_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductImageCreate_productImageCreate_product_variants_price | null; margin: number | null; stocks: (ProductImageCreate_productImageCreate_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductImageCreate_productImageCreate_product_variants_channelListings[] | null; } export interface ProductImageCreate_productImageCreate_product_weight { @@ -215,7 +202,7 @@ export interface ProductImageCreate_productImageCreate_product { id: string; attributes: ProductImageCreate_productImageCreate_product_attributes[]; productType: ProductImageCreate_productImageCreate_product_productType; - pricing: ProductImageCreate_productImageCreate_product_pricing | null; + channelListings: ProductImageCreate_productImageCreate_product_channelListings[] | null; metadata: (ProductImageCreate_productImageCreate_product_metadata | null)[]; privateMetadata: (ProductImageCreate_productImageCreate_product_privateMetadata | null)[]; name: string; @@ -226,19 +213,12 @@ export interface ProductImageCreate_productImageCreate_product { defaultVariant: ProductImageCreate_productImageCreate_product_defaultVariant | null; category: ProductImageCreate_productImageCreate_product_category | null; collections: (ProductImageCreate_productImageCreate_product_collections | null)[] | null; - margin: ProductImageCreate_productImageCreate_product_margin | null; - purchaseCost: ProductImageCreate_productImageCreate_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductImageCreate_productImageCreate_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductImageCreate_productImageCreate_product_variants | null)[] | null; weight: ProductImageCreate_productImageCreate_product_weight | null; taxType: ProductImageCreate_productImageCreate_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductImageCreate_productImageCreate { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 2df7a1953..b2e8a1c3d 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -73,37 +73,28 @@ export interface ProductImageUpdate_productImageUpdate_product_productType { taxType: ProductImageUpdate_productImageUpdate_product_productType_taxType | null; } -export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductImageUpdate_productImageUpdate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductImageUpdate_productImageUpdate_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductImageUpdate_productImageUpdate_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductImageUpdate_productImageUpdate_product_pricing_priceRangeUndiscounted | null; +export interface ProductImageUpdate_productImageUpdate_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductImageUpdate_productImageUpdate_product_channelListings_channel; + discountedPrice: ProductImageUpdate_productImageUpdate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductImageUpdate_productImageUpdate_product_metadata { @@ -135,30 +126,6 @@ export interface ProductImageUpdate_productImageUpdate_product_collections { name: string; } -export interface ProductImageUpdate_productImageUpdate_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductImageUpdate_productImageUpdate_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageUpdate_productImageUpdate_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductImageUpdate_productImageUpdate_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductImageUpdate_productImageUpdate_product_purchaseCost_start | null; - stop: ProductImageUpdate_productImageUpdate_product_purchaseCost_stop | null; -} - export interface ProductImageUpdate_productImageUpdate_product_images { __typename: "ProductImage"; id: string; @@ -167,12 +134,6 @@ export interface ProductImageUpdate_productImageUpdate_product_images { url: string; } -export interface ProductImageUpdate_productImageUpdate_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductImageUpdate_productImageUpdate_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -187,15 +148,41 @@ export interface ProductImageUpdate_productImageUpdate_product_variants_stocks { warehouse: ProductImageUpdate_productImageUpdate_product_variants_stocks_warehouse; } +export interface ProductImageUpdate_productImageUpdate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductImageUpdate_productImageUpdate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductImageUpdate_productImageUpdate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductImageUpdate_productImageUpdate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductImageUpdate_productImageUpdate_product_variants_channelListings_channel; + price: ProductImageUpdate_productImageUpdate_product_variants_channelListings_price | null; + costPrice: ProductImageUpdate_productImageUpdate_product_variants_channelListings_costPrice | null; +} + export interface ProductImageUpdate_productImageUpdate_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductImageUpdate_productImageUpdate_product_variants_price | null; margin: number | null; stocks: (ProductImageUpdate_productImageUpdate_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductImageUpdate_productImageUpdate_product_variants_channelListings[] | null; } export interface ProductImageUpdate_productImageUpdate_product_weight { @@ -215,7 +202,7 @@ export interface ProductImageUpdate_productImageUpdate_product { id: string; attributes: ProductImageUpdate_productImageUpdate_product_attributes[]; productType: ProductImageUpdate_productImageUpdate_product_productType; - pricing: ProductImageUpdate_productImageUpdate_product_pricing | null; + channelListings: ProductImageUpdate_productImageUpdate_product_channelListings[] | null; metadata: (ProductImageUpdate_productImageUpdate_product_metadata | null)[]; privateMetadata: (ProductImageUpdate_productImageUpdate_product_privateMetadata | null)[]; name: string; @@ -226,19 +213,12 @@ export interface ProductImageUpdate_productImageUpdate_product { defaultVariant: ProductImageUpdate_productImageUpdate_product_defaultVariant | null; category: ProductImageUpdate_productImageUpdate_product_category | null; collections: (ProductImageUpdate_productImageUpdate_product_collections | null)[] | null; - margin: ProductImageUpdate_productImageUpdate_product_margin | null; - purchaseCost: ProductImageUpdate_productImageUpdate_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductImageUpdate_productImageUpdate_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductImageUpdate_productImageUpdate_product_variants | null)[] | null; weight: ProductImageUpdate_productImageUpdate_product_weight | null; taxType: ProductImageUpdate_productImageUpdate_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductImageUpdate_productImageUpdate { diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts index c96e1001a..7dd42074a 100644 --- a/src/products/types/ProductList.ts +++ b/src/products/types/ProductList.ts @@ -20,6 +20,30 @@ export interface ProductList_products_edges_node_productType { hasVariants: boolean; } +export interface ProductList_products_edges_node_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductList_products_edges_node_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductList_products_edges_node_channelListings { + __typename: "ProductChannelListing"; + isPublished: boolean; + publicationDate: any | null; + discountedPrice: ProductList_products_edges_node_channelListings_discountedPrice | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; + channel: ProductList_products_edges_node_channelListings_channel; +} + export interface ProductList_products_edges_node_attributes_attribute { __typename: "Attribute"; id: string; @@ -37,49 +61,14 @@ export interface ProductList_products_edges_node_attributes { values: (ProductList_products_edges_node_attributes_values | null)[]; } -export interface ProductList_products_edges_node_pricing_priceRangeUndiscounted_start_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductList_products_edges_node_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductList_products_edges_node_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductList_products_edges_node_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductList_products_edges_node_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductList_products_edges_node_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductList_products_edges_node_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductList_products_edges_node_pricing_priceRangeUndiscounted_start | null; - stop: ProductList_products_edges_node_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductList_products_edges_node_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductList_products_edges_node_pricing_priceRangeUndiscounted | null; -} - export interface ProductList_products_edges_node { __typename: "Product"; id: string; name: string; thumbnail: ProductList_products_edges_node_thumbnail | null; - isAvailable: boolean | null; - isPublished: boolean; productType: ProductList_products_edges_node_productType; + channelListings: ProductList_products_edges_node_channelListings[] | null; attributes: ProductList_products_edges_node_attributes[]; - pricing: ProductList_products_edges_node_pricing | null; } export interface ProductList_products_edges { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index db1c2a1d9..378008e24 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -74,37 +74,28 @@ export interface ProductUpdate_productUpdate_product_productType { taxType: ProductUpdate_productUpdate_product_productType_taxType | null; } -export interface ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductUpdate_productUpdate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductUpdate_productUpdate_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductUpdate_productUpdate_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted | null; +export interface ProductUpdate_productUpdate_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductUpdate_productUpdate_product_channelListings_channel; + discountedPrice: ProductUpdate_productUpdate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductUpdate_productUpdate_product_metadata { @@ -136,30 +127,6 @@ export interface ProductUpdate_productUpdate_product_collections { name: string; } -export interface ProductUpdate_productUpdate_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductUpdate_productUpdate_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductUpdate_productUpdate_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductUpdate_productUpdate_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductUpdate_productUpdate_product_purchaseCost_start | null; - stop: ProductUpdate_productUpdate_product_purchaseCost_stop | null; -} - export interface ProductUpdate_productUpdate_product_images { __typename: "ProductImage"; id: string; @@ -168,12 +135,6 @@ export interface ProductUpdate_productUpdate_product_images { url: string; } -export interface ProductUpdate_productUpdate_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductUpdate_productUpdate_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -188,15 +149,41 @@ export interface ProductUpdate_productUpdate_product_variants_stocks { warehouse: ProductUpdate_productUpdate_product_variants_stocks_warehouse; } +export interface ProductUpdate_productUpdate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductUpdate_productUpdate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductUpdate_productUpdate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductUpdate_productUpdate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductUpdate_productUpdate_product_variants_channelListings_channel; + price: ProductUpdate_productUpdate_product_variants_channelListings_price | null; + costPrice: ProductUpdate_productUpdate_product_variants_channelListings_costPrice | null; +} + export interface ProductUpdate_productUpdate_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductUpdate_productUpdate_product_variants_price | null; margin: number | null; stocks: (ProductUpdate_productUpdate_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductUpdate_productUpdate_product_variants_channelListings[] | null; } export interface ProductUpdate_productUpdate_product_weight { @@ -216,7 +203,7 @@ export interface ProductUpdate_productUpdate_product { id: string; attributes: ProductUpdate_productUpdate_product_attributes[]; productType: ProductUpdate_productUpdate_product_productType; - pricing: ProductUpdate_productUpdate_product_pricing | null; + channelListings: ProductUpdate_productUpdate_product_channelListings[] | null; metadata: (ProductUpdate_productUpdate_product_metadata | null)[]; privateMetadata: (ProductUpdate_productUpdate_product_privateMetadata | null)[]; name: string; @@ -227,19 +214,12 @@ export interface ProductUpdate_productUpdate_product { defaultVariant: ProductUpdate_productUpdate_product_defaultVariant | null; category: ProductUpdate_productUpdate_product_category | null; collections: (ProductUpdate_productUpdate_product_collections | null)[] | null; - margin: ProductUpdate_productUpdate_product_margin | null; - purchaseCost: ProductUpdate_productUpdate_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductUpdate_productUpdate_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductUpdate_productUpdate_product_variants | null)[] | null; weight: ProductUpdate_productUpdate_product_weight | null; taxType: ProductUpdate_productUpdate_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductUpdate_productUpdate { diff --git a/src/products/types/ProductVariantBulkCreate.ts b/src/products/types/ProductVariantBulkCreate.ts index 2dc8867e6..ee3603eb8 100644 --- a/src/products/types/ProductVariantBulkCreate.ts +++ b/src/products/types/ProductVariantBulkCreate.ts @@ -13,6 +13,7 @@ export interface ProductVariantBulkCreate_productVariantBulkCreate_errors { field: string | null; code: ProductErrorCode; index: number | null; + channels: string[] | null; } export interface ProductVariantBulkCreate_productVariantBulkCreate { diff --git a/src/products/types/ProductVariantChannelListingUpdate.ts b/src/products/types/ProductVariantChannelListingUpdate.ts new file mode 100644 index 000000000..5addf30e6 --- /dev/null +++ b/src/products/types/ProductVariantChannelListingUpdate.ts @@ -0,0 +1,203 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductVariantChannelListingAddInput, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ProductVariantChannelListingUpdate +// ==================================================== + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + valueRequired: boolean; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute_values | null)[] | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes { + __typename: "SelectedAttribute"; + attribute: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_attribute; + values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes_values | null)[]; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_defaultVariant { + __typename: "ProductVariant"; + id: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_thumbnail { + __typename: "Image"; + url: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_channel; + discountedPrice: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_discountedPrice | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_variants_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_variants { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + images: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_variants_images | null)[] | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product { + __typename: "Product"; + id: string; + defaultVariant: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_defaultVariant | null; + images: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_images | null)[] | null; + name: string; + thumbnail: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_thumbnail | null; + channelListings: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings[] | null; + variants: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_variants | null)[] | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_channel; + price: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_price | null; + costPrice: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_costPrice | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + quantityAllocated: number; + warehouse: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_stocks_warehouse; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_weight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant { + __typename: "ProductVariant"; + id: string; + metadata: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_metadata | null)[]; + privateMetadata: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_privateMetadata | null)[]; + attributes: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_attributes[]; + images: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_images | null)[] | null; + name: string; + product: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product; + channelListings: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings[] | null; + sku: string; + stocks: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_stocks | null)[] | null; + trackInventory: boolean; + weight: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_weight | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_errors { + __typename: "ProductChannelListingError"; + code: ProductErrorCode; + field: string | null; + message: string | null; + channels: string[] | null; +} + +export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate { + __typename: "ProductVariantChannelListingUpdate"; + variant: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant | null; + errors: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_errors[]; +} + +export interface ProductVariantChannelListingUpdate { + productVariantChannelListingUpdate: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate | null; +} + +export interface ProductVariantChannelListingUpdateVariables { + id: string; + input: ProductVariantChannelListingAddInput[]; +} diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 248ec0fe1..55a8638d1 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -13,6 +13,18 @@ export interface ProductVariantCreateData_product_images { url: string; } +export interface ProductVariantCreateData_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantCreateData_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantCreateData_product_channelListings_channel; +} + export interface ProductVariantCreateData_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; @@ -58,6 +70,7 @@ export interface ProductVariantCreateData_product { __typename: "Product"; id: string; images: (ProductVariantCreateData_product_images | null)[] | null; + channelListings: ProductVariantCreateData_product_channelListings[] | null; name: string; productType: ProductVariantCreateData_product_productType; thumbnail: ProductVariantCreateData_product_thumbnail | null; diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index 7227a7405..25e72981e 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -49,24 +49,12 @@ export interface ProductVariantDetails_productVariant_attributes { values: (ProductVariantDetails_productVariant_attributes_values | null)[]; } -export interface ProductVariantDetails_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariantDetails_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface ProductVariantDetails_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariantDetails_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -85,6 +73,25 @@ export interface ProductVariantDetails_productVariant_product_thumbnail { url: string; } +export interface ProductVariantDetails_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantDetails_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantDetails_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantDetails_productVariant_product_channelListings_channel; + discountedPrice: ProductVariantDetails_productVariant_product_channelListings_discountedPrice | null; +} + export interface ProductVariantDetails_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -106,9 +113,36 @@ export interface ProductVariantDetails_productVariant_product { images: (ProductVariantDetails_productVariant_product_images | null)[] | null; name: string; thumbnail: ProductVariantDetails_productVariant_product_thumbnail | null; + channelListings: ProductVariantDetails_productVariant_product_channelListings[] | null; variants: (ProductVariantDetails_productVariant_product_variants | null)[] | null; } +export interface ProductVariantDetails_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantDetails_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantDetails_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantDetails_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductVariantDetails_productVariant_channelListings_channel; + price: ProductVariantDetails_productVariant_channelListings_price | null; + costPrice: ProductVariantDetails_productVariant_channelListings_costPrice | null; +} + export interface ProductVariantDetails_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -135,11 +169,10 @@ export interface ProductVariantDetails_productVariant { metadata: (ProductVariantDetails_productVariant_metadata | null)[]; privateMetadata: (ProductVariantDetails_productVariant_privateMetadata | null)[]; attributes: ProductVariantDetails_productVariant_attributes[]; - costPrice: ProductVariantDetails_productVariant_costPrice | null; images: (ProductVariantDetails_productVariant_images | null)[] | null; name: string; - price: ProductVariantDetails_productVariant_price | null; product: ProductVariantDetails_productVariant_product; + channelListings: ProductVariantDetails_productVariant_channelListings[] | null; sku: string; stocks: (ProductVariantDetails_productVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index 1870dd9c6..7fc8b77c2 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -73,37 +73,28 @@ export interface ProductVariantReorder_productVariantReorder_product_productType taxType: ProductVariantReorder_productVariantReorder_product_productType_taxType | null; } -export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductVariantReorder_productVariantReorder_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductVariantReorder_productVariantReorder_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted | null; +export interface ProductVariantReorder_productVariantReorder_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantReorder_productVariantReorder_product_channelListings_channel; + discountedPrice: ProductVariantReorder_productVariantReorder_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductVariantReorder_productVariantReorder_product_metadata { @@ -135,30 +126,6 @@ export interface ProductVariantReorder_productVariantReorder_product_collections name: string; } -export interface ProductVariantReorder_productVariantReorder_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantReorder_productVariantReorder_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductVariantReorder_productVariantReorder_product_purchaseCost_start | null; - stop: ProductVariantReorder_productVariantReorder_product_purchaseCost_stop | null; -} - export interface ProductVariantReorder_productVariantReorder_product_images { __typename: "ProductImage"; id: string; @@ -167,12 +134,6 @@ export interface ProductVariantReorder_productVariantReorder_product_images { url: string; } -export interface ProductVariantReorder_productVariantReorder_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -187,15 +148,41 @@ export interface ProductVariantReorder_productVariantReorder_product_variants_st warehouse: ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse; } +export interface ProductVariantReorder_productVariantReorder_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductVariantReorder_productVariantReorder_product_variants_channelListings_channel; + price: ProductVariantReorder_productVariantReorder_product_variants_channelListings_price | null; + costPrice: ProductVariantReorder_productVariantReorder_product_variants_channelListings_costPrice | null; +} + export interface ProductVariantReorder_productVariantReorder_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductVariantReorder_productVariantReorder_product_variants_price | null; margin: number | null; stocks: (ProductVariantReorder_productVariantReorder_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductVariantReorder_productVariantReorder_product_variants_channelListings[] | null; } export interface ProductVariantReorder_productVariantReorder_product_weight { @@ -215,7 +202,7 @@ export interface ProductVariantReorder_productVariantReorder_product { id: string; attributes: ProductVariantReorder_productVariantReorder_product_attributes[]; productType: ProductVariantReorder_productVariantReorder_product_productType; - pricing: ProductVariantReorder_productVariantReorder_product_pricing | null; + channelListings: ProductVariantReorder_productVariantReorder_product_channelListings[] | null; metadata: (ProductVariantReorder_productVariantReorder_product_metadata | null)[]; privateMetadata: (ProductVariantReorder_productVariantReorder_product_privateMetadata | null)[]; name: string; @@ -226,19 +213,12 @@ export interface ProductVariantReorder_productVariantReorder_product { defaultVariant: ProductVariantReorder_productVariantReorder_product_defaultVariant | null; category: ProductVariantReorder_productVariantReorder_product_category | null; collections: (ProductVariantReorder_productVariantReorder_product_collections | null)[] | null; - margin: ProductVariantReorder_productVariantReorder_product_margin | null; - purchaseCost: ProductVariantReorder_productVariantReorder_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductVariantReorder_productVariantReorder_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductVariantReorder_productVariantReorder_product_variants | null)[] | null; weight: ProductVariantReorder_productVariantReorder_product_weight | null; taxType: ProductVariantReorder_productVariantReorder_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductVariantReorder_productVariantReorder { diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index 2448d3574..1b765a784 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -73,37 +73,28 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_produ taxType: ProductVariantSetDefault_productVariantSetDefault_product_productType_taxType | null; } -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_start_gross { +export interface ProductVariantSetDefault_productVariantSetDefault_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantSetDefault_productVariantSetDefault_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_start | null; - stop: ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: ProductVariantSetDefault_productVariantSetDefault_product_pricing_priceRangeUndiscounted | null; +export interface ProductVariantSetDefault_productVariantSetDefault_product_channelListings { + __typename: "ProductChannelListing"; + channel: ProductVariantSetDefault_productVariantSetDefault_product_channelListings_channel; + discountedPrice: ProductVariantSetDefault_productVariantSetDefault_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface ProductVariantSetDefault_productVariantSetDefault_product_metadata { @@ -135,30 +126,6 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_colle name: string; } -export interface ProductVariantSetDefault_productVariantSetDefault_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost { - __typename: "MoneyRange"; - start: ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost_start | null; - stop: ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost_stop | null; -} - export interface ProductVariantSetDefault_productVariantSetDefault_product_images { __typename: "ProductImage"; id: string; @@ -167,12 +134,6 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_image url: string; } -export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -187,15 +148,41 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_varia warehouse: ProductVariantSetDefault_productVariantSetDefault_product_variants_stocks_warehouse; } +export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_channel; + price: ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_price | null; + costPrice: ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings_costPrice | null; +} + export interface ProductVariantSetDefault_productVariantSetDefault_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: ProductVariantSetDefault_productVariantSetDefault_product_variants_price | null; margin: number | null; stocks: (ProductVariantSetDefault_productVariantSetDefault_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: ProductVariantSetDefault_productVariantSetDefault_product_variants_channelListings[] | null; } export interface ProductVariantSetDefault_productVariantSetDefault_product_weight { @@ -215,7 +202,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product { id: string; attributes: ProductVariantSetDefault_productVariantSetDefault_product_attributes[]; productType: ProductVariantSetDefault_productVariantSetDefault_product_productType; - pricing: ProductVariantSetDefault_productVariantSetDefault_product_pricing | null; + channelListings: ProductVariantSetDefault_productVariantSetDefault_product_channelListings[] | null; metadata: (ProductVariantSetDefault_productVariantSetDefault_product_metadata | null)[]; privateMetadata: (ProductVariantSetDefault_productVariantSetDefault_product_privateMetadata | null)[]; name: string; @@ -226,19 +213,12 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product { defaultVariant: ProductVariantSetDefault_productVariantSetDefault_product_defaultVariant | null; category: ProductVariantSetDefault_productVariantSetDefault_product_category | null; collections: (ProductVariantSetDefault_productVariantSetDefault_product_collections | null)[] | null; - margin: ProductVariantSetDefault_productVariantSetDefault_product_margin | null; - purchaseCost: ProductVariantSetDefault_productVariantSetDefault_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (ProductVariantSetDefault_productVariantSetDefault_product_images | null)[] | null; + isAvailable: boolean | null; variants: (ProductVariantSetDefault_productVariantSetDefault_product_variants | null)[] | null; weight: ProductVariantSetDefault_productVariantSetDefault_product_weight | null; taxType: ProductVariantSetDefault_productVariantSetDefault_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface ProductVariantSetDefault_productVariantSetDefault { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index b0c1a04d3..aebfc4f61 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -74,37 +74,28 @@ export interface SimpleProductUpdate_productUpdate_product_productType { taxType: SimpleProductUpdate_productUpdate_product_productType_taxType | null; } -export interface SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start_gross { +export interface SimpleProductUpdate_productUpdate_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productUpdate_product_channelListings_discountedPrice { __typename: "Money"; amount: number; currency: string; } -export interface SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start { - __typename: "TaxedMoney"; - gross: SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start_gross; -} - -export interface SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop { - __typename: "TaxedMoney"; - gross: SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop_gross; -} - -export interface SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted { - __typename: "TaxedMoneyRange"; - start: SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_start | null; - stop: SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted_stop | null; -} - -export interface SimpleProductUpdate_productUpdate_product_pricing { - __typename: "ProductPricingInfo"; - priceRangeUndiscounted: SimpleProductUpdate_productUpdate_product_pricing_priceRangeUndiscounted | null; +export interface SimpleProductUpdate_productUpdate_product_channelListings { + __typename: "ProductChannelListing"; + channel: SimpleProductUpdate_productUpdate_product_channelListings_channel; + discountedPrice: SimpleProductUpdate_productUpdate_product_channelListings_discountedPrice | null; + isPublished: boolean; + publicationDate: any | null; + isAvailableForPurchase: boolean | null; + availableForPurchase: any | null; + visibleInListings: boolean; } export interface SimpleProductUpdate_productUpdate_product_metadata { @@ -136,30 +127,6 @@ export interface SimpleProductUpdate_productUpdate_product_collections { name: string; } -export interface SimpleProductUpdate_productUpdate_product_margin { - __typename: "Margin"; - start: number | null; - stop: number | null; -} - -export interface SimpleProductUpdate_productUpdate_product_purchaseCost_start { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface SimpleProductUpdate_productUpdate_product_purchaseCost_stop { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface SimpleProductUpdate_productUpdate_product_purchaseCost { - __typename: "MoneyRange"; - start: SimpleProductUpdate_productUpdate_product_purchaseCost_start | null; - stop: SimpleProductUpdate_productUpdate_product_purchaseCost_stop | null; -} - export interface SimpleProductUpdate_productUpdate_product_images { __typename: "ProductImage"; id: string; @@ -168,12 +135,6 @@ export interface SimpleProductUpdate_productUpdate_product_images { url: string; } -export interface SimpleProductUpdate_productUpdate_product_variants_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productUpdate_product_variants_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -188,15 +149,41 @@ export interface SimpleProductUpdate_productUpdate_product_variants_stocks { warehouse: SimpleProductUpdate_productUpdate_product_variants_stocks_warehouse; } +export interface SimpleProductUpdate_productUpdate_product_variants_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productUpdate_product_variants_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productUpdate_product_variants_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productUpdate_product_variants_channelListings { + __typename: "ProductVariantChannelListing"; + channel: SimpleProductUpdate_productUpdate_product_variants_channelListings_channel; + price: SimpleProductUpdate_productUpdate_product_variants_channelListings_price | null; + costPrice: SimpleProductUpdate_productUpdate_product_variants_channelListings_costPrice | null; +} + export interface SimpleProductUpdate_productUpdate_product_variants { __typename: "ProductVariant"; id: string; sku: string; name: string; - price: SimpleProductUpdate_productUpdate_product_variants_price | null; margin: number | null; stocks: (SimpleProductUpdate_productUpdate_product_variants_stocks | null)[] | null; trackInventory: boolean; + channelListings: SimpleProductUpdate_productUpdate_product_variants_channelListings[] | null; } export interface SimpleProductUpdate_productUpdate_product_weight { @@ -216,7 +203,7 @@ export interface SimpleProductUpdate_productUpdate_product { id: string; attributes: SimpleProductUpdate_productUpdate_product_attributes[]; productType: SimpleProductUpdate_productUpdate_product_productType; - pricing: SimpleProductUpdate_productUpdate_product_pricing | null; + channelListings: SimpleProductUpdate_productUpdate_product_channelListings[] | null; metadata: (SimpleProductUpdate_productUpdate_product_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productUpdate_product_privateMetadata | null)[]; name: string; @@ -227,19 +214,12 @@ export interface SimpleProductUpdate_productUpdate_product { defaultVariant: SimpleProductUpdate_productUpdate_product_defaultVariant | null; category: SimpleProductUpdate_productUpdate_product_category | null; collections: (SimpleProductUpdate_productUpdate_product_collections | null)[] | null; - margin: SimpleProductUpdate_productUpdate_product_margin | null; - purchaseCost: SimpleProductUpdate_productUpdate_product_purchaseCost | null; - isAvailableForPurchase: boolean | null; - isAvailable: boolean | null; - isPublished: boolean; chargeTaxes: boolean; - publicationDate: any | null; images: (SimpleProductUpdate_productUpdate_product_images | null)[] | null; + isAvailable: boolean | null; variants: (SimpleProductUpdate_productUpdate_product_variants | null)[] | null; weight: SimpleProductUpdate_productUpdate_product_weight | null; taxType: SimpleProductUpdate_productUpdate_product_taxType | null; - availableForPurchase: any | null; - visibleInListings: boolean; } export interface SimpleProductUpdate_productUpdate { @@ -296,24 +276,12 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_attribu values: (SimpleProductUpdate_productVariantUpdate_productVariant_attributes_values | null)[]; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantUpdate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface SimpleProductUpdate_productVariantUpdate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -332,6 +300,25 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_product url: string; } +export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_channel; + discountedPrice: SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_discountedPrice | null; +} + export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -353,9 +340,36 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_product images: (SimpleProductUpdate_productVariantUpdate_productVariant_product_images | null)[] | null; name: string; thumbnail: SimpleProductUpdate_productVariantUpdate_productVariant_product_thumbnail | null; + channelListings: SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings[] | null; variants: (SimpleProductUpdate_productVariantUpdate_productVariant_product_variants | null)[] | null; } +export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_channel; + price: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_price | null; + costPrice: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_costPrice | null; +} + export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -382,11 +396,10 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant { metadata: (SimpleProductUpdate_productVariantUpdate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantUpdate_productVariant_privateMetadata | null)[]; attributes: SimpleProductUpdate_productVariantUpdate_productVariant_attributes[]; - costPrice: SimpleProductUpdate_productVariantUpdate_productVariant_costPrice | null; images: (SimpleProductUpdate_productVariantUpdate_productVariant_images | null)[] | null; name: string; - price: SimpleProductUpdate_productVariantUpdate_productVariant_price | null; product: SimpleProductUpdate_productVariantUpdate_productVariant_product; + channelListings: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings[] | null; sku: string; stocks: (SimpleProductUpdate_productVariantUpdate_productVariant_stocks | null)[] | null; trackInventory: boolean; @@ -447,24 +460,12 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_a values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_values | null)[]; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -483,6 +484,25 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_p url: string; } +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_channel; + discountedPrice: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_discountedPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -504,9 +524,36 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_p images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_product_images | null)[] | null; name: string; thumbnail: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_thumbnail | null; + channelListings: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings[] | null; variants: (SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants | null)[] | null; } +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_channel; + price: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_price | null; + costPrice: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_costPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -533,11 +580,10 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant { metadata: (SimpleProductUpdate_productVariantStocksCreate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksCreate_productVariant_privateMetadata | null)[]; attributes: SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes[]; - costPrice: SimpleProductUpdate_productVariantStocksCreate_productVariant_costPrice | null; images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_images | null)[] | null; name: string; - price: SimpleProductUpdate_productVariantStocksCreate_productVariant_price | null; product: SimpleProductUpdate_productVariantStocksCreate_productVariant_product; + channelListings: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings[] | null; sku: string; stocks: (SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks | null)[] | null; trackInventory: boolean; @@ -597,24 +643,12 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_a values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_values | null)[]; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -633,6 +667,25 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_p url: string; } +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_channel; + discountedPrice: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_discountedPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -654,9 +707,36 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_p images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_product_images | null)[] | null; name: string; thumbnail: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_thumbnail | null; + channelListings: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings[] | null; variants: (SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants | null)[] | null; } +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_channel; + price: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_price | null; + costPrice: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_costPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -683,11 +763,10 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant { metadata: (SimpleProductUpdate_productVariantStocksDelete_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksDelete_productVariant_privateMetadata | null)[]; attributes: SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes[]; - costPrice: SimpleProductUpdate_productVariantStocksDelete_productVariant_costPrice | null; images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_images | null)[] | null; name: string; - price: SimpleProductUpdate_productVariantStocksDelete_productVariant_price | null; product: SimpleProductUpdate_productVariantStocksDelete_productVariant_product; + channelListings: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings[] | null; sku: string; stocks: (SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks | null)[] | null; trackInventory: boolean; @@ -748,24 +827,12 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_a values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_values | null)[]; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -784,6 +851,25 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_p url: string; } +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel; + discountedPrice: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_discountedPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -805,9 +891,36 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_p images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_images | null)[] | null; name: string; thumbnail: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_thumbnail | null; + channelListings: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings[] | null; variants: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants | null)[] | null; } +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_channel; + price: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_price | null; + costPrice: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice | null; +} + export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -834,11 +947,10 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant { metadata: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_metadata | null)[]; privateMetadata: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_privateMetadata | null)[]; attributes: SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes[]; - costPrice: SimpleProductUpdate_productVariantStocksUpdate_productVariant_costPrice | null; images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; name: string; - price: SimpleProductUpdate_productVariantStocksUpdate_productVariant_price | null; product: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product; + channelListings: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings[] | null; sku: string; stocks: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index be28216f6..a0b50a9d5 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -56,24 +56,12 @@ export interface VariantCreate_productVariantCreate_productVariant_attributes { values: (VariantCreate_productVariantCreate_productVariant_attributes_values | null)[]; } -export interface VariantCreate_productVariantCreate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantCreate_productVariantCreate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface VariantCreate_productVariantCreate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantCreate_productVariantCreate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -92,6 +80,25 @@ export interface VariantCreate_productVariantCreate_productVariant_product_thumb url: string; } +export interface VariantCreate_productVariantCreate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: VariantCreate_productVariantCreate_productVariant_product_channelListings_channel; + discountedPrice: VariantCreate_productVariantCreate_productVariant_product_channelListings_discountedPrice | null; +} + export interface VariantCreate_productVariantCreate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -113,9 +120,36 @@ export interface VariantCreate_productVariantCreate_productVariant_product { images: (VariantCreate_productVariantCreate_productVariant_product_images | null)[] | null; name: string; thumbnail: VariantCreate_productVariantCreate_productVariant_product_thumbnail | null; + channelListings: VariantCreate_productVariantCreate_productVariant_product_channelListings[] | null; variants: (VariantCreate_productVariantCreate_productVariant_product_variants | null)[] | null; } +export interface VariantCreate_productVariantCreate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: VariantCreate_productVariantCreate_productVariant_channelListings_channel; + price: VariantCreate_productVariantCreate_productVariant_channelListings_price | null; + costPrice: VariantCreate_productVariantCreate_productVariant_channelListings_costPrice | null; +} + export interface VariantCreate_productVariantCreate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -142,11 +176,10 @@ export interface VariantCreate_productVariantCreate_productVariant { metadata: (VariantCreate_productVariantCreate_productVariant_metadata | null)[]; privateMetadata: (VariantCreate_productVariantCreate_productVariant_privateMetadata | null)[]; attributes: VariantCreate_productVariantCreate_productVariant_attributes[]; - costPrice: VariantCreate_productVariantCreate_productVariant_costPrice | null; images: (VariantCreate_productVariantCreate_productVariant_images | null)[] | null; name: string; - price: VariantCreate_productVariantCreate_productVariant_price | null; product: VariantCreate_productVariantCreate_productVariant_product; + channelListings: VariantCreate_productVariantCreate_productVariant_channelListings[] | null; sku: string; stocks: (VariantCreate_productVariantCreate_productVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index 5dcf48722..baf4917ca 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -55,24 +55,12 @@ export interface VariantImageAssign_variantImageAssign_productVariant_attributes values: (VariantImageAssign_variantImageAssign_productVariant_attributes_values | null)[]; } -export interface VariantImageAssign_variantImageAssign_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantImageAssign_variantImageAssign_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface VariantImageAssign_variantImageAssign_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantImageAssign_variantImageAssign_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -91,6 +79,25 @@ export interface VariantImageAssign_variantImageAssign_productVariant_product_th url: string; } +export interface VariantImageAssign_variantImageAssign_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: VariantImageAssign_variantImageAssign_productVariant_product_channelListings_channel; + discountedPrice: VariantImageAssign_variantImageAssign_productVariant_product_channelListings_discountedPrice | null; +} + export interface VariantImageAssign_variantImageAssign_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -112,9 +119,36 @@ export interface VariantImageAssign_variantImageAssign_productVariant_product { images: (VariantImageAssign_variantImageAssign_productVariant_product_images | null)[] | null; name: string; thumbnail: VariantImageAssign_variantImageAssign_productVariant_product_thumbnail | null; + channelListings: VariantImageAssign_variantImageAssign_productVariant_product_channelListings[] | null; variants: (VariantImageAssign_variantImageAssign_productVariant_product_variants | null)[] | null; } +export interface VariantImageAssign_variantImageAssign_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: VariantImageAssign_variantImageAssign_productVariant_channelListings_channel; + price: VariantImageAssign_variantImageAssign_productVariant_channelListings_price | null; + costPrice: VariantImageAssign_variantImageAssign_productVariant_channelListings_costPrice | null; +} + export interface VariantImageAssign_variantImageAssign_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -141,11 +175,10 @@ export interface VariantImageAssign_variantImageAssign_productVariant { metadata: (VariantImageAssign_variantImageAssign_productVariant_metadata | null)[]; privateMetadata: (VariantImageAssign_variantImageAssign_productVariant_privateMetadata | null)[]; attributes: VariantImageAssign_variantImageAssign_productVariant_attributes[]; - costPrice: VariantImageAssign_variantImageAssign_productVariant_costPrice | null; images: (VariantImageAssign_variantImageAssign_productVariant_images | null)[] | null; name: string; - price: VariantImageAssign_variantImageAssign_productVariant_price | null; product: VariantImageAssign_variantImageAssign_productVariant_product; + channelListings: VariantImageAssign_variantImageAssign_productVariant_channelListings[] | null; sku: string; stocks: (VariantImageAssign_variantImageAssign_productVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index 347b3f992..c55630f1f 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -55,24 +55,12 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_attrib values: (VariantImageUnassign_variantImageUnassign_productVariant_attributes_values | null)[]; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantImageUnassign_variantImageUnassign_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface VariantImageUnassign_variantImageUnassign_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantImageUnassign_variantImageUnassign_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -91,6 +79,25 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_produc url: string; } +export interface VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings_channel; + discountedPrice: VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings_discountedPrice | null; +} + export interface VariantImageUnassign_variantImageUnassign_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -112,9 +119,36 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_produc images: (VariantImageUnassign_variantImageUnassign_productVariant_product_images | null)[] | null; name: string; thumbnail: VariantImageUnassign_variantImageUnassign_productVariant_product_thumbnail | null; + channelListings: VariantImageUnassign_variantImageUnassign_productVariant_product_channelListings[] | null; variants: (VariantImageUnassign_variantImageUnassign_productVariant_product_variants | null)[] | null; } +export interface VariantImageUnassign_variantImageUnassign_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: VariantImageUnassign_variantImageUnassign_productVariant_channelListings_channel; + price: VariantImageUnassign_variantImageUnassign_productVariant_channelListings_price | null; + costPrice: VariantImageUnassign_variantImageUnassign_productVariant_channelListings_costPrice | null; +} + export interface VariantImageUnassign_variantImageUnassign_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -141,11 +175,10 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant { metadata: (VariantImageUnassign_variantImageUnassign_productVariant_metadata | null)[]; privateMetadata: (VariantImageUnassign_variantImageUnassign_productVariant_privateMetadata | null)[]; attributes: VariantImageUnassign_variantImageUnassign_productVariant_attributes[]; - costPrice: VariantImageUnassign_variantImageUnassign_productVariant_costPrice | null; images: (VariantImageUnassign_variantImageUnassign_productVariant_images | null)[] | null; name: string; - price: VariantImageUnassign_variantImageUnassign_productVariant_price | null; product: VariantImageUnassign_variantImageUnassign_productVariant_product; + channelListings: VariantImageUnassign_variantImageUnassign_productVariant_channelListings[] | null; sku: string; stocks: (VariantImageUnassign_variantImageUnassign_productVariant_stocks | null)[] | null; trackInventory: boolean; diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 7bb411da3..3c18b6acc 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -56,24 +56,12 @@ export interface VariantUpdate_productVariantUpdate_productVariant_attributes { values: (VariantUpdate_productVariantUpdate_productVariant_attributes_values | null)[]; } -export interface VariantUpdate_productVariantUpdate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantUpdate_productVariantUpdate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface VariantUpdate_productVariantUpdate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantUpdate_productVariantUpdate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -92,6 +80,25 @@ export interface VariantUpdate_productVariantUpdate_productVariant_product_thumb url: string; } +export interface VariantUpdate_productVariantUpdate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: VariantUpdate_productVariantUpdate_productVariant_product_channelListings_channel; + discountedPrice: VariantUpdate_productVariantUpdate_productVariant_product_channelListings_discountedPrice | null; +} + export interface VariantUpdate_productVariantUpdate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -113,9 +120,36 @@ export interface VariantUpdate_productVariantUpdate_productVariant_product { images: (VariantUpdate_productVariantUpdate_productVariant_product_images | null)[] | null; name: string; thumbnail: VariantUpdate_productVariantUpdate_productVariant_product_thumbnail | null; + channelListings: VariantUpdate_productVariantUpdate_productVariant_product_channelListings[] | null; variants: (VariantUpdate_productVariantUpdate_productVariant_product_variants | null)[] | null; } +export interface VariantUpdate_productVariantUpdate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: VariantUpdate_productVariantUpdate_productVariant_channelListings_channel; + price: VariantUpdate_productVariantUpdate_productVariant_channelListings_price | null; + costPrice: VariantUpdate_productVariantUpdate_productVariant_channelListings_costPrice | null; +} + export interface VariantUpdate_productVariantUpdate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -142,11 +176,10 @@ export interface VariantUpdate_productVariantUpdate_productVariant { metadata: (VariantUpdate_productVariantUpdate_productVariant_metadata | null)[]; privateMetadata: (VariantUpdate_productVariantUpdate_productVariant_privateMetadata | null)[]; attributes: VariantUpdate_productVariantUpdate_productVariant_attributes[]; - costPrice: VariantUpdate_productVariantUpdate_productVariant_costPrice | null; images: (VariantUpdate_productVariantUpdate_productVariant_images | null)[] | null; name: string; - price: VariantUpdate_productVariantUpdate_productVariant_price | null; product: VariantUpdate_productVariantUpdate_productVariant_product; + channelListings: VariantUpdate_productVariantUpdate_productVariant_channelListings[] | null; sku: string; stocks: (VariantUpdate_productVariantUpdate_productVariant_stocks | null)[] | null; trackInventory: boolean; @@ -207,24 +240,12 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_attribu values: (VariantUpdate_productVariantStocksUpdate_productVariant_attributes_values | null)[]; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_costPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantUpdate_productVariantStocksUpdate_productVariant_images { __typename: "ProductImage"; id: string; url: string; } -export interface VariantUpdate_productVariantStocksUpdate_productVariant_price { - __typename: "Money"; - amount: number; - currency: string; -} - export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_defaultVariant { __typename: "ProductVariant"; id: string; @@ -243,6 +264,25 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_product url: string; } +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_discountedPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings { + __typename: "ProductChannelListing"; + channel: VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel; + discountedPrice: VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_discountedPrice | null; +} + export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_variants_images { __typename: "ProductImage"; id: string; @@ -264,9 +304,36 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_product images: (VariantUpdate_productVariantStocksUpdate_productVariant_product_images | null)[] | null; name: string; thumbnail: VariantUpdate_productVariantStocksUpdate_productVariant_product_thumbnail | null; + channelListings: VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings[] | null; variants: (VariantUpdate_productVariantStocksUpdate_productVariant_product_variants | null)[] | null; } +export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings { + __typename: "ProductVariantChannelListing"; + channel: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_channel; + price: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_price | null; + costPrice: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice | null; +} + export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse { __typename: "Warehouse"; id: string; @@ -293,11 +360,10 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant { metadata: (VariantUpdate_productVariantStocksUpdate_productVariant_metadata | null)[]; privateMetadata: (VariantUpdate_productVariantStocksUpdate_productVariant_privateMetadata | null)[]; attributes: VariantUpdate_productVariantStocksUpdate_productVariant_attributes[]; - costPrice: VariantUpdate_productVariantStocksUpdate_productVariant_costPrice | null; images: (VariantUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; name: string; - price: VariantUpdate_productVariantStocksUpdate_productVariant_price | null; product: VariantUpdate_productVariantStocksUpdate_productVariant_product; + channelListings: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings[] | null; sku: string; stocks: (VariantUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null; trackInventory: boolean; @@ -387,8 +453,6 @@ export interface VariantUpdateVariables { removeStocks: string[]; id: string; attributes?: (AttributeValueInput | null)[] | null; - costPrice?: any | null; - price?: any | null; sku?: string | null; trackInventory: boolean; stocks: StockInput[]; diff --git a/src/products/types/productBulkPublish.ts b/src/products/types/productBulkPublish.ts index f429764a4..f34853adc 100644 --- a/src/products/types/productBulkPublish.ts +++ b/src/products/types/productBulkPublish.ts @@ -25,5 +25,4 @@ export interface productBulkPublish { export interface productBulkPublishVariables { ids: string[]; - isPublished: boolean; } diff --git a/src/products/urls.ts b/src/products/urls.ts index 859229d35..0040f887f 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -20,8 +20,7 @@ export const productAddUrl = productAddPath; export const productListPath = productSection; export type ProductListUrlDialog = - | "publish" - | "unpublish" + | "settings" | "delete" | "export" | TabActionDialog; diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 6bc8bc719..5b32e2fdb 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -1,3 +1,4 @@ +import { ChannelData } from "@saleor/channels/utils"; import { MetadataFormData } from "@saleor/components/Metadata/types"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; @@ -168,48 +169,45 @@ export function getChoices(nodes: Node[]): SingleAutocompleteChoiceType[] { } export interface ProductUpdatePageFormData extends MetadataFormData { - availableForPurchase: string; basePrice: number; category: string | null; changeTaxCode: boolean; + channelListings: ChannelData[]; chargeTaxes: boolean; collections: string[]; isAvailable: boolean; - isAvailableForPurchase: boolean; - isPublished: boolean; name: string; slug: string; - publicationDate: string; seoDescription: string; seoTitle: string; sku: string; taxCode: string; trackInventory: boolean; - visibleInListings: boolean; weight: string; } export function getProductUpdatePageFormData( product: ProductDetails_product, - variants: ProductDetails_product_variants[] + variants: ProductDetails_product_variants[], + currentChannels: ChannelData[] ): ProductUpdatePageFormData { return { - availableForPurchase: product?.availableForPurchase, - basePrice: maybe(() => product.variants[0].price.amount, 0), + basePrice: maybe( + () => product.channelListings[0].discountedPrice.amount, + 0 + ), category: maybe(() => product.category.id, ""), changeTaxCode: !!product?.taxType.taxCode, + channelListings: currentChannels, chargeTaxes: maybe(() => product.chargeTaxes, false), collections: maybe( () => product.collections.map(collection => collection.id), [] ), isAvailable: !!product?.isAvailable, - isAvailableForPurchase: !!product?.isAvailableForPurchase, - isPublished: maybe(() => product.isPublished, false), metadata: product?.metadata?.map(mapMetadataItemToInput), name: maybe(() => product.name, ""), privateMetadata: product?.privateMetadata?.map(mapMetadataItemToInput), - publicationDate: maybe(() => product.publicationDate, ""), seoDescription: maybe(() => product.seoDescription, ""), seoTitle: maybe(() => product.seoTitle, ""), sku: maybe( @@ -224,7 +222,6 @@ export function getProductUpdatePageFormData( slug: product?.slug || "", taxCode: product?.taxType.taxCode, trackInventory: !!product?.variants[0]?.trackInventory, - visibleInListings: !!product?.visibleInListings, weight: product?.weight?.value.toString() || "" }; } diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts index 9c7021d43..866e5813e 100644 --- a/src/products/utils/handlers.ts +++ b/src/products/utils/handlers.ts @@ -1,3 +1,8 @@ +import { + ChannelData, + ChannelPriceArgs, + ChannelPriceData +} from "@saleor/channels/utils"; import { FormChange } from "@saleor/hooks/useForm"; import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; import { toggle } from "@saleor/utils/lists"; @@ -15,6 +20,85 @@ export function createAttributeChangeHandler( }; } +export function createChannelsPriceChangeHandler( + channelListings: ChannelPriceData[], + updateChannels: (data: ChannelPriceData[]) => void, + triggerChange: () => void +) { + return (id: string, priceData: ChannelPriceArgs) => { + const { costPrice, price } = priceData; + const channelIndex = channelListings.findIndex( + channel => channel.id === id + ); + const channel = channelListings[channelIndex]; + + const updatedChannels = [ + ...channelListings.slice(0, channelIndex), + { + ...channel, + costPrice, + price + }, + ...channelListings.slice(channelIndex + 1) + ]; + updateChannels(updatedChannels); + triggerChange(); + }; +} + +export function createChannelsChangeHandler( + channelListings: ChannelData[], + updateChannels: (data: ChannelData[]) => void, + triggerChange: () => void +) { + return ( + id: string, + data: Omit + ) => { + const channelIndex = channelListings.findIndex( + channel => channel.id === id + ); + const channel = channelListings[channelIndex]; + + const updatedChannels = [ + ...channelListings.slice(0, channelIndex), + { + ...channel, + ...data + }, + ...channelListings.slice(channelIndex + 1) + ]; + updateChannels(updatedChannels); + triggerChange(); + }; +} + +export function createVariantChannelsChangeHandler( + channelListings: ChannelPriceData[], + setData: (data: ChannelPriceData[]) => void, + triggerChange: () => void +) { + return (id: string, priceData: ChannelPriceArgs) => { + const { costPrice, price } = priceData; + const channelIndex = channelListings.findIndex( + channel => channel.id === id + ); + const channel = channelListings[channelIndex]; + + const updatedChannels = [ + ...channelListings.slice(0, channelIndex), + { + ...channel, + costPrice, + price + }, + ...channelListings.slice(channelIndex + 1) + ]; + setData(updatedChannels); + triggerChange(); + }; +} + export function createAttributeMultiChangeHandler( changeAttributeData: FormsetChange, attributes: FormsetData, @@ -53,18 +137,34 @@ export function createProductTypeSelectHandler( }; } -interface ProductAvailabilityArgs { - availableForPurchase: string | null; - isAvailableForPurchase: boolean; - productId: string; -} +export const getChannelsInput = (channels: ChannelPriceData[]) => + channels?.map(channel => ({ + data: channel, + id: channel.id, + label: channel.name, + value: { + costPrice: channel.costPrice || "", + price: channel.price || "" + } + })); -export const getProductAvailabilityVariables = ({ - isAvailableForPurchase, - availableForPurchase, - productId -}: ProductAvailabilityArgs) => ({ - isAvailable: isAvailableForPurchase, - productId, - startDate: availableForPurchase || null -}); +export const getAvailabilityVariables = (channels: ChannelData[]) => + channels.map(channel => { + const { isAvailableForPurchase, availableForPurchase } = channel; + const isAvailable = + availableForPurchase && !isAvailableForPurchase + ? true + : isAvailableForPurchase; + + return { + availableForPurchaseDate: + isAvailableForPurchase || availableForPurchase === "" + ? null + : availableForPurchase, + channelId: channel.id, + isAvailableForPurchase: isAvailable, + isPublished: channel.isPublished, + publicationDate: channel.publicationDate, + visibleInListings: channel.visibleInListings + }; + }); diff --git a/src/products/utils/validation.ts b/src/products/utils/validation.ts new file mode 100644 index 000000000..ebaa9dd15 --- /dev/null +++ b/src/products/utils/validation.ts @@ -0,0 +1,5 @@ +export const validatePrice = (price: string) => + price === "" || parseInt(price, 10) < 0; + +export const validateCostPrice = (price: string) => + price !== "" && parseInt(price, 10) < 0; diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate/ProductCreate.tsx similarity index 50% rename from src/products/views/ProductCreate.tsx rename to src/products/views/ProductCreate/ProductCreate.tsx index ed3918d8c..a2474630c 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate/ProductCreate.tsx @@ -1,13 +1,25 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { ChannelData, createSortedChannelsData } from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; -import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers"; +import ProductCreatePage from "@saleor/products/components/ProductCreatePage"; +import { + useProductChannelListingUpdate, + useProductVariantChannelListingUpdate, + useVariantCreateMutation +} from "@saleor/products/mutations"; +import { useProductCreateMutation } from "@saleor/products/mutations"; +import { productListUrl, productUrl } from "@saleor/products/urls"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { useTaxTypeList } from "@saleor/taxes/queries"; +import { getProductErrorMessage } from "@saleor/utils/errors"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -18,14 +30,7 @@ import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; -import { decimal, weight } from "../../misc"; -import ProductCreatePage from "../components/ProductCreatePage"; -import { ProductCreateData } from "../components/ProductCreatePage/form"; -import { - useProductCreateMutation, - useProductSetAvailabilityForPurchase -} from "../mutations"; -import { productListUrl, productUrl } from "../urls"; +import { createHandler } from "./handlers"; export const ProductCreateView: React.FC = () => { const navigate = useNavigator(); @@ -63,89 +68,79 @@ export const ProductCreateView: React.FC = () => { const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const taxTypes = useTaxTypeList({}); + const productTypes = searchProductTypesOpts?.data?.search?.edges?.map( + edge => edge.node + ); + + const { data: channelsData } = useChannelsList({}); + const allChannels: ChannelData[] = createSortedChannelsData( + channelsData?.channels + ); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(allChannels); + + const handleSuccess = (productId: string) => { + notify({ + status: "success", + text: intl.formatMessage({ + defaultMessage: "Product created" + }) + }); + navigate(productUrl(productId)); + }; + + const [updateChannels, updateChannelsOpts] = useProductChannelListingUpdate({ + onCompleted: data => { + const productId = data.productChannelListingUpdate.product.id; + if (productId) { + handleSuccess(productId); + } + } + }); + const [ + updateVariantChannels, + updateVariantChannelsOpts + ] = useProductVariantChannelListingUpdate({}); + const handleBack = () => navigate(productListUrl()); + const [productCreate, productCreateOpts] = useProductCreateMutation({}); const [ - setProductAvailability, - productAvailabilityOpts - ] = useProductSetAvailabilityForPurchase({ + productVariantCreate, + productVariantCreateOpts + ] = useVariantCreateMutation({ onCompleted: data => { - const errors = data?.productSetAvailabilityForPurchase?.errors; - if (errors?.length === 0) { - navigate(productUrl(data.productSetAvailabilityForPurchase.product.id)); - } - } - }); - - const [productCreate, productCreateOpts] = useProductCreateMutation({ - onCompleted: data => { - if (data.productCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage({ - defaultMessage: "Product created" + const errors = data.productVariantCreate.errors; + if (errors.length) { + errors.map(error => + notify({ + status: "error", + text: getProductErrorMessage(error, intl) }) - }); + ); } } }); - const handleCreate = async (formData: ProductCreateData) => { - const result = await productCreate({ - variables: { - input: { - attributes: formData.attributes.map(attribute => ({ - id: attribute.id, - values: attribute.value - })), - basePrice: decimal(formData.basePrice), - category: formData.category, - chargeTaxes: formData.chargeTaxes, - collections: formData.collections, - descriptionJson: JSON.stringify(formData.description), - isPublished: formData.isPublished, - name: formData.name, - productType: formData.productType?.id, - publicationDate: - formData.publicationDate !== "" ? formData.publicationDate : null, - seo: { - description: formData.seoDescription, - title: formData.seoTitle - }, - sku: formData.sku, - slug: formData.slug, - stocks: formData.stocks.map(stock => ({ - quantity: parseInt(stock.value, 0), - warehouse: stock.id - })), - taxCode: formData.changeTaxCode ? formData.taxCode : undefined, - trackInventory: formData.trackInventory, - visibleInListings: formData.visibleInListings, - weight: weight(formData.weight) - } - } - }); - - const productId = result.data.productCreate?.product?.id; - - if (productId) { - const { isAvailableForPurchase, availableForPurchase } = formData; - - const variables = getProductAvailabilityVariables({ - availableForPurchase, - isAvailableForPurchase, - productId - }); - - setProductAvailability({ - variables - }); - } - - return productId || null; - }; const handleSubmit = createMetadataCreateHandler( - handleCreate, + createHandler( + productTypes, + variables => productCreate({ variables }), + variables => productVariantCreate({ variables }), + updateChannels, + updateVariantChannels + ), updateMetadata, updatePrivateMetadata ); @@ -158,16 +153,46 @@ export const ProductCreateView: React.FC = () => { description: "window title" })} /> + {!!allChannels?.length && ( + + )} edge.node )} - collections={(searchCollectionOpts.data?.search.edges || []).map( + collections={(searchCollectionOpts?.data?.search?.edges || []).map( edge => edge.node )} - disabled={productCreateOpts.loading || productAvailabilityOpts.loading} - errors={productCreateOpts.data?.productCreate.errors || []} + disabled={ + productCreateOpts.loading || + productVariantCreateOpts.loading || + updateChannelsOpts.loading || + updateVariantChannelsOpts.loading + } + channelsErrors={ + updateVariantChannelsOpts.data?.productVariantChannelListingUpdate + ?.errors + } + errors={[ + ...(productCreateOpts.data?.productCreate.errors || []), + ...(productVariantCreateOpts.data?.productVariantCreate.errors || []) + ]} fetchCategories={searchCategory} fetchCollections={searchCollection} fetchProductTypes={searchProductTypes} @@ -175,9 +200,7 @@ export const ProductCreateView: React.FC = () => { defaultMessage: "New Product", description: "page header" })} - productTypes={searchProductTypesOpts.data?.search.edges.map( - edge => edge.node - )} + productTypes={productTypes} onBack={handleBack} onSubmit={handleSubmit} onWarehouseConfigure={() => navigate(warehouseAddPath)} @@ -202,6 +225,8 @@ export const ProductCreateView: React.FC = () => { } taxTypes={taxTypes.data?.taxTypes || []} weightUnit={shop?.defaultWeightUnit} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} /> ); diff --git a/src/products/views/ProductCreate/handlers.ts b/src/products/views/ProductCreate/handlers.ts new file mode 100644 index 000000000..024033cb4 --- /dev/null +++ b/src/products/views/ProductCreate/handlers.ts @@ -0,0 +1,120 @@ +import { ChannelData } from "@saleor/channels/utils"; +import { weight } from "@saleor/misc"; +import { ProductCreateData } from "@saleor/products/components/ProductCreatePage/form"; +import { + ProductChannelListingUpdate, + ProductChannelListingUpdateVariables +} from "@saleor/products/types/ProductChannelListingUpdate"; +import { + ProductCreate, + ProductCreateVariables +} from "@saleor/products/types/ProductCreate"; +import { + ProductVariantChannelListingUpdate, + ProductVariantChannelListingUpdateVariables +} from "@saleor/products/types/ProductVariantChannelListingUpdate"; +import { + VariantCreate, + VariantCreateVariables +} from "@saleor/products/types/VariantCreate"; +import { getAvailabilityVariables } from "@saleor/products/utils/handlers"; +import { SearchProductTypes_search_edges_node } from "@saleor/searches/types/SearchProductTypes"; +import { MutationFetchResult } from "react-apollo"; + +const getChannelsVariables = (productId: string, channels: ChannelData[]) => ({ + variables: { + id: productId, + input: { + addChannels: getAvailabilityVariables(channels) + } + } +}); + +const getSimpleProductVariables = ( + formData: ProductCreateData, + productId: string +) => ({ + input: { + attributes: [], + product: productId, + sku: formData.sku, + stocks: formData.stocks?.map(stock => ({ + quantity: parseInt(stock.value, 10), + warehouse: stock.id + })), + trackInventory: formData.trackInventory + } +}); + +export function createHandler( + productTypes: SearchProductTypes_search_edges_node[], + productCreate: ( + variables: ProductCreateVariables + ) => Promise>, + productVariantCreate: ( + variables: VariantCreateVariables + ) => Promise>, + updateChannels: (options: { + variables: ProductChannelListingUpdateVariables; + }) => Promise>, + updateVariantChannels: (options: { + variables: ProductVariantChannelListingUpdateVariables; + }) => Promise> +) { + return async (formData: ProductCreateData) => { + const productVariables: ProductCreateVariables = { + input: { + attributes: formData.attributes.map(attribute => ({ + id: attribute.id, + values: attribute.value + })), + category: formData.category, + chargeTaxes: formData.chargeTaxes, + collections: formData.collections, + descriptionJson: JSON.stringify(formData.description), + name: formData.name, + productType: formData.productType?.id, + seo: { + description: formData.seoDescription, + title: formData.seoTitle + }, + slug: formData.slug, + taxCode: formData.changeTaxCode ? formData.taxCode : undefined, + weight: weight(formData.weight) + } + }; + + const result = await productCreate(productVariables); + + const hasVariants = productTypes.find( + product => product.id === formData.productType.id + ).hasVariants; + const productId = result.data.productCreate.product.id; + + if (!hasVariants) { + const result = await Promise.all([ + updateChannels( + getChannelsVariables(productId, formData.channelListings) + ), + productVariantCreate(getSimpleProductVariables(formData, productId)) + ]); + const variantErrors = result[1].data.productVariantCreate.errors; + const variantId = result[1].data.productVariantCreate.productVariant?.id; + if (variantErrors.length === 0 && variantId) { + updateVariantChannels({ + variables: { + id: variantId, + input: formData.channelListings.map(listing => ({ + channelId: listing.id, + costPrice: listing.costPrice || null, + price: listing.price + })) + } + }); + } + } else { + updateChannels(getChannelsVariables(productId, formData.channelListings)); + } + return productId || null; + }; +} diff --git a/src/products/views/ProductCreate/index.ts b/src/products/views/ProductCreate/index.ts new file mode 100644 index 000000000..7764d5051 --- /dev/null +++ b/src/products/views/ProductCreate/index.ts @@ -0,0 +1,2 @@ +export * from "./ProductCreate"; +export { default } from "./ProductCreate"; diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index e25ca42eb..836998057 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -1,7 +1,7 @@ -import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog"; import ActionDialog from "@saleor/components/ActionDialog"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { @@ -16,13 +16,13 @@ import { import { Task } from "@saleor/containers/BackgroundTasks/types"; import useBackgroundTask from "@saleor/hooks/useBackgroundTask"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannelsSettings from "@saleor/hooks/useChannelsSettings"; import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import ProductExportDialog from "@saleor/products/components/ProductExportDialog"; @@ -30,7 +30,21 @@ import { getAttributeIdFromColumnValue, isAttributeColumnValue } from "@saleor/products/components/ProductListPage/utils"; +import { + useAvailableInGridAttributesQuery, + useCountAllProducts, + useInitialProductFilterDataQuery, + useProductListQuery +} from "@saleor/products/queries"; import { ProductListVariables } from "@saleor/products/types/ProductList"; +import { + productAddUrl, + productListUrl, + ProductListUrlDialog, + ProductListUrlQueryParams, + ProductListUrlSortField, + productUrl +} from "@saleor/products/urls"; import useAttributeSearch from "@saleor/searches/useAttributeSearch"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; @@ -46,23 +60,8 @@ import { FormattedMessage, useIntl } from "react-intl"; import ProductListPage from "../../components/ProductListPage"; import { useProductBulkDeleteMutation, - useProductBulkPublishMutation, useProductExport } from "../../mutations"; -import { - useAvailableInGridAttributesQuery, - useCountAllProducts, - useInitialProductFilterDataQuery, - useProductListQuery -} from "../../queries"; -import { - productAddUrl, - productListUrl, - ProductListUrlDialog, - ProductListUrlQueryParams, - ProductListUrlSortField, - productUrl -} from "../../urls"; import { areFiltersApplied, deleteFilterTab, @@ -84,7 +83,6 @@ export const ProductList: React.FC = ({ params }) => { const notify = useNotifier(); const paginate = usePaginator(); const { queue } = useBackgroundTask(); - const shop = useShop(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); @@ -129,17 +127,32 @@ export const ProductList: React.FC = ({ params }) => { } }); - React.useEffect( - () => - navigate( - productListUrl({ - ...params, - ...DEFAULT_INITIAL_PAGINATION_DATA - }), - true - ), - [settings.rowNumber] - ); + const [openModal, closeModal] = createDialogActionHandlers< + ProductListUrlDialog, + ProductListUrlQueryParams + >(navigate, productListUrl, params); + + const { + channel, + channels, + channelChoices, + handleChannelSelectConfirm, + selectedChannel, + slug + } = useChannelsSettings("productsListChannel", { closeModal, openModal }); + + React.useEffect(() => { + const action = selectedChannel + ? {} + : { action: "settings" as ProductListUrlDialog }; + navigate( + productListUrl({ + ...{ ...params, ...action }, + ...DEFAULT_INITIAL_PAGINATION_DATA + }), + true + ); + }, [settings.rowNumber]); const tabs = getFilterTabs(); @@ -150,10 +163,6 @@ export const ProductList: React.FC = ({ params }) => { : 0 : parseInt(params.activeTab, 0); - const [openModal, closeModal] = createDialogActionHandlers< - ProductListUrlDialog, - ProductListUrlQueryParams - >(navigate, productListUrl, params); const countAllProducts = useCountAllProducts({}); const [exportProducts, exportProductsOpts] = useProductExport({ @@ -222,14 +231,13 @@ export const ProductList: React.FC = ({ params }) => { ); const paginationState = createPaginationState(settings.rowNumber, params); - const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); - const filter = getFilterVariables(params); - const sort = getSortQueryVariables(params); + const filter = getFilterVariables(params, slug); + const sort = getSortQueryVariables(params, slug); const queryVariables = React.useMemo( () => ({ ...paginationState, filter, - sort + ...(slug ? { sort } : {}) }), [params, settings.rowNumber] ); @@ -264,51 +272,28 @@ export const ProductList: React.FC = ({ params }) => { } }); - const [ - productBulkPublish, - productBulkPublishOpts - ] = useProductBulkPublishMutation({ - onCompleted: data => { - if (data.productBulkPublish.errors.length === 0) { - closeModal(); - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - reset(); - refetch(); - } - } - }); - const filterOpts = getFilterOpts( params, - maybe(() => initialFilterData.attributes.edges.map(edge => edge.node), []), + initialFilterData?.attributes?.edges?.map(edge => edge.node) || [], { - initial: maybe( - () => initialFilterData.categories.edges.map(edge => edge.node), - [] - ), + initial: + initialFilterData?.categories?.edges?.map(edge => edge.node) || [], search: searchCategories }, { - initial: maybe( - () => initialFilterData.collections.edges.map(edge => edge.node), - [] - ), + initial: + initialFilterData?.collections?.edges?.map(edge => edge.node) || [], search: searchCollections }, { - initial: maybe( - () => initialFilterData.productTypes.edges.map(edge => edge.node), - [] - ), + initial: + initialFilterData?.productTypes?.edges?.map(edge => edge.node) || [], search: searchProductTypes } ); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( - maybe(() => data.products.pageInfo), + data?.products?.pageInfo, paginationState, params ); @@ -326,7 +311,7 @@ export const ProductList: React.FC = ({ params }) => { () => attributes.data.availableInGrid.edges.map(edge => edge.node), [] )} - currencySymbol={currencySymbol} + currencySymbol={channel?.currencyCode} currentTab={currentTab} defaultSettings={defaultListSettings[ListViews.PRODUCT_LIST]} filterOpts={filterOpts} @@ -380,44 +365,16 @@ export const ProductList: React.FC = ({ params }) => { onRowClick={id => () => navigate(productUrl(id))} onAll={resetFilters} toolbar={ - <> - - - - openModal("delete", { - ids: listElements - }) - } - > - - - + + openModal("delete", { + ids: listElements + }) + } + > + + } isChecked={isSelected} selected={listElements.length} @@ -431,7 +388,22 @@ export const ProductList: React.FC = ({ params }) => { initialSearch={params.query || ""} tabs={getFilterTabs().map(tab => tab.name)} onExport={() => openModal("export")} + channelsCount={channelChoices?.length} + selectedChannel={selectedChannel} + onSettingsOpen={ + !!channelChoices?.length ? () => openModal("settings") : undefined + } /> + {!!channelChoices?.length && ( + + )} = ({ params }) => { defaultMessage="{counter,plural,one{Are you sure you want to delete this product?} other{Are you sure you want to delete {displayQuantity} products?}}" description="dialog content" values={{ - counter: maybe(() => params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} - }} - /> - - - - productBulkPublish({ - variables: { - ids: params.ids, - isPublished: true - } - }) - } - title={intl.formatMessage({ - defaultMessage: "Publish Products", - description: "dialog header" - })} - > - - params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} - }} - /> - - - - productBulkPublish({ - variables: { - ids: params.ids, - isPublished: false - } - }) - } - title={intl.formatMessage({ - defaultMessage: "Unpublish Products", - description: "dialog header" - })} - > - - params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} + counter: params?.ids?.length, + displayQuantity: {params?.ids?.length} }} /> @@ -533,6 +449,7 @@ export const ProductList: React.FC = ({ params }) => { warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } + channels={channels} onClose={closeModal} onSubmit={data => exportProducts({ diff --git a/src/products/views/ProductList/__snapshots__/filters.test.ts.snap b/src/products/views/ProductList/__snapshots__/filters.test.ts.snap index 7b7084a87..6a24a8073 100644 --- a/src/products/views/ProductList/__snapshots__/filters.test.ts.snap +++ b/src/products/views/ProductList/__snapshots__/filters.test.ts.snap @@ -63,9 +63,8 @@ Object { "productTypes": Array [ "UHJvZHVjdFR5cGU6MQ==", ], - "status": "published", "stockStatus": "IN_STOCK", } `; -exports[`Filtering URL params should not be empty if active filters are present 2`] = `"status=published&stockStatus=IN_STOCK&priceFrom=10&priceTo=20&categories%5B0%5D=878752&collections%5B0%5D=Q29sbGVjdGlvbjoc&productTypes%5B0%5D=UHJvZHVjdFR5cGU6MQ%3D%3D&attributes%5Bauthor%5D%5B0%5D=john-doe&attributes%5Bauthor%5D%5B1%5D=false&attributes%5Bbox-size%5D%5B0%5D=100g&attributes%5Bbox-size%5D%5B1%5D=500g&attributes%5Bbrand%5D%5B0%5D=saleor&attributes%5Bbrand%5D%5B1%5D=false&attributes%5Bcandy-box-size%5D%5B0%5D=100g&attributes%5Bcandy-box-size%5D%5B1%5D=500g&attributes%5Bcoffee-genre%5D%5B0%5D=arabica&attributes%5Bcoffee-genre%5D%5B1%5D=false&attributes%5Bcollar%5D%5B0%5D=round&attributes%5Bcollar%5D%5B1%5D=polo&attributes%5Bcolor%5D%5B0%5D=blue&attributes%5Bcolor%5D%5B1%5D=false&attributes%5Bcover%5D%5B0%5D=soft&attributes%5Bcover%5D%5B1%5D=middle-soft&attributes%5Bflavor%5D%5B0%5D=sour&attributes%5Bflavor%5D%5B1%5D=false&attributes%5Blanguage%5D%5B0%5D=english&attributes%5Blanguage%5D%5B1%5D=false&attributes%5Bpublisher%5D%5B0%5D=mirumee-press&attributes%5Bpublisher%5D%5B1%5D=false&attributes%5Bsize%5D%5B0%5D=xs&attributes%5Bsize%5D%5B1%5D=m"`; +exports[`Filtering URL params should not be empty if active filters are present 2`] = `"stockStatus=IN_STOCK&priceFrom=10&priceTo=20&categories%5B0%5D=878752&collections%5B0%5D=Q29sbGVjdGlvbjoc&productTypes%5B0%5D=UHJvZHVjdFR5cGU6MQ%3D%3D&attributes%5Bauthor%5D%5B0%5D=john-doe&attributes%5Bauthor%5D%5B1%5D=false&attributes%5Bbox-size%5D%5B0%5D=100g&attributes%5Bbox-size%5D%5B1%5D=500g&attributes%5Bbrand%5D%5B0%5D=saleor&attributes%5Bbrand%5D%5B1%5D=false&attributes%5Bcandy-box-size%5D%5B0%5D=100g&attributes%5Bcandy-box-size%5D%5B1%5D=500g&attributes%5Bcoffee-genre%5D%5B0%5D=arabica&attributes%5Bcoffee-genre%5D%5B1%5D=false&attributes%5Bcollar%5D%5B0%5D=round&attributes%5Bcollar%5D%5B1%5D=polo&attributes%5Bcolor%5D%5B0%5D=blue&attributes%5Bcolor%5D%5B1%5D=false&attributes%5Bcover%5D%5B0%5D=soft&attributes%5Bcover%5D%5B1%5D=middle-soft&attributes%5Bflavor%5D%5B0%5D=sour&attributes%5Bflavor%5D%5B1%5D=false&attributes%5Blanguage%5D%5B0%5D=english&attributes%5Blanguage%5D%5B1%5D=false&attributes%5Bpublisher%5D%5B0%5D=mirumee-press&attributes%5Bpublisher%5D%5B1%5D=false&attributes%5Bsize%5D%5B0%5D=xs&attributes%5Bsize%5D%5B1%5D=m"`; diff --git a/src/products/views/ProductList/filters.test.ts b/src/products/views/ProductList/filters.test.ts index 4a55b212a..8da8539cc 100644 --- a/src/products/views/ProductList/filters.test.ts +++ b/src/products/views/ProductList/filters.test.ts @@ -13,7 +13,7 @@ import { productListFilterOpts } from "./fixtures"; describe("Filtering query params", () => { it("should be empty object if no params given", () => { const params: ProductListUrlFilters = {}; - const filterVariables = getFilterVariables(params); + const filterVariables = getFilterVariables(params, undefined); expect(getExistingKeys(filterVariables)).toHaveLength(0); }); @@ -25,7 +25,7 @@ describe("Filtering query params", () => { status: true.toString(), stockStatus: StockAvailability.IN_STOCK }; - const filterVariables = getFilterVariables(params); + const filterVariables = getFilterVariables(params, "default-channel"); expect(getExistingKeys(filterVariables)).toHaveLength(3); }); diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 44f6701d6..32c5891f8 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -2,8 +2,7 @@ import { UseSearchResult } from "@saleor/hooks/makeSearch"; import { findValueInEnum, maybe } from "@saleor/misc"; import { ProductFilterKeys, - ProductListFilterOpts, - ProductStatus + ProductListFilterOpts } from "@saleor/products/components/ProductListPage"; import { InitialProductFilterData_attributes_edges_node, @@ -182,10 +181,6 @@ export function getFilterOpts( onSearchChange: productTypes.search.search, value: maybe(() => dedupeFilter(params.productTypes), []) }, - status: { - active: maybe(() => params.status !== undefined, false), - value: maybe(() => findValueInEnum(params.status, ProductStatus)) - }, stockStatus: { active: maybe(() => params.stockStatus !== undefined, false), value: maybe(() => findValueInEnum(params.stockStatus, StockAvailability)) @@ -194,7 +189,8 @@ export function getFilterOpts( } export function getFilterVariables( - params: ProductListUrlFilters + params: ProductListUrlFilters, + channel: string | undefined ): ProductFilterInput { return { attributes: !!params.attributes @@ -207,15 +203,14 @@ export function getFilterVariables( })) : null, categories: params.categories !== undefined ? params.categories : null, + channel: channel || null, collections: params.collections !== undefined ? params.collections : null, - isPublished: - params.status !== undefined - ? params.status === ProductStatus.PUBLISHED - : null, - price: getGteLteVariables({ - gte: parseFloat(params.priceFrom), - lte: parseFloat(params.priceTo) - }), + price: channel + ? getGteLteVariables({ + gte: parseFloat(params.priceFrom), + lte: parseFloat(params.priceTo) + }) + : null, productTypes: params.productTypes !== undefined ? params.productTypes : null, search: params.query, @@ -271,13 +266,6 @@ export function getFilterQueryParam( ProductListUrlFiltersWithMultipleValues.productTypes ); - case ProductFilterKeys.status: - return getSingleEnumValueQueryParam( - filter, - ProductListUrlFiltersEnum.status, - ProductStatus - ); - case ProductFilterKeys.stock: return getSingleEnumValueQueryParam( filter, diff --git a/src/products/views/ProductList/fixtures.ts b/src/products/views/ProductList/fixtures.ts index ad31a116c..3e72e83a1 100644 --- a/src/products/views/ProductList/fixtures.ts +++ b/src/products/views/ProductList/fixtures.ts @@ -2,10 +2,7 @@ import { attributes } from "@saleor/attributes/fixtures"; import { categories } from "@saleor/categories/fixtures"; import { collections } from "@saleor/collections/fixtures"; import { fetchMoreProps, searchPageProps } from "@saleor/fixtures"; -import { - ProductListFilterOpts, - ProductStatus -} from "@saleor/products/components/ProductListPage"; +import { ProductListFilterOpts } from "@saleor/products/components/ProductListPage"; import { productTypes } from "@saleor/productTypes/fixtures"; import { StockAvailability } from "@saleor/types/globalTypes"; @@ -75,10 +72,6 @@ export const productListFilterOpts: ProductListFilterOpts = { ], value: [productTypes[4].id] }, - status: { - active: false, - value: ProductStatus.PUBLISHED - }, stockStatus: { active: false, value: StockAvailability.IN_STOCK diff --git a/src/products/views/ProductList/sort.ts b/src/products/views/ProductList/sort.ts index 893190925..b3e431228 100644 --- a/src/products/views/ProductList/sort.ts +++ b/src/products/views/ProductList/sort.ts @@ -23,7 +23,8 @@ export function getSortQueryField( } export function getSortQueryVariables( - params: ProductListUrlQueryParams + params: ProductListUrlQueryParams, + channel: string ): ProductOrder { if (params.sort === ProductListUrlSortField.attribute) { return { @@ -32,6 +33,7 @@ export function getSortQueryVariables( }; } return { + channel, direction: getOrderDirection(params.asc), field: getSortQueryField(params.sort) }; diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 294db7989..d25d5febc 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -2,27 +2,36 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; +import { useChannelsList } from "@saleor/channels/queries"; +import { + ChannelData, + createChannelsDataWithPrice, + createSortedChannelsDataFromProduct +} from "@saleor/channels/utils"; import ActionDialog from "@saleor/components/ActionDialog"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useOnSetDefaultVariant from "@saleor/hooks/useOnSetDefaultVariant"; import useShop from "@saleor/hooks/useShop"; -import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { commonMessages } from "@saleor/intl"; import { + useProductChannelListingUpdate, useProductDeleteMutation, useProductImageCreateMutation, useProductImageDeleteMutation, useProductImagesReorder, - useProductSetAvailabilityForPurchase, useProductUpdateMutation, useProductVariantBulkDeleteMutation, + useProductVariantChannelListingUpdate, useProductVariantReorderMutation, - useSimpleProductUpdateMutation + useSimpleProductUpdateMutation, + useVariantCreateMutation } from "@saleor/products/mutations"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; @@ -37,7 +46,7 @@ import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { getMutationState, maybe } from "../../../misc"; +import { getMutationState } from "../../../misc"; import ProductUpdatePage from "../../components/ProductUpdatePage"; import { useProductDetails } from "../../queries"; import { ProductImageCreateVariables } from "../../types/ProductImageCreate"; @@ -94,12 +103,16 @@ export const ProductUpdate: React.FC = ({ id, params }) => { const shop = useShop(); const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + const [ + productVariantCreate, + productVariantCreateOpts + ] = useVariantCreateMutation({}); + + const { data: channelsData } = useChannelsList({}); const { data, loading, refetch } = useProductDetails({ displayLoader: true, - variables: { - id - } + variables: { id } }); const handleUpdate = (data: ProductUpdateMutationResult) => { @@ -175,29 +188,50 @@ export const ProductUpdate: React.FC = ({ id, params }) => { } }); - const [ - setProductAvailability, - productAvailabilityOpts - ] = useProductSetAvailabilityForPurchase({ + const product = data?.product; + + const allChannels: ChannelData[] = createChannelsDataWithPrice( + product, + channelsData?.channels + ).sort((channel, nextChannel) => + channel.name.localeCompare(nextChannel.name) + ); + + const productChannelsChoices: ChannelData[] = createSortedChannelsDataFromProduct( + product + ); + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(productChannelsChoices); + + const [updateChannels, updateChannelsOpts] = useProductChannelListingUpdate({ onCompleted: data => { - const errors = data?.productSetAvailabilityForPurchase?.errors; - if (errors?.length === 0) { - const updatedProduct = data?.productSetAvailabilityForPurchase?.product; - setProduct(product => ({ - ...product, - availableForPurchase: updatedProduct.availableForPurchase, - isAvailableForPurchase: updatedProduct.isAvailableForPurchase - })); - notify({ - status: "success", - text: intl.formatMessage({ - defaultMessage: "Product availability updated", - description: "snackbar text" - }) - }); + if (data.productChannelListingUpdate.errors.length === 0) { + const updatedProductChannelsChoices: ChannelData[] = createSortedChannelsDataFromProduct( + data.productChannelListingUpdate.product + ); + setCurrentChannels(updatedProductChannelsChoices); } } }); + const [ + updateVariantChannels, + updateVariantChannelsOpts + ] = useProductVariantChannelListingUpdate({}); + + const channelChoices = product?.channelListings.map(listing => ({ + label: listing.channel.name, + value: listing.channel.id + })); const [openModal, closeModal] = createDialogActionHandlers< ProductUrlDialog, @@ -206,12 +240,9 @@ export const ProductUpdate: React.FC = ({ id, params }) => { const handleBack = () => navigate(productListUrl()); - const [product, setProduct] = useStateFromProps(data?.product); - if (product === null) { return ; } - const handleVariantAdd = () => navigate(productVariantAddUrl(id)); const handleImageDelete = (id: string) => () => @@ -224,11 +255,14 @@ export const ProductUpdate: React.FC = ({ id, params }) => { product, variables => updateProduct({ variables }), variables => updateSimpleProduct({ variables }), - variables => setProductAvailability({ variables }) + updateChannels, + updateVariantChannels, + productVariantCreate ), variables => updateMetadata({ variables }), variables => updatePrivateMetadata({ variables }) ); + const handleImageUpload = createImageUploadHandler(id, variables => createProductImage({ variables }) ); @@ -250,57 +284,87 @@ export const ProductUpdate: React.FC = ({ id, params }) => { deleteProductOpts.loading || reorderProductImagesOpts.loading || updateProductOpts.loading || - productAvailabilityOpts.loading || reorderProductVariantsOpts.loading || + updateChannelsOpts.loading || + updateVariantChannelsOpts.loading || + productVariantCreateOpts.loading || loading; const formTransitionState = getMutationState( updateProductOpts.called || updateSimpleProductOpts.called, updateProductOpts.loading || updateSimpleProductOpts.loading, - maybe(() => updateProductOpts.data.productUpdate.errors), - maybe(() => updateSimpleProductOpts.data.productUpdate.errors), - maybe(() => updateSimpleProductOpts.data.productVariantUpdate.errors) + updateProductOpts.data?.productUpdate.errors, + updateSimpleProductOpts.data?.productUpdate.errors, + updateSimpleProductOpts.data?.productVariantUpdate.errors ); - const categories = maybe( - () => searchCategoriesOpts.data.search.edges, - [] - ).map(edge => edge.node); - const collections = maybe( - () => searchCollectionsOpts.data.search.edges, - [] - ).map(edge => edge.node); + const categories = (searchCategoriesOpts?.data?.search?.edges || []).map( + edge => edge.node + ); + const collections = (searchCollectionsOpts?.data?.search?.edges || []).map( + edge => edge.node + ); const errors = [ - ...maybe(() => updateProductOpts.data.productUpdate.errors, []), - ...maybe(() => updateSimpleProductOpts.data.productUpdate.errors, []) + ...(updateProductOpts.data?.productUpdate.errors || []), + ...(updateSimpleProductOpts.data?.productUpdate.errors || []), + ...(productVariantCreateOpts.data?.productVariantCreate.errors || []) ]; const onSetDefaultVariant = useOnSetDefaultVariant( product ? product.id : null, null ); + const channelsErrors = [ + ...(updateChannelsOpts?.data?.productChannelListingUpdate?.errors || []), + ...(updateVariantChannelsOpts?.data?.productVariantChannelListingUpdate + ?.errors || []) + ]; return ( <> - data.product.name)} /> + + {!!allChannels?.length && ( + + )} data.product.images)} - header={maybe(() => product.name)} + images={data?.product?.images} + header={product?.name} placeholderImage={placeholderImg} product={product} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } taxTypes={data?.taxTypes} - variants={maybe(() => product.variants)} + variants={product?.variants} onBack={handleBack} onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} @@ -331,19 +395,17 @@ export const ProductUpdate: React.FC = ({ id, params }) => { toggle={toggle} toggleAll={toggleAll} fetchMoreCategories={{ - hasMore: maybe( - () => searchCategoriesOpts.data.search.pageInfo.hasNextPage - ), + hasMore: searchCategoriesOpts?.data?.search?.pageInfo?.hasNextPage, loading: searchCategoriesOpts.loading, onFetchMore: loadMoreCategories }} fetchMoreCollections={{ - hasMore: maybe( - () => searchCollectionsOpts.data.search.pageInfo.hasNextPage - ), + hasMore: searchCollectionsOpts?.data?.search?.pageInfo?.hasNextPage, loading: searchCollectionsOpts.loading, onFetchMore: loadMoreCollections }} + openChannelsModal={handleChannelsModalOpen} + onChannelsChange={setCurrentChannels} /> = ({ id, params }) => { defaultMessage="{counter,plural,one{Are you sure you want to delete this variant?} other{Are you sure you want to delete {displayQuantity} variants?}}" description="dialog content" values={{ - counter: maybe(() => params.ids.length), - displayQuantity: {maybe(() => params.ids.length)} + counter: params?.ids?.length, + displayQuantity: {params?.ids?.length} }} /> diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index c68e32bf5..79d439494 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -1,19 +1,25 @@ +import { createSortedChannelsDataFromProduct } from "@saleor/channels/utils"; import { BulkStockErrorFragment } from "@saleor/fragments/types/BulkStockErrorFragment"; +import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { StockErrorFragment } from "@saleor/fragments/types/StockErrorFragment"; -import { decimal, weight } from "@saleor/misc"; +import { weight } from "@saleor/misc"; import { ProductUpdatePageSubmitData } from "@saleor/products/components/ProductUpdatePage"; +import { + ProductChannelListingUpdate, + ProductChannelListingUpdateVariables +} from "@saleor/products/types/ProductChannelListingUpdate"; import { ProductDetails_product } from "@saleor/products/types/ProductDetails"; import { ProductImageCreateVariables } from "@saleor/products/types/ProductImageCreate"; import { ProductImageReorderVariables } from "@saleor/products/types/ProductImageReorder"; -import { - ProductSetAvailabilityForPurchase, - ProductSetAvailabilityForPurchaseVariables -} from "@saleor/products/types/ProductSetAvailabilityForPurchase"; import { ProductUpdate, ProductUpdateVariables } from "@saleor/products/types/ProductUpdate"; +import { + ProductVariantChannelListingUpdate, + ProductVariantChannelListingUpdateVariables +} from "@saleor/products/types/ProductVariantChannelListingUpdate"; import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData"; import { ProductVariantDetails_productVariant_product } from "@saleor/products/types/ProductVariantDetails"; import { ProductVariantReorderVariables } from "@saleor/products/types/ProductVariantReorder"; @@ -21,12 +27,73 @@ import { SimpleProductUpdate, SimpleProductUpdateVariables } from "@saleor/products/types/SimpleProductUpdate"; +import { + VariantCreate, + VariantCreateVariables +} from "@saleor/products/types/VariantCreate"; import { mapFormsetStockToStockInput } from "@saleor/products/utils/data"; -import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers"; +import { getAvailabilityVariables } from "@saleor/products/utils/handlers"; import { ReorderEvent } from "@saleor/types"; +import { diff } from "fast-array-diff"; import { MutationFetchResult } from "react-apollo"; import { arrayMove } from "react-sortable-hoc"; +const getSimpleProductVariables = ( + productVariables: ProductUpdateVariables, + data: ProductUpdatePageSubmitData, + productId: string +) => ({ + ...productVariables, + addStocks: data.addStocks.map(mapFormsetStockToStockInput), + deleteStocks: data.removeStocks, + input: { + ...productVariables.input, + weight: weight(data.weight) + }, + productVariantId: productId, + productVariantInput: { + sku: data.sku, + trackInventory: data.trackInventory + }, + updateStocks: data.updateStocks.map(mapFormsetStockToStockInput) +}); + +const getSimpleProductErrors = (data: SimpleProductUpdate) => [ + ...data.productUpdate.errors, + ...data.productVariantStocksCreate.errors, + ...data.productVariantStocksDelete.errors, + ...data.productVariantStocksUpdate.errors +]; + +const getChannelsVariables = ( + data: ProductUpdatePageSubmitData, + product: ProductDetails_product +) => { + const productChannels = createSortedChannelsDataFromProduct(product); + const diffChannels = diff( + productChannels, + data.channelListings, + (a, b) => a.id === b.id + ); + + return { + id: product.id, + input: { + addChannels: getAvailabilityVariables(data.channelListings), + removeChannels: diffChannels.removed?.map( + removedChannel => removedChannel.id + ) + } + }; +}; + +const getVariantChannelsInput = (data: ProductUpdatePageSubmitData) => + data.channelListings.map(listing => ({ + channelId: listing.id, + costPrice: listing.costPrice || null, + price: listing.price + })); + export function createUpdateHandler( product: ProductDetails_product, updateProduct: ( @@ -35,9 +102,15 @@ export function createUpdateHandler( updateSimpleProduct: ( variables: SimpleProductUpdateVariables ) => Promise>, - setProductAvailability: ( - variables: ProductSetAvailabilityForPurchaseVariables - ) => Promise> + updateChannels: (options: { + variables: ProductChannelListingUpdateVariables; + }) => Promise>, + updateVariantChannels: (options: { + variables: ProductVariantChannelListingUpdateVariables; + }) => Promise>, + productVariantCreate: (options: { + variables: VariantCreateVariables; + }) => Promise> ) { return async (data: ProductUpdatePageSubmitData) => { const productVariables: ProductUpdateVariables = { @@ -47,72 +120,89 @@ export function createUpdateHandler( id: attribute.id, values: attribute.value[0] === "" ? [] : attribute.value })), - basePrice: decimal(data.basePrice), category: data.category, chargeTaxes: data.chargeTaxes, collections: data.collections, descriptionJson: JSON.stringify(data.description), - isPublished: data.isPublished, name: data.name, - publicationDate: - data.publicationDate !== "" ? data.publicationDate : null, seo: { description: data.seoDescription, title: data.seoTitle }, slug: data.slug, - taxCode: data.changeTaxCode ? data.taxCode : null, - visibleInListings: data.visibleInListings + taxCode: data.changeTaxCode ? data.taxCode : null } }; let errors: Array< - ProductErrorFragment | StockErrorFragment | BulkStockErrorFragment + | ProductErrorFragment + | StockErrorFragment + | BulkStockErrorFragment + | ProductChannelListingErrorFragment >; if (product.productType.hasVariants) { const result = await updateProduct(productVariables); errors = result.data.productUpdate.errors; - } else { - const result = await updateSimpleProduct({ - ...productVariables, - addStocks: data.addStocks.map(mapFormsetStockToStockInput), - deleteStocks: data.removeStocks, - input: { - ...productVariables.input, - weight: weight(data.weight) - }, - productVariantId: product.variants[0].id, - productVariantInput: { - sku: data.sku, - trackInventory: data.trackInventory - }, - updateStocks: data.updateStocks.map(mapFormsetStockToStockInput) - }); - errors = [ - ...result.data.productUpdate.errors, - ...result.data.productVariantStocksCreate.errors, - ...result.data.productVariantStocksDelete.errors, - ...result.data.productVariantStocksUpdate.errors, - ...result.data.productVariantUpdate.errors - ]; - } - const { isAvailableForPurchase, availableForPurchase } = data; - if ( - isAvailableForPurchase !== product.isAvailableForPurchase || - availableForPurchase !== product.availableForPurchase - ) { - const variables = getProductAvailabilityVariables({ - availableForPurchase, - isAvailableForPurchase, - productId: product.id - }); - const availabilityResult = await setProductAvailability(variables); - errors = [ - ...errors, - ...availabilityResult.data.productSetAvailabilityForPurchase.errors - ]; + updateChannels({ + variables: getChannelsVariables(data, product) + }); + } else { + if (!product.variants.length) { + const productVariantResult = await productVariantCreate({ + variables: { + input: { + attributes: + product.productType.variantAttributes?.map(attribute => ({ + id: attribute.id, + values: attribute.values.map(value => value.slug) + })) || [], + product: product.id, + sku: data.sku, + stocks: data.updateStocks.map(mapFormsetStockToStockInput) + } + } + }); + errors = productVariantResult.data.productVariantCreate.errors; + + const variantId = + productVariantResult.data.productVariantCreate?.productVariant?.id; + if (variantId) { + updateVariantChannels({ + variables: { + id: variantId, + input: getVariantChannelsInput(data) + } + }); + updateChannels({ + variables: getChannelsVariables(data, product) + }); + const result = await updateSimpleProduct( + getSimpleProductVariables(productVariables, data, variantId) + ); + errors = [...errors, ...getSimpleProductErrors(result.data)]; + } + } else { + const result = await updateSimpleProduct( + getSimpleProductVariables( + productVariables, + data, + product.variants[0].id + ) + ); + errors = getSimpleProductErrors(result.data); + + await updateChannels({ + variables: getChannelsVariables(data, product) + }); + updateVariantChannels({ + variables: { + id: product.variants[0].id, + input: getVariantChannelsInput(data) + } + }); + } } return errors; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 7b99e3ba3..f383700ae 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,4 +1,5 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; +import { createVariantChannels } from "@saleor/channels/utils"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -6,6 +7,8 @@ import useNotifier from "@saleor/hooks/useNotifier"; import useOnSetDefaultVariant from "@saleor/hooks/useOnSetDefaultVariant"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; +import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations"; +import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { @@ -17,11 +20,10 @@ import { warehouseAddPath } from "@saleor/warehouses/urls"; import React, { useEffect, useState } from "react"; import { useIntl } from "react-intl"; -import { decimal, weight } from "../../misc"; +import { weight } from "../../misc"; import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog"; -import ProductVariantPage, { - ProductVariantPageSubmitData -} from "../components/ProductVariantPage"; +import ProductVariantPage from "../components/ProductVariantPage"; +import { ProductVariantUpdateSubmitData } from "../components/ProductVariantPage/form"; import { useProductVariantReorderMutation, useVariantDeleteMutation, @@ -79,6 +81,11 @@ export const ProductVariant: React.FC = ({ const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + const [ + updateChannels, + updateChannelsOpts + ] = useProductVariantChannelListingUpdate({}); + const [openModal] = createDialogActionHandlers< ProductVariantEditUrlDialog, ProductVariantEditUrlQueryParams @@ -117,7 +124,35 @@ export const ProductVariant: React.FC = ({ } }); + const handleSubmitChannels = ( + data: ProductVariantUpdateSubmitData, + variant: ProductVariantDetails_productVariant + ) => { + const isChannelPriceChange = data.channelListings.some(channel => { + const variantChannel = variant.channelListings.find( + variantChannel => variantChannel.channel.id === channel.id + ); + return ( + channel.value.price !== variantChannel?.price?.amount.toString() || + channel.value.costPrice !== variantChannel?.costPrice?.amount.toString() + ); + }); + if (isChannelPriceChange) { + updateChannels({ + variables: { + id: variant.id, + input: data.channelListings.map(listing => ({ + channelId: listing.id, + costPrice: listing.value.costPrice || null, + price: listing.value.price + })) + } + }); + } + }; + const variant = data?.productVariant; + const channels = createVariantChannels(variant); if (variant === null) { return ; @@ -163,7 +198,7 @@ export const ProductVariant: React.FC = ({ } }; - const handleUpdate = async (data: ProductVariantPageSubmitData) => { + const handleUpdate = async (data: ProductVariantUpdateSubmitData) => { const result = await updateVariant({ variables: { addStocks: data.addStocks.map(mapFormsetStockToStockInput), @@ -171,9 +206,7 @@ export const ProductVariant: React.FC = ({ id: attribute.id, values: [attribute.value] })), - costPrice: decimal(data.costPrice), id: variantId, - price: decimal(data.price), removeStocks: data.removeStocks, sku: data.sku, stocks: data.updateStocks.map(mapFormsetStockToStockInput), @@ -181,6 +214,7 @@ export const ProductVariant: React.FC = ({ weight: weight(data.weight) } }); + handleSubmitChannels(data, variant); return [ ...result.data?.productVariantStocksCreate.errors, @@ -203,6 +237,11 @@ export const ProductVariant: React.FC = ({ defaultWeightUnit={shop?.defaultWeightUnit} defaultVariantId={data?.productVariant.product.defaultVariant?.id} errors={errors} + channels={channels} + channelErrors={ + updateChannelsOpts?.data?.productVariantChannelListingUpdate + ?.errors || [] + } onSetDefaultVariant={onSetDefaultVariant} saveButtonBarState={updateVariantOpts.status} loading={disableFormSave} @@ -216,7 +255,10 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} - onSubmit={handleSubmit} + onSubmit={data => { + handleSubmit(data); + handleSubmitChannels(data, variant); + }} onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 1b891c0e5..8939631f1 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -1,9 +1,12 @@ +import { ChannelPriceData } from "@saleor/channels/utils"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; +import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations"; +import { ProductVariantChannelListingUpdate } from "@saleor/products/types/ProductVariantChannelListingUpdate"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -14,7 +17,7 @@ import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; -import { decimal, weight } from "../../misc"; +import { weight } from "../../misc"; import ProductVariantCreatePage from "../components/ProductVariantCreatePage"; import { ProductVariantCreateData } from "../components/ProductVariantCreatePage/form"; import { @@ -42,33 +45,65 @@ export const ProductVariant: React.FC = ({ first: 50 } }); + const handleCreateSuccess = (data: ProductVariantChannelListingUpdate) => { + if (data.productVariantChannelListingUpdate.errors.length === 0) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate( + productVariantEditUrl( + productId, + data.productVariantChannelListingUpdate.variant.id + ) + ); + } + }; const { data, loading: productLoading } = useProductVariantCreateQuery({ displayLoader: true, variables: { id: productId } }); - const [variantCreate, variantCreateResult] = useVariantCreateMutation({ - onCompleted: data => { - if (data.productVariantCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - navigate( - productVariantEditUrl( - productId, - data.productVariantCreate.productVariant.id - ) - ); - } - } + const [ + updateChannels, + updateChannelsOpts + ] = useProductVariantChannelListingUpdate({ + onCompleted: handleCreateSuccess }); - const [updateMetadata] = useMetadataUpdate({}); - const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const product = data?.product; + const channels: ChannelPriceData[] = product?.channelListings.map( + listing => ({ + costPrice: null, + currency: listing.channel.currencyCode, + id: listing.channel.id, + name: listing.channel.name, + price: null + }) + ); + + const handleSubmitChannels = ( + data: ProductVariantCreateData, + variantId: string + ) => + updateChannels({ + variables: { + id: variantId, + input: data.channelListings.map(listing => ({ + channelId: listing.id, + costPrice: listing.value.costPrice || null, + price: listing.value.price + })) + } + }); + + const [variantCreate, variantCreateResult] = useVariantCreateMutation({}); + + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + if (product === null) { return navigate(productListUrl())} />; } @@ -93,8 +128,6 @@ export const ProductVariant: React.FC = ({ id: attribute.id, values: [attribute.value] })), - costPrice: decimal(formData.costPrice), - price: decimal(formData.price), product: productId, sku: formData.sku, stocks: formData.stocks.map(stock => ({ @@ -106,8 +139,12 @@ export const ProductVariant: React.FC = ({ } } }); + const id = result.data?.productVariantCreate?.productVariant?.id; + if (id) { + handleSubmitChannels(formData, id); + } - return result.data.productVariantCreate?.productVariant?.id || null; + return id || null; }; const handleSubmit = createMetadataCreateHandler( handleCreate, @@ -131,7 +168,10 @@ export const ProductVariant: React.FC = ({ })} /> = ({ } } }); - const shop = useShop(); return ( <> @@ -52,13 +50,17 @@ const ProductVariantCreator: React.FC = ({ })} /> ({ + currency: listing.channel.currencyCode, + id: listing.channel.id, + name: listing.channel.name, + price: "" + }))} attributes={data?.product?.productType?.variantAttributes || []} - currencySymbol={shop?.defaultCurrency} onSubmit={inputs => bulkProductVariantCreate({ variables: { id, inputs } diff --git a/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.stories.tsx b/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.stories.tsx new file mode 100644 index 000000000..c1105e889 --- /dev/null +++ b/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.stories.tsx @@ -0,0 +1,22 @@ +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import DeleteShippingRateDialog, { + DeleteShippingRateDialogProps +} from "./DeleteShippingRateDialog"; + +const props: DeleteShippingRateDialogProps = { + confirmButtonState: "default", + handleConfirm: () => undefined, + name: "Shipping", + onClose: () => undefined, + open: true +}; + +storiesOf("Shipping / DeleteShippingRateDialog", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )); diff --git a/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.tsx b/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.tsx new file mode 100644 index 000000000..1476c57c0 --- /dev/null +++ b/src/shipping/components/DeleteShippingRateDialog/DeleteShippingRateDialog.tsx @@ -0,0 +1,50 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import { getStringOrPlaceholder } from "@saleor/misc"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +export interface DeleteShippingRateDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + name: string; + onClose: () => void; + handleConfirm: () => void; +} + +export const DeleteShippingRateDialog: React.FC = ({ + confirmButtonState, + onClose, + handleConfirm, + name, + open +}) => { + const intl = useIntl(); + return ( + + + + + + ); +}; + +export default DeleteShippingRateDialog; diff --git a/src/shipping/components/DeleteShippingRateDialog/index.ts b/src/shipping/components/DeleteShippingRateDialog/index.ts new file mode 100644 index 000000000..9b52b882a --- /dev/null +++ b/src/shipping/components/DeleteShippingRateDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./DeleteShippingRateDialog"; +export { default } from "./DeleteShippingRateDialog"; diff --git a/src/shipping/components/OrderValue/OrderValue.stories.tsx b/src/shipping/components/OrderValue/OrderValue.stories.tsx new file mode 100644 index 000000000..8c176384f --- /dev/null +++ b/src/shipping/components/OrderValue/OrderValue.stories.tsx @@ -0,0 +1,23 @@ +import { createShippingChannelsFromRate } from "@saleor/channels/utils"; +import { shippingZone } from "@saleor/shipping/fixtures"; +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import OrderValue, { OrderValueProps } from "./OrderValue"; + +const props: OrderValueProps = { + channels: createShippingChannelsFromRate( + shippingZone.shippingMethods[0].channelListings + ), + disabled: false, + errors: [], + noLimits: false, + onChange: () => undefined, + onChannelsChange: () => undefined +}; + +storiesOf("Shipping / Order value rates", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ); diff --git a/src/shipping/components/OrderValue/OrderValue.tsx b/src/shipping/components/OrderValue/OrderValue.tsx new file mode 100644 index 000000000..28b846fcc --- /dev/null +++ b/src/shipping/components/OrderValue/OrderValue.tsx @@ -0,0 +1,190 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import { ChannelShippingData } from "@saleor/channels/utils"; +import CardTitle from "@saleor/components/CardTitle"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import PriceField from "@saleor/components/PriceField"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import TableHead from "@saleor/components/TableHead"; +import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import { + getFormChannelError, + getFormChannelErrors +} from "@saleor/utils/errors"; +import getShippingErrorMessage from "@saleor/utils/errors/shipping"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "./styles"; + +interface Value { + maxValue: string; + minValue: string; +} +export interface OrderValueProps { + channels: ChannelShippingData[]; + errors: ShippingChannelsErrorFragment[]; + disabled: boolean; + noLimits: boolean; + onChange: (event: ChangeEvent) => void; + onChannelsChange: (channelId: string, value: Value) => void; +} + +const numberOfColumns = 3; + +export const OrderValue: React.FC = ({ + channels, + errors, + noLimits, + disabled, + onChannelsChange, + onChange +}) => { + const classes = useStyles({}); + const intl = useIntl(); + const formErrors = getFormChannelErrors( + ["maximumOrderPrice", "minimumOrderPrice"], + errors + ); + + return ( + + + +
+ + + + {intl.formatMessage({ + defaultMessage: + "This rate will apply to all orders of all prices", + description: "price rates info" + })} + + + } + checked={noLimits} + onChange={onChange} + disabled={disabled} + /> + + + +
+ {!noLimits && ( + + + + + + + + + + + + + + + + + + + + {channels?.map(channel => { + const minError = getFormChannelError( + formErrors.minimumOrderPrice, + channel.id + ); + const maxError = getFormChannelError( + formErrors.maximumOrderPrice, + channel.id + ); + + return ( + + + {channel.name} + + + + onChannelsChange(channel.id, { + ...channel, + minValue: e.target.value + }) + } + currencySymbol={channel.currency} + hint={ + minError && getShippingErrorMessage(minError, intl) + } + /> + + + + onChannelsChange(channel.id, { + ...channel, + maxValue: e.target.value + }) + } + currencySymbol={channel.currency} + hint={ + maxError && getShippingErrorMessage(maxError, intl) + } + /> + + + ); + })} + + + )} +
+
+ ); +}; + +export default OrderValue; diff --git a/src/shipping/components/OrderValue/index.ts b/src/shipping/components/OrderValue/index.ts new file mode 100644 index 000000000..607516293 --- /dev/null +++ b/src/shipping/components/OrderValue/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderValue"; +export { default } from "./OrderValue"; diff --git a/src/shipping/components/OrderValue/styles.ts b/src/shipping/components/OrderValue/styles.ts new file mode 100644 index 000000000..f7013a5d4 --- /dev/null +++ b/src/shipping/components/OrderValue/styles.ts @@ -0,0 +1,41 @@ +import { makeStyles } from "@material-ui/core/styles"; + +export const useStyles = makeStyles( + theme => ({ + caption: { + marginBottom: theme.spacing(2) + }, + colName: { + fontSize: 14, + paddingLeft: 0, + width: "auto" + }, + colType: { + fontSize: 14, + textAlign: "right", + width: 200 + }, + content: { + "&:last-child": { + paddingBottom: 0 + }, + paddingLeft: 0, + paddingRight: 0 + }, + info: { + fontSize: 14 + }, + price: { + verticalAlign: "top" + }, + subheader: { + padding: theme.spacing(0, 3, 2, 3) + }, + table: { + tableLayout: "fixed" + } + }), + { + name: "OrderValue" + } +); diff --git a/src/shipping/components/OrderWeight/OrderWeight.stories.tsx b/src/shipping/components/OrderWeight/OrderWeight.stories.tsx new file mode 100644 index 000000000..4025f22aa --- /dev/null +++ b/src/shipping/components/OrderWeight/OrderWeight.stories.tsx @@ -0,0 +1,31 @@ +import Decorator from "@saleor/storybook//Decorator"; +import { ShippingErrorCode } from "@saleor/types/globalTypes"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import OrderWeight, { OrderWeightProps } from "./OrderWeight"; + +const props: OrderWeightProps = { + disabled: false, + errors: [], + maxValue: "2", + minValue: "1", + noLimits: false, + onChange: () => undefined +}; + +storiesOf("Shipping / Order weight rates", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ) + .add("new", () => ) + .add("form errors", () => ( + ({ + __typename: "ShippingError", + code: ShippingErrorCode.INVALID, + field + }))} + /> + )); diff --git a/src/shipping/components/OrderWeight/OrderWeight.tsx b/src/shipping/components/OrderWeight/OrderWeight.tsx new file mode 100644 index 000000000..6e29a52ba --- /dev/null +++ b/src/shipping/components/OrderWeight/OrderWeight.tsx @@ -0,0 +1,110 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import CardTitle from "@saleor/components/CardTitle"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import { getShippingWeightRateErrorMessage } from "@saleor/shipping/errors"; +import { getFormErrors } from "@saleor/utils/errors"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "./styles"; + +export interface OrderWeightProps { + disabled: boolean; + errors: ShippingErrorFragment[]; + noLimits: boolean; + maxValue: string; + minValue: string; + onChange: (event: ChangeEvent) => void; +} + +export const OrderWeight: React.FC = ({ + noLimits, + disabled, + errors, + maxValue = "", + minValue = "", + onChange +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + const formFields = ["minimumOrderWeight", "maximumOrderWeight"]; + const formErrors = getFormErrors(formFields, errors); + + return ( + + + + + + + + + + } + checked={noLimits} + onChange={onChange} + disabled={disabled} + /> + + {!noLimits && ( +
+ + +
+ )} +
+
+ ); +}; + +export default OrderWeight; diff --git a/src/shipping/components/OrderWeight/index.ts b/src/shipping/components/OrderWeight/index.ts new file mode 100644 index 000000000..1c3e28b40 --- /dev/null +++ b/src/shipping/components/OrderWeight/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderWeight"; +export { default } from "./OrderWeight"; diff --git a/src/shipping/components/OrderWeight/styles.ts b/src/shipping/components/OrderWeight/styles.ts new file mode 100644 index 000000000..da6488a3c --- /dev/null +++ b/src/shipping/components/OrderWeight/styles.ts @@ -0,0 +1,17 @@ +import { makeStyles } from "@material-ui/core/styles"; + +export const useStyles = makeStyles( + theme => ({ + caption: { + marginBottom: theme.spacing(2) + }, + grid: { + display: "grid", + gridColumnGap: theme.spacing(2), + gridTemplateColumns: "1fr 1fr" + } + }), + { + name: "OrderWeight" + } +); diff --git a/src/shipping/components/PricingCard/PricingCard.stories.tsx b/src/shipping/components/PricingCard/PricingCard.stories.tsx new file mode 100644 index 000000000..85649002f --- /dev/null +++ b/src/shipping/components/PricingCard/PricingCard.stories.tsx @@ -0,0 +1,21 @@ +import { createShippingChannelsFromRate } from "@saleor/channels/utils"; +import { shippingZone } from "@saleor/shipping/fixtures"; +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import PricingCard, { PricingCardProps } from "./PricingCard"; + +const props: PricingCardProps = { + channels: createShippingChannelsFromRate( + shippingZone.shippingMethods[0].channelListings + ), + disabled: false, + errors: [], + onChange: () => undefined +}; + +storiesOf("Shipping / Pricing Card", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ); diff --git a/src/shipping/components/PricingCard/PricingCard.tsx b/src/shipping/components/PricingCard/PricingCard.tsx new file mode 100644 index 000000000..922df602e --- /dev/null +++ b/src/shipping/components/PricingCard/PricingCard.tsx @@ -0,0 +1,123 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import { ChannelShippingData } from "@saleor/channels/utils"; +import CardTitle from "@saleor/components/CardTitle"; +import PriceField from "@saleor/components/PriceField"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import TableHead from "@saleor/components/TableHead"; +import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; +import { + getFormChannelError, + getFormChannelErrors +} from "@saleor/utils/errors"; +import getShippingErrorMessage from "@saleor/utils/errors/shipping"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { useStyles } from "./styles"; + +interface Value { + maxValue: string; + minValue: string; + price: string; +} + +export interface PricingCardProps { + channels: ChannelShippingData[]; + errors: ShippingChannelsErrorFragment[]; + disabled: boolean; + onChange: (channelId: string, value: Value) => void; +} + +const numberOfColumns = 2; + +export const PricingCard: React.FC = ({ + channels, + disabled, + errors, + onChange +}) => { + const classes = useStyles({}); + const intl = useIntl(); + const formErrors = getFormChannelErrors(["price"], errors); + + return ( + + + + + {intl.formatMessage({ + defaultMessage: + "Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency", + description: "info text" + })} + + + + + + + + + + + + + + + + {channels?.map(channel => { + const error = getFormChannelError(formErrors.price, channel.id); + + return ( + + + {channel.name} + + + + onChange(channel.id, { + ...channel, + price: e.target.value + }) + } + currencySymbol={channel.currency} + required + hint={error && getShippingErrorMessage(error, intl)} + /> + + + ); + })} + + + + + ); +}; + +export default PricingCard; diff --git a/src/shipping/components/PricingCard/index.ts b/src/shipping/components/PricingCard/index.ts new file mode 100644 index 000000000..3716da074 --- /dev/null +++ b/src/shipping/components/PricingCard/index.ts @@ -0,0 +1,2 @@ +export * from "./PricingCard"; +export { default } from "./PricingCard"; diff --git a/src/shipping/components/PricingCard/styles.ts b/src/shipping/components/PricingCard/styles.ts new file mode 100644 index 000000000..108b749f1 --- /dev/null +++ b/src/shipping/components/PricingCard/styles.ts @@ -0,0 +1,33 @@ +import { makeStyles } from "@material-ui/core/styles"; + +export const useStyles = makeStyles( + theme => ({ + caption: { + fontSize: 14, + padding: theme.spacing(0, 3, 2, 3) + }, + colName: { + fontSize: 14, + paddingLeft: 0, + width: "auto" + }, + colType: { + fontSize: 14, + textAlign: "right", + width: 200 + }, + pricingContent: { + "&:last-child": { + paddingBottom: 0 + }, + paddingLeft: 0, + paddingRight: 0 + }, + table: { + tableLayout: "fixed" + } + }), + { + name: "PricingCard" + } +); diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index c814f3c86..ff3b2f189 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -38,6 +38,7 @@ export interface ShippingZoneDetailsPageProps disabled: boolean; errors: ShippingErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; + selectedChannel: string; shippingZone: ShippingZoneDetailsFragment; warehouses: ShippingZoneDetailsFragment_warehouses[]; onBack: () => void; @@ -81,6 +82,7 @@ const ShippingZoneDetailsPage: React.FC = ({ onWeightRateAdd, onWeightRateEdit, saveButtonBarState, + selectedChannel, shippingZone, warehouses }) => { @@ -158,6 +160,7 @@ const ShippingZoneDetailsPage: React.FC = ({ method => method.type === ShippingMethodTypeEnum.PRICE )} variant="price" + selectedChannel={selectedChannel} /> = ({ method => method.type === ShippingMethodTypeEnum.WEIGHT )} variant="weight" + selectedChannel={selectedChannel} />
diff --git a/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx b/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx deleted file mode 100644 index 904cb2117..000000000 --- a/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx +++ /dev/null @@ -1,375 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import { makeStyles } from "@material-ui/core/styles"; -import TextField from "@material-ui/core/TextField"; -import Typography from "@material-ui/core/Typography"; -import ConfirmButton, { - ConfirmButtonTransitionState -} from "@saleor/components/ConfirmButton"; -import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; -import Form from "@saleor/components/Form"; -import FormSpacer from "@saleor/components/FormSpacer"; -import Hr from "@saleor/components/Hr"; -import Skeleton from "@saleor/components/Skeleton"; -import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; -import { ShippingZoneDetailsFragment_shippingMethods } from "@saleor/fragments/types/ShippingZoneDetailsFragment"; -import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; -import { buttonMessages } from "@saleor/intl"; -import { getFormErrors } from "@saleor/utils/errors"; -import getShippingErrorMessage from "@saleor/utils/errors/shipping"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { maybe } from "../../../misc"; -import { ShippingMethodTypeEnum } from "../../../types/globalTypes"; -import { - getShippingPriceRateErrorMessage, - getShippingWeightRateErrorMessage -} from "./errors"; - -export interface FormData { - name: string; - noLimits: boolean; - minValue: string; - maxValue: string; - isFree: boolean; - price: string; - type: ShippingMethodTypeEnum; -} - -export interface ShippingZoneRateDialogProps { - action: "create" | "edit"; - confirmButtonState: ConfirmButtonTransitionState; - defaultCurrency: string; - disabled: boolean; - errors: ShippingErrorFragment[]; - open: boolean; - rate: ShippingZoneDetailsFragment_shippingMethods; - variant: ShippingMethodTypeEnum; - onClose: () => void; - onSubmit: (data: FormData) => void; -} - -const useStyles = makeStyles( - theme => ({ - grid: { - display: "grid", - gridColumnGap: theme.spacing(2), - gridTemplateColumns: "1fr 1fr" - }, - subheading: { - marginBottom: theme.spacing(2), - marginTop: theme.spacing(2) - } - }), - { - name: "ShippingZoneRateDialog" - } -); -const ShippingZoneRateDialog: React.FC = props => { - const { - action, - confirmButtonState, - defaultCurrency, - disabled, - errors: apiErrors, - onClose, - onSubmit, - open, - rate, - variant - } = props; - - const classes = useStyles(props); - const intl = useIntl(); - const errors = useModalDialogErrors(apiErrors, open); - - const formFields = [ - "name", - "minimumOrderPrice", - "minimumOrderWeight", - "maximumOrderPrice", - "maximumOrderWeight", - "price" - ]; - const formErrors = getFormErrors(formFields, errors); - - const initialForm: FormData = - action === "create" - ? { - isFree: false, - maxValue: "", - minValue: "", - name: "", - noLimits: false, - price: "", - type: null - } - : { - isFree: maybe(() => rate.price.amount === 0, false), - maxValue: - variant === ShippingMethodTypeEnum.PRICE - ? maybe(() => rate.maximumOrderPrice.amount.toString(), "") - : maybe(() => rate.maximumOrderWeight.value.toString(), ""), - minValue: - variant === ShippingMethodTypeEnum.PRICE - ? maybe(() => rate.minimumOrderPrice.amount.toString(), "") - : maybe(() => rate.minimumOrderWeight.value.toString(), ""), - name: maybe(() => rate.name, ""), - noLimits: false, - price: maybe(() => rate.price.amount.toString(), ""), - type: variant - }; - if (action === "edit") { - initialForm.noLimits = !initialForm.maxValue && !initialForm.minValue; - } - - const getErrorMessage = - variant === ShippingMethodTypeEnum.PRICE - ? getShippingPriceRateErrorMessage - : getShippingWeightRateErrorMessage; - - return ( - -
- {({ change, data, hasChanged }) => ( - <> - - {variant === ShippingMethodTypeEnum.PRICE - ? action === "create" - ? intl.formatMessage({ - defaultMessage: "Add Price Rate", - description: "dialog header" - }) - : intl.formatMessage({ - defaultMessage: "Edit Price Rate", - description: "dialog header" - }) - : action === "create" - ? intl.formatMessage({ - defaultMessage: "Add Weight Rate", - description: - "add weight based shipping method, dialog header" - }) - : intl.formatMessage({ - defaultMessage: "Edit Weight Rate", - description: - "edit weight based shipping method, dialog header" - })} - - - - -
- - {!!variant ? ( - <> - - {variant === ShippingMethodTypeEnum.PRICE - ? intl.formatMessage({ - defaultMessage: "Value range", - description: "order price range" - }) - : intl.formatMessage({ - defaultMessage: "Weight range", - description: "order weight range" - })} - - - - - {variant === ShippingMethodTypeEnum.PRICE - ? intl.formatMessage({ - defaultMessage: - "This rate will apply to all orders of all prices" - }) - : intl.formatMessage({ - defaultMessage: - "This rate will apply to all orders of all weights" - })} - - - } - checked={data.noLimits} - onChange={change} - disabled={disabled} - /> - {!data.noLimits && ( - <> - -
- - -
- - )} - - ) : ( - - )} -
-
- - - - - - {!data.isFree && ( - <> - -
- -
- - )} -
- - - - {action === "create" - ? intl.formatMessage({ - defaultMessage: "Create rate", - description: "button" - }) - : intl.formatMessage({ - defaultMessage: "Update rate", - description: "button" - })} - - - - )} -
-
- ); -}; -ShippingZoneRateDialog.displayName = "ShippingZoneRateDialog"; -export default ShippingZoneRateDialog; diff --git a/src/shipping/components/ShippingZoneRateDialog/index.ts b/src/shipping/components/ShippingZoneRateDialog/index.ts deleted file mode 100644 index 45ef7d694..000000000 --- a/src/shipping/components/ShippingZoneRateDialog/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./ShippingZoneRateDialog"; -export * from "./ShippingZoneRateDialog"; diff --git a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx index 40b4eb145..0ba002150 100644 --- a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx +++ b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx @@ -24,6 +24,7 @@ import { ICONBUTTON_SIZE } from "../../../theme"; export interface ShippingZoneRatesProps { disabled: boolean; rates: ShippingZoneDetailsFragment_shippingMethods[]; + selectedChannel: string; variant: "price" | "weight"; onRateAdd: () => void; onRateEdit: (id: string) => void; @@ -55,6 +56,7 @@ const ShippingZoneRates: React.FC = props => { onRateEdit, onRateRemove, rates, + selectedChannel, variant } = props; @@ -118,54 +120,64 @@ const ShippingZoneRates: React.FC = props => { {renderCollection( rates, - rate => ( - onRateEdit(rate.id) : undefined} - > - - {maybe(() => rate.name, )} - - - {maybe( - () => - variant === "price" ? ( - - ) : ( - - ), - - )} - - - {maybe( - () => ( - - ), - - )} - - onRateEdit(rate.id)} + rate => { + const channel = rate?.channelListings?.find( + listing => listing.channel.id === selectedChannel + ); + return ( + onRateEdit(rate.id) : undefined} > - - - onRateRemove(rate.id)} - > - - - - ), + + {maybe(() => rate.name, )} + + + {maybe( + () => + rate && !channel ? ( + "-" + ) : variant === "price" ? ( + + ) : ( + + ), + + )} + + + {maybe( + () => + rate && !channel ? ( + "-" + ) : ( + + ), + + )} + + onRateEdit(rate.id)} + > + + + onRateRemove(rate.id)} + > + + + + ); + }, () => ( diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx new file mode 100644 index 000000000..abac4a784 --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx @@ -0,0 +1,83 @@ +import { shippingZone } from "@saleor/shipping/fixtures"; +import Decorator from "@saleor/storybook//Decorator"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ShippingZoneRatesPage, { + ShippingZoneRatesPageProps +} from "./ShippingZoneRatesPage"; + +const channels = [ + { + currency: "USD", + id: "1", + maxValue: "10", + minValue: "0", + name: "channel", + price: "5" + }, + { + currency: "USD", + id: "2", + maxValue: "20", + minValue: "1", + name: "test", + price: "6" + } +]; + +const defaultChannels = [ + { + currency: "USD", + id: "1", + maxValue: "", + minValue: "", + name: "channel", + price: "" + } +]; + +const props: ShippingZoneRatesPageProps = { + allChannelsCount: 3, + channelErrors: [], + disabled: false, + errors: [], + onBack: () => undefined, + onChannelsChange: () => undefined, + onDelete: () => undefined, + onSubmit: () => undefined, + openChannelsModal: () => undefined, + saveButtonBarState: "default", + shippingChannels: defaultChannels, + variant: ShippingMethodTypeEnum.PRICE +}; + +storiesOf("Shipping / ShippingZoneRates page", module) + .addDecorator(Decorator) + .add("default price", () => ) + .add("loading", () => ( + + )) + .add("update price", () => ( + + )) + .add("default weight", () => ( + + )) + .add("update weight", () => ( + + )); diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx new file mode 100644 index 000000000..0384e299b --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx @@ -0,0 +1,175 @@ +import { ChannelShippingData } from "@saleor/channels/utils"; +import AppHeader from "@saleor/components/AppHeader"; +import CardSpacer from "@saleor/components/CardSpacer"; +import ChannelsAvailability from "@saleor/components/ChannelsAvailability"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import Grid from "@saleor/components/Grid"; +import PageHeader from "@saleor/components/PageHeader"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; +import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; +import { validatePrice } from "@saleor/products/utils/validation"; +import OrderValue from "@saleor/shipping/components/OrderValue"; +import OrderWeight from "@saleor/shipping/components/OrderWeight"; +import PricingCard from "@saleor/shipping/components/PricingCard"; +import ShippingZoneInfo from "@saleor/shipping/components/ShippingZoneInfo"; +import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; +import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +export interface FormData { + channelListings: ChannelShippingData[]; + name: string; + noLimits: boolean; + minValue: string; + maxValue: string; + type: ShippingMethodTypeEnum; +} + +export interface ShippingZoneRatesPageProps { + allChannelsCount?: number; + shippingChannels: ChannelShippingData[]; + disabled: boolean; + hasChannelChanged?: boolean; + rate?: ShippingZone_shippingZone_shippingMethods; + channelErrors: ShippingChannelsErrorFragment[]; + errors: ShippingErrorFragment[]; + saveButtonBarState: ConfirmButtonTransitionState; + onBack: () => void; + onDelete?: () => void; + onSubmit: (data: FormData) => void; + onChannelsChange: (data: ChannelShippingData[]) => void; + openChannelsModal: () => void; + variant: ShippingMethodTypeEnum; +} + +export const ShippingZoneRatesPage: React.FC = ({ + allChannelsCount, + shippingChannels, + channelErrors, + disabled, + errors, + hasChannelChanged, + onBack, + onDelete, + onSubmit, + onChannelsChange, + openChannelsModal, + rate, + saveButtonBarState, + variant +}) => { + const intl = useIntl(); + const initialForm: FormData = { + channelListings: shippingChannels, + maxValue: rate?.maximumOrderWeight?.value.toString() || "", + minValue: rate?.minimumOrderWeight?.value.toString() || "", + name: rate?.name || "", + noLimits: false, + type: rate?.type || null + }; + + return ( +
+ {({ change, data, hasChanged, submit, triggerChange }) => { + const handleChannelsChange = createChannelsChangeHandler( + shippingChannels, + onChannelsChange, + triggerChange + ); + const formDisabled = data.channelListings?.some( + channel => + validatePrice(channel.minValue) || + validatePrice(channel.maxValue) || + validatePrice(channel.price) + ); + + return ( + + + + + + +
+ + + {variant === ShippingMethodTypeEnum.PRICE ? ( + + ) : ( + + )} + + + +
+
+ ({ + id: channel.id, + name: channel.name + }))} + openModal={openChannelsModal} + /> +
+
+ +
+ ); + }} +
+ ); +}; + +export default ShippingZoneRatesPage; diff --git a/src/shipping/components/ShippingZoneRatesPage/index.ts b/src/shipping/components/ShippingZoneRatesPage/index.ts new file mode 100644 index 000000000..6a5b5d935 --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesPage/index.ts @@ -0,0 +1,2 @@ +export * from "./ShippingZoneRatesPage"; +export { default } from "./ShippingZoneRatesPage"; diff --git a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx index 39ecadbd5..cf7c5c6ca 100644 --- a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx +++ b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx @@ -1,4 +1,5 @@ import AppHeader from "@saleor/components/AppHeader"; +import CardMenu from "@saleor/components/CardMenu"; import Container from "@saleor/components/Container"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; @@ -22,6 +23,8 @@ export interface ShippingZonesListPageProps onBack: () => void; onRemove: (id: string) => void; onSubmit: (unit: WeightUnitsEnum) => void; + selectedChannel: string; + onSettingsOpen: () => void; } const ShippingZonesListPage: React.FC = ({ @@ -29,6 +32,7 @@ const ShippingZonesListPage: React.FC = ({ disabled, userPermissions, onBack, + onSettingsOpen, onSubmit, ...listProps }) => { @@ -44,7 +48,19 @@ const ShippingZonesListPage: React.FC = ({ defaultMessage: "Shipping", description: "header" })} - /> + > + +
diff --git a/src/shipping/components/ShippingZoneRateDialog/errors.ts b/src/shipping/errors.ts similarity index 100% rename from src/shipping/components/ShippingZoneRateDialog/errors.ts rename to src/shipping/errors.ts diff --git a/src/shipping/fixtures.ts b/src/shipping/fixtures.ts index 209d6c32b..ac4920183 100644 --- a/src/shipping/fixtures.ts +++ b/src/shipping/fixtures.ts @@ -1550,64 +1550,65 @@ export const shippingZone: ShippingZoneDetailsFragment = { shippingMethods: [ { __typename: "ShippingMethod", + channelListings: [ + { + __typename: "ShippingMethodChannelListing", + channel: { + __typename: "Channel", + currencyCode: "USD", + id: "Q2hhbm5lbDo5", + name: "Channel USD" + }, + id: "U2hpcHBpbmdNZXRob2RDaGFubmVsTGlzdGluZzo0", + maximumOrderPrice: { + __typename: "Money", + amount: 2, + currency: "USD" + }, + minimumOrderPrice: { + __typename: "Money", + amount: 1, + currency: "USD" + }, + price: { + __typename: "Money", + amount: 86.21, + currency: "USD" + } + } + ], id: "U2hpcHBpbmdNZXRob2Q6NA==", - maximumOrderPrice: null, maximumOrderWeight: { __typename: "Weight", unit: WeightUnitsEnum.KG, value: 80 }, - minimumOrderPrice: { - __typename: "Money", - amount: 0, - currency: "USD" - }, minimumOrderWeight: { __typename: "Weight", unit: WeightUnitsEnum.KG, value: 0 }, name: "DB Schenker", - price: { - __typename: "Money", - amount: 45.93, - currency: "USD" - }, type: ShippingMethodTypeEnum.WEIGHT }, { __typename: "ShippingMethod", + channelListings: [], id: "U2hpcHBpbmdNZXRob2Q6Mw==", - maximumOrderPrice: null, maximumOrderWeight: null, - minimumOrderPrice: { - __typename: "Money", - amount: 0, - currency: "USD" - }, minimumOrderWeight: { __typename: "Weight", unit: WeightUnitsEnum.KG, value: 0 }, name: "Registred priority", - price: { - __typename: "Money", - amount: 73.87, - currency: "USD" - }, type: ShippingMethodTypeEnum.WEIGHT }, { __typename: "ShippingMethod", + channelListings: [], id: "U2hpcHBpbmdNZXRob2Q6Mg==", - maximumOrderPrice: null, maximumOrderWeight: null, - minimumOrderPrice: { - __typename: "Money", - amount: 0, - currency: "USD" - }, minimumOrderWeight: { __typename: "Weight", unit: WeightUnitsEnum.KG, @@ -1615,34 +1616,19 @@ export const shippingZone: ShippingZoneDetailsFragment = { }, name: "UPS", - price: { - __typename: "Money", - amount: 48.11, - currency: "USD" - }, type: ShippingMethodTypeEnum.PRICE }, { __typename: "ShippingMethod", + channelListings: [], id: "U2hpcHBpbmdNZXRob2Q6MQ==", - maximumOrderPrice: null, maximumOrderWeight: null, - minimumOrderPrice: { - __typename: "Money", - amount: 0, - currency: "USD" - }, minimumOrderWeight: { __typename: "Weight", unit: WeightUnitsEnum.KG, value: 0 }, name: "DHL", - price: { - __typename: "Money", - amount: 95.24, - currency: "USD" - }, type: ShippingMethodTypeEnum.PRICE } ], diff --git a/src/shipping/handlers.ts b/src/shipping/handlers.ts new file mode 100644 index 000000000..e367fbe0c --- /dev/null +++ b/src/shipping/handlers.ts @@ -0,0 +1,124 @@ +import { ChannelShippingData } from "@saleor/channels/utils"; +import { FormData as ShippingZoneRatesPageFormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; +import { CreateShippingRateVariables } from "@saleor/shipping/types/CreateShippingRate"; +import { ShippingMethodChannelListingUpdateVariables } from "@saleor/shipping/types/ShippingMethodChannelListingUpdate"; +import { UpdateShippingRateVariables } from "@saleor/shipping/types/UpdateShippingRate"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { diff } from "fast-array-diff"; + +export const createChannelsChangeHandler = ( + selectedChannels: ChannelShippingData[], + setSelectedChannels: (channels: ChannelShippingData[]) => void, + triggerChange: () => void +) => ( + channelId: string, + value: { maxValue: string; minValue: string; price: string } +) => { + const itemIndex = selectedChannels.findIndex(item => item.id === channelId); + const channel = selectedChannels[itemIndex]; + setSelectedChannels([ + ...selectedChannels.slice(0, itemIndex), + { + ...channel, + maxValue: value.maxValue, + minValue: value.minValue, + price: value.price + }, + ...selectedChannels.slice(itemIndex + 1) + ]); + triggerChange(); +}; + +export function getCreateShippingPriceRateVariables( + data: ShippingZoneRatesPageFormData, + id: string +): CreateShippingRateVariables { + return { + input: { + name: data.name, + shippingZone: id, + type: ShippingMethodTypeEnum.PRICE + } + }; +} + +export function getCreateShippingWeightRateVariables( + data: ShippingZoneRatesPageFormData, + id: string +): CreateShippingRateVariables { + const parsedMinValue = parseFloat(data.minValue); + const parsedMaxValue = parseFloat(data.maxValue); + const isWeightSet = !data.noLimits; + return { + input: { + maximumOrderWeight: isWeightSet ? parsedMaxValue : null, + minimumOrderWeight: isWeightSet ? parsedMinValue : null, + name: data.name, + shippingZone: id, + type: ShippingMethodTypeEnum.WEIGHT + } + }; +} + +export function getUpdateShippingPriceRateVariables( + data: ShippingZoneRatesPageFormData, + id: string, + rateId: string +): UpdateShippingRateVariables { + return { + id: rateId, + input: { + name: data.name, + shippingZone: id, + type: ShippingMethodTypeEnum.PRICE + } + }; +} + +export function getUpdateShippingWeightRateVariables( + data: ShippingZoneRatesPageFormData, + id: string, + rateId: string +): UpdateShippingRateVariables { + const parsedMinValue = parseFloat(data.minValue); + const parsedMaxValue = parseFloat(data.maxValue); + const isWeightSet = !data.noLimits; + return { + id: rateId, + input: { + maximumOrderWeight: isWeightSet ? parsedMaxValue : null, + minimumOrderWeight: isWeightSet ? parsedMinValue : null, + name: data.name, + shippingZone: id, + type: ShippingMethodTypeEnum.WEIGHT + } + }; +} +export function getShippingMethodChannelVariables( + id: string, + noLimits: boolean, + formChannels: ChannelShippingData[], + prevChannels?: ChannelShippingData[] +): ShippingMethodChannelListingUpdateVariables { + const removeChannels = prevChannels + ? diff(prevChannels, formChannels, (a, b) => a.id === b.id).removed?.map( + removedChannel => removedChannel.id + ) + : []; + + return { + id, + input: { + addChannels: + formChannels?.map(channel => ({ + channelId: channel.id, + maximumOrderPrice: + channel.maxValue && !noLimits ? channel.maxValue : null, + minimumOrderPrice: + channel.minValue && !noLimits ? channel.minValue : null, + price: channel.price || null + })) || [], + removeChannels + } + }; +} diff --git a/src/shipping/index.tsx b/src/shipping/index.tsx index 32bdd7f7b..508241e9d 100644 --- a/src/shipping/index.tsx +++ b/src/shipping/index.tsx @@ -6,15 +6,23 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; import { + shippingPriceRatesEditUrl, + shippingPriceRatesUrl, + shippingWeightRatesEditUrl, + shippingWeightRatesUrl, shippingZoneAddPath, shippingZonePath, shippingZonesListPath, ShippingZonesListUrlQueryParams, ShippingZoneUrlQueryParams } from "./urls"; +import PriceRatesCreateComponent from "./views/PriceRatesCreate"; +import PriceRatesUpdateComponent from "./views/PriceRatesUpdate"; import ShippingZoneCreate from "./views/ShippingZoneCreate"; import ShippingZoneDetailsComponent from "./views/ShippingZoneDetails"; import ShippingZonesListComponent from "./views/ShippingZonesList"; +import WeightRatesCreateComponent from "./views/WeightRatesCreate"; +import WeightRatesUpdateComponent from "./views/WeightRatesUpdate"; const ShippingZonesList: React.FC> = ({ location }) => { const qs = parseQs(location.search.substr(1)); @@ -38,6 +46,34 @@ const ShippingZoneDetails: React.FC> = ({ + match +}) => ; + +const WeightRatesCreate: React.FC> = ({ + match +}) => ; + +const WeightRatesUpdate: React.FC> = ({ match }) => ( + +); + +const PriceRatesUpdate: React.FC> = ({ match }) => ( + +); + export const ShippingRouter: React.FC = () => { const intl = useIntl(); @@ -55,7 +91,27 @@ export const ShippingRouter: React.FC = () => { path={shippingZoneAddPath} component={ShippingZoneCreate} /> - + + + + + ); diff --git a/src/shipping/mutations.ts b/src/shipping/mutations.ts index e437bfa81..d950893e2 100644 --- a/src/shipping/mutations.ts +++ b/src/shipping/mutations.ts @@ -1,4 +1,7 @@ -import { shippingErrorFragment } from "@saleor/fragments/errors"; +import { + shippingChannelsErrorFragment, + shippingErrorFragment +} from "@saleor/fragments/errors"; import { shippingMethodFragment, shippingZoneDetailsFragment @@ -31,6 +34,10 @@ import { DeleteShippingZone, DeleteShippingZoneVariables } from "./types/DeleteShippingZone"; +import { + ShippingMethodChannelListingUpdate, + ShippingMethodChannelListingUpdateVariables +} from "./types/ShippingMethodChannelListingUpdate"; import { UpdateDefaultWeightUnit, UpdateDefaultWeightUnitVariables @@ -161,6 +168,7 @@ export const useShippingRateUpdate = makeMutation< const createShippingRate = gql` ${shippingErrorFragment} + ${shippingMethodFragment} ${shippingZoneDetailsFragment} mutation CreateShippingRate($input: ShippingPriceInput!) { shippingPriceCreate(input: $input) { @@ -170,6 +178,9 @@ const createShippingRate = gql` shippingZone { ...ShippingZoneDetailsFragment } + shippingMethod { + ...ShippingMethodFragment + } } } `; @@ -211,3 +222,26 @@ export const useShippingRateBulkDelete = makeMutation< BulkDeleteShippingRate, BulkDeleteShippingRateVariables >(bulkDeleteShippingRate); + +export const shippingMethodChannelListingUpdate = gql` + ${shippingChannelsErrorFragment} + ${shippingMethodFragment} + mutation ShippingMethodChannelListingUpdate( + $id: ID! + $input: ShippingMethodChannelListingInput! + ) { + shippingMethodChannelListingUpdate(id: $id, input: $input) { + shippingMethod { + ...ShippingMethodFragment + } + errors: shippingErrors { + ...ShippingChannelsErrorFragment + } + } + } +`; + +export const useShippingMethodChannelListingUpdate = makeMutation< + ShippingMethodChannelListingUpdate, + ShippingMethodChannelListingUpdateVariables +>(shippingMethodChannelListingUpdate); diff --git a/src/shipping/types/CreateShippingRate.ts b/src/shipping/types/CreateShippingRate.ts index b1a8d9b95..81ad1e07c 100644 --- a/src/shipping/types/CreateShippingRate.ts +++ b/src/shipping/types/CreateShippingRate.ts @@ -20,46 +20,60 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_countries { country: string; } -export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_price { +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_channel; + price: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_price | null; + minimumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_minimumOrderPrice | null; + maximumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings_maximumOrderPrice | null; +} + export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderPrice | null; minimumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderWeight | null; - maximumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderPrice | null; maximumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderWeight | null; name: string; - price: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_price | null; type: ShippingMethodTypeEnum | null; + channelListings: CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_channelListings[] | null; } export interface CreateShippingRate_shippingPriceCreate_shippingZone_warehouses { @@ -78,10 +92,67 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone { warehouses: (CreateShippingRate_shippingPriceCreate_shippingZone_warehouses | null)[] | null; } +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_minimumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_maximumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_channel; + price: CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_price | null; + minimumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_minimumOrderPrice | null; + maximumOrderPrice: CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings_maximumOrderPrice | null; +} + +export interface CreateShippingRate_shippingPriceCreate_shippingMethod { + __typename: "ShippingMethod"; + id: string; + minimumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingMethod_minimumOrderWeight | null; + maximumOrderWeight: CreateShippingRate_shippingPriceCreate_shippingMethod_maximumOrderWeight | null; + name: string; + type: ShippingMethodTypeEnum | null; + channelListings: CreateShippingRate_shippingPriceCreate_shippingMethod_channelListings[] | null; +} + export interface CreateShippingRate_shippingPriceCreate { __typename: "ShippingPriceCreate"; errors: CreateShippingRate_shippingPriceCreate_errors[]; shippingZone: CreateShippingRate_shippingPriceCreate_shippingZone | null; + shippingMethod: CreateShippingRate_shippingPriceCreate_shippingMethod | null; } export interface CreateShippingRate { diff --git a/src/shipping/types/DeleteShippingRate.ts b/src/shipping/types/DeleteShippingRate.ts index c5d02a026..13cd8ac9a 100644 --- a/src/shipping/types/DeleteShippingRate.ts +++ b/src/shipping/types/DeleteShippingRate.ts @@ -20,46 +20,60 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_countries { country: string; } -export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_price { +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_channel; + price: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_price | null; + minimumOrderPrice: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_minimumOrderPrice | null; + maximumOrderPrice: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings_maximumOrderPrice | null; +} + export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderPrice | null; minimumOrderWeight: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderWeight | null; - maximumOrderPrice: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderPrice | null; maximumOrderWeight: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderWeight | null; name: string; - price: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_price | null; type: ShippingMethodTypeEnum | null; + channelListings: DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_channelListings[] | null; } export interface DeleteShippingRate_shippingPriceDelete_shippingZone_warehouses { diff --git a/src/shipping/types/ShippingMethodChannelListingUpdate.ts b/src/shipping/types/ShippingMethodChannelListingUpdate.ts new file mode 100644 index 000000000..89e925294 --- /dev/null +++ b/src/shipping/types/ShippingMethodChannelListingUpdate.ts @@ -0,0 +1,87 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ShippingMethodChannelListingInput, WeightUnitsEnum, ShippingMethodTypeEnum, ShippingErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ShippingMethodChannelListingUpdate +// ==================================================== + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_minimumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_maximumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_channel; + price: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_price | null; + minimumOrderPrice: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_minimumOrderPrice | null; + maximumOrderPrice: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings_maximumOrderPrice | null; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod { + __typename: "ShippingMethod"; + id: string; + minimumOrderWeight: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_minimumOrderWeight | null; + maximumOrderWeight: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_maximumOrderWeight | null; + name: string; + type: ShippingMethodTypeEnum | null; + channelListings: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod_channelListings[] | null; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_errors { + __typename: "ShippingError"; + code: ShippingErrorCode; + field: string | null; + channels: string[] | null; +} + +export interface ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate { + __typename: "ShippingMethodChannelListingUpdate"; + shippingMethod: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_shippingMethod | null; + errors: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate_errors[]; +} + +export interface ShippingMethodChannelListingUpdate { + shippingMethodChannelListingUpdate: ShippingMethodChannelListingUpdate_shippingMethodChannelListingUpdate | null; +} + +export interface ShippingMethodChannelListingUpdateVariables { + id: string; + input: ShippingMethodChannelListingInput; +} diff --git a/src/shipping/types/ShippingZone.ts b/src/shipping/types/ShippingZone.ts index bfde35cbb..a7a73c9e1 100644 --- a/src/shipping/types/ShippingZone.ts +++ b/src/shipping/types/ShippingZone.ts @@ -14,46 +14,60 @@ export interface ShippingZone_shippingZone_countries { country: string; } -export interface ShippingZone_shippingZone_shippingMethods_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingZone_shippingZone_shippingMethods_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingZone_shippingZone_shippingMethods_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface ShippingZone_shippingZone_shippingMethods_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface ShippingZone_shippingZone_shippingMethods_price { +export interface ShippingZone_shippingZone_shippingMethods_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ShippingZone_shippingZone_shippingMethods_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface ShippingZone_shippingZone_shippingMethods_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingZone_shippingZone_shippingMethods_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingZone_shippingZone_shippingMethods_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: ShippingZone_shippingZone_shippingMethods_channelListings_channel; + price: ShippingZone_shippingZone_shippingMethods_channelListings_price | null; + minimumOrderPrice: ShippingZone_shippingZone_shippingMethods_channelListings_minimumOrderPrice | null; + maximumOrderPrice: ShippingZone_shippingZone_shippingMethods_channelListings_maximumOrderPrice | null; +} + export interface ShippingZone_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: ShippingZone_shippingZone_shippingMethods_minimumOrderPrice | null; minimumOrderWeight: ShippingZone_shippingZone_shippingMethods_minimumOrderWeight | null; - maximumOrderPrice: ShippingZone_shippingZone_shippingMethods_maximumOrderPrice | null; maximumOrderWeight: ShippingZone_shippingZone_shippingMethods_maximumOrderWeight | null; name: string; - price: ShippingZone_shippingZone_shippingMethods_price | null; type: ShippingMethodTypeEnum | null; + channelListings: ShippingZone_shippingZone_shippingMethods_channelListings[] | null; } export interface ShippingZone_shippingZone_warehouses { diff --git a/src/shipping/types/UpdateShippingRate.ts b/src/shipping/types/UpdateShippingRate.ts index 880203eef..f505c129c 100644 --- a/src/shipping/types/UpdateShippingRate.ts +++ b/src/shipping/types/UpdateShippingRate.ts @@ -14,46 +14,60 @@ export interface UpdateShippingRate_shippingPriceUpdate_errors { field: string | null; } -export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderPrice { - __typename: "Money"; - amount: number; - currency: string; -} - export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderWeight { __typename: "Weight"; unit: WeightUnitsEnum; value: number; } -export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_price { +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_price { __typename: "Money"; amount: number; currency: string; } +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_channel; + price: UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_price | null; + minimumOrderPrice: UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_minimumOrderPrice | null; + maximumOrderPrice: UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings_maximumOrderPrice | null; +} + export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod { __typename: "ShippingMethod"; id: string; - minimumOrderPrice: UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderPrice | null; minimumOrderWeight: UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderWeight | null; - maximumOrderPrice: UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderPrice | null; maximumOrderWeight: UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderWeight | null; name: string; - price: UpdateShippingRate_shippingPriceUpdate_shippingMethod_price | null; type: ShippingMethodTypeEnum | null; + channelListings: UpdateShippingRate_shippingPriceUpdate_shippingMethod_channelListings[] | null; } export interface UpdateShippingRate_shippingPriceUpdate { diff --git a/src/shipping/urls.ts b/src/shipping/urls.ts index c9dc4d062..6c8db1401 100644 --- a/src/shipping/urls.ts +++ b/src/shipping/urls.ts @@ -7,7 +7,7 @@ import { ShippingMethodTypeEnum } from "../types/globalTypes"; export const shippingSection = "/shipping/"; export const shippingZonesListPath = shippingSection; -export type ShippingZonesListUrlDialog = "remove" | "remove-many"; +export type ShippingZonesListUrlDialog = "remove" | "remove-many" | "settings"; export type ShippingZonesListUrlQueryParams = BulkAction & Dialog & Pagination & @@ -36,5 +36,22 @@ export const shippingZoneUrl = ( params?: ShippingZoneUrlQueryParams ) => shippingZonePath(encodeURIComponent(id)) + "?" + stringifyQs(params); +export const shippingPriceRatesUrl = (id: string) => + urlJoin(shippingZonePath(id), "price", "add"); + +export const shippingWeightRatesUrl = (id: string) => + urlJoin(shippingZonePath(id), "weight", "add"); + +export const shippingWeightRatesEditUrl = (id: string, rateId: string) => + urlJoin(shippingZonePath(id), "weight", rateId); + +export const shippingPriceRatesEditUrl = (id: string, rateId: string) => + urlJoin(shippingZonePath(id), "price", rateId); + export const shippingZoneAddPath = urlJoin(shippingZonesListPath, "add"); export const shippingZoneAddUrl = shippingZoneAddPath; +export const shippingPriceRatesAddPath = urlJoin( + shippingZonesListPath, + "price", + "add" +); diff --git a/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx new file mode 100644 index 000000000..281f5eca4 --- /dev/null +++ b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx @@ -0,0 +1,142 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { createSortedShippingChannels } from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useChannels from "@saleor/hooks/useChannels"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { sectionNames } from "@saleor/intl"; +import { FormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; +import ShippingZoneRatesPage from "@saleor/shipping/components/ShippingZoneRatesPage"; +import { + getCreateShippingPriceRateVariables, + getShippingMethodChannelVariables +} from "@saleor/shipping/handlers"; +import { useShippingMethodChannelListingUpdate } from "@saleor/shipping/mutations"; +import { useShippingRateCreate } from "@saleor/shipping/mutations"; +import { + shippingPriceRatesEditUrl, + shippingZoneUrl +} from "@saleor/shipping/urls"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import getShippingErrorMessage from "@saleor/utils/errors/shipping"; +import React from "react"; +import { useIntl } from "react-intl"; + +export interface PriceRatesCreateProps { + id: string; +} + +export const PriceRatesCreate: React.FC = ({ id }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data: channelsData, loading: channelsLoading } = useChannelsList({}); + + const [ + updateShippingMethodChannelListing, + updateShippingMethodChannelListingOpts + ] = useShippingMethodChannelListingUpdate({ + onCompleted: data => { + const errors = data.shippingMethodChannelListingUpdate.errors; + if (errors.length === 0) { + navigate( + shippingPriceRatesEditUrl( + id, + data.shippingMethodChannelListingUpdate.shippingMethod.id + ) + ); + } + } + }); + + const allChannels = createSortedShippingChannels(channelsData?.channels); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(allChannels); + + const [createShippingRate, createShippingRateOpts] = useShippingRateCreate( + {} + ); + + const handleSubmit = async (data: FormData) => { + const response = await createShippingRate({ + variables: getCreateShippingPriceRateVariables(data, id) + }); + const errors = response.data.shippingPriceCreate.errors; + if (errors.length === 0) { + updateShippingMethodChannelListing({ + variables: getShippingMethodChannelVariables( + response.data.shippingPriceCreate.shippingMethod.id, + data.noLimits, + data.channelListings + ) + }); + } else { + errors.map(err => + notify({ + status: "error", + text: getShippingErrorMessage(err, intl) + }) + ); + } + }; + const handleBack = () => navigate(shippingZoneUrl(id)); + + return ( + <> + + {!!allChannels?.length && ( + + )} + + + + ); +}; + +export default PriceRatesCreate; diff --git a/src/shipping/views/PriceRatesCreate/index.ts b/src/shipping/views/PriceRatesCreate/index.ts new file mode 100644 index 000000000..a49932cfc --- /dev/null +++ b/src/shipping/views/PriceRatesCreate/index.ts @@ -0,0 +1,2 @@ +export * from "./PriceRatesCreate"; +export { default } from "./PriceRatesCreate"; diff --git a/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx new file mode 100644 index 000000000..0f5b8da73 --- /dev/null +++ b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx @@ -0,0 +1,188 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { + createShippingChannelsFromRate, + createSortedShippingChannels +} from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useChannels from "@saleor/hooks/useChannels"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { sectionNames } from "@saleor/intl"; +import { commonMessages } from "@saleor/intl"; +import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; +import ShippingZoneRatesPage, { + FormData +} from "@saleor/shipping/components/ShippingZoneRatesPage"; +import { + getShippingMethodChannelVariables, + getUpdateShippingPriceRateVariables +} from "@saleor/shipping/handlers"; +import { useShippingMethodChannelListingUpdate } from "@saleor/shipping/mutations"; +import { + useShippingRateDelete, + useShippingRateUpdate +} from "@saleor/shipping/mutations"; +import { useShippingZone } from "@saleor/shipping/queries"; +import { shippingZoneUrl } from "@saleor/shipping/urls"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import getShippingErrorMessage from "@saleor/utils/errors/shipping"; +import React from "react"; +import { useIntl } from "react-intl"; + +export interface PriceRatesUpdateProps { + id: string; + rateId: string; +} + +export const PriceRatesUpdate: React.FC = ({ + id, + rateId +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data, loading } = useShippingZone({ + displayLoader: true, + variables: { id } + }); + + const rate = data?.shippingZone?.shippingMethods.find( + rate => rate.id === rateId + ); + const { data: channelsData } = useChannelsList({}); + + const [ + updateShippingMethodChannelListing, + updateShippingMethodChannelListingOpts + ] = useShippingMethodChannelListingUpdate({}); + + const shippingChannels = createShippingChannelsFromRate( + rate?.channelListings + ); + const allChannels = createSortedShippingChannels(channelsData?.channels); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(shippingChannels); + + const [openModal, setOpenModal] = React.useState(false); + + const [updateShippingRate, updateShippingRateOpts] = useShippingRateUpdate( + {} + ); + + const handleSuccess = () => { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + }; + const [deleteShippingRate, deleteShippingRateOpts] = useShippingRateDelete({ + onCompleted: data => { + if (data.shippingPriceDelete.errors.length === 0) { + handleSuccess(); + navigate(shippingZoneUrl(id)); + } + } + }); + + const handleDelete = () => setOpenModal(true); + const handleSubmit = async (formData: FormData) => { + const response = await updateShippingRate({ + variables: getUpdateShippingPriceRateVariables(formData, id, rateId) + }); + const errors = response.data.shippingPriceUpdate.errors; + if (errors.length === 0) { + handleSuccess(); + updateShippingMethodChannelListing({ + variables: getShippingMethodChannelVariables( + rateId, + formData.noLimits, + formData.channelListings, + shippingChannels + ) + }); + } else { + errors.map(err => + notify({ + status: "error", + text: getShippingErrorMessage(err, intl) + }) + ); + } + }; + + const handleBack = () => navigate(shippingZoneUrl(id)); + + return ( + <> + + {!!allChannels?.length && ( + + )} + setOpenModal(false)} + handleConfirm={() => + deleteShippingRate({ + variables: { + id: rateId + } + }) + } + open={openModal} + name={rate?.name} + /> + + + ); +}; + +export default PriceRatesUpdate; diff --git a/src/shipping/views/PriceRatesUpdate/index.ts b/src/shipping/views/PriceRatesUpdate/index.ts new file mode 100644 index 000000000..b7963e205 --- /dev/null +++ b/src/shipping/views/PriceRatesUpdate/index.ts @@ -0,0 +1,2 @@ +export * from "./PriceRatesUpdate"; +export { default } from "./PriceRatesUpdate"; diff --git a/src/shipping/views/ShippingZoneDetails/data.ts b/src/shipping/views/ShippingZoneDetails/data.ts deleted file mode 100644 index 799e91600..000000000 --- a/src/shipping/views/ShippingZoneDetails/data.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { CreateShippingRateVariables } from "@saleor/shipping/types/CreateShippingRate"; -import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone"; -import { UpdateShippingRateVariables } from "@saleor/shipping/types/UpdateShippingRate"; -import { ShippingZoneUrlQueryParams } from "@saleor/shipping/urls"; -import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; - -import { FormData as ShippingZoneRateDialogFormData } from "../../components/ShippingZoneRateDialog"; - -function getValue(value: string, hasLimits: boolean): number | null { - return hasLimits ? null : parseFloat(value); -} - -export function getCreateShippingRateVariables( - data: ShippingZoneRateDialogFormData, - params: ShippingZoneUrlQueryParams, - id: string -): CreateShippingRateVariables { - return { - input: { - maximumOrderPrice: - params.type === ShippingMethodTypeEnum.PRICE - ? getValue(data.maxValue, data.noLimits) - : null, - maximumOrderWeight: - params.type === ShippingMethodTypeEnum.WEIGHT - ? getValue(data.maxValue, data.noLimits) - : null, - - minimumOrderPrice: - params.type === ShippingMethodTypeEnum.PRICE - ? getValue(data.minValue, data.noLimits) - : null, - minimumOrderWeight: - params.type === ShippingMethodTypeEnum.WEIGHT - ? getValue(data.minValue, data.noLimits) - : null, - name: data.name, - price: data.isFree ? 0 : parseFloat(data.price), - shippingZone: id, - type: params.type - } - }; -} - -export function getUpdateShippingRateVariables( - data: ShippingZoneRateDialogFormData, - shippingRate: ShippingZone_shippingZone_shippingMethods, - shippingZoneId: string -): UpdateShippingRateVariables { - const base: UpdateShippingRateVariables = { - id: shippingRate.id, - input: { - name: data.name, - price: data.isFree ? 0 : parseFloat(data.price), - shippingZone: shippingZoneId, - type: shippingRate.type - } - }; - - if (shippingRate.type === ShippingMethodTypeEnum.PRICE) { - return { - ...base, - input: { - ...base.input, - maximumOrderPrice: getValue(data.maxValue, data.noLimits), - minimumOrderPrice: getValue(data.minValue, data.noLimits) - } - }; - } - - return { - ...base, - input: { - ...base.input, - maximumOrderWeight: getValue(data.maxValue, data.noLimits), - minimumOrderWeight: getValue(data.minValue, data.noLimits) - } - }; -} diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index 84bc07de1..a50012149 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -2,18 +2,17 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import ActionDialog from "@saleor/components/ActionDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; import ShippingZoneAddWarehouseDialog from "@saleor/shipping/components/ShippingZoneAddWarehouseDialog"; import ShippingZoneCountriesAssignDialog from "@saleor/shipping/components/ShippingZoneCountriesAssignDialog"; -import ShippingZoneRateDialog from "@saleor/shipping/components/ShippingZoneRateDialog"; import { - useShippingRateCreate, useShippingRateDelete, - useShippingRateUpdate, useShippingZoneDelete, useShippingZoneUpdate } from "@saleor/shipping/mutations"; @@ -24,24 +23,21 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { findValueInEnum, getStringOrPlaceholder } from "../../../misc"; -import { - CountryCode, - ShippingMethodTypeEnum -} from "../../../types/globalTypes"; +import { CountryCode } from "../../../types/globalTypes"; import ShippingZoneDetailsPage, { FormData } from "../../components/ShippingZoneDetailsPage"; import { useShippingZone } from "../../queries"; import { + shippingPriceRatesEditUrl, + shippingPriceRatesUrl, + shippingWeightRatesEditUrl, + shippingWeightRatesUrl, shippingZonesListUrl, shippingZoneUrl, ShippingZoneUrlDialog, ShippingZoneUrlQueryParams } from "../../urls"; -import { - getCreateShippingRateVariables, - getUpdateShippingRateVariables -} from "./data"; export interface ShippingZoneDetailsProps { id: string; @@ -68,38 +64,16 @@ const ShippingZoneDetails: React.FC = ({ variables: { id } }); + const [selectedChannel] = useLocalStorage("shippingListChannel", ""); + const [openModal, closeModal] = createDialogActionHandlers< ShippingZoneUrlDialog, ShippingZoneUrlQueryParams >(navigate, params => shippingZoneUrl(id, params), params); - const rate = data?.shippingZone?.shippingMethods.find( + const rate = data?.shippingZone?.shippingMethods?.find( rate => rate.id === params.id ); - const [createShippingRate, createShippingRateOpts] = useShippingRateCreate({ - onCompleted: data => { - if (data.shippingPriceCreate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - } - }); - - const [updateShippingRate, updateShippingRateOpts] = useShippingRateUpdate({ - onCompleted: data => { - if (data.shippingPriceUpdate.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - } - }); - const [deleteShippingRate, deleteShippingRateOpts] = useShippingRateDelete({ onCompleted: data => { if (data.shippingPriceDelete.errors.length === 0) { @@ -185,15 +159,9 @@ const ShippingZoneDetails: React.FC = ({ }) } onDelete={() => openModal("remove")} - onPriceRateAdd={() => - openModal("add-rate", { - type: ShippingMethodTypeEnum.PRICE - }) - } + onPriceRateAdd={() => navigate(shippingPriceRatesUrl(id))} onPriceRateEdit={rateId => - openModal("edit-rate", { - id: rateId - }) + navigate(shippingPriceRatesEditUrl(id, rateId)) } onRateRemove={rateId => openModal("remove-rate", { @@ -202,15 +170,9 @@ const ShippingZoneDetails: React.FC = ({ } onSubmit={handleSubmit} onWarehouseAdd={() => openModal("add-warehouse")} - onWeightRateAdd={() => - openModal("add-rate", { - type: ShippingMethodTypeEnum.WEIGHT - }) - } + onWeightRateAdd={() => navigate(shippingWeightRatesUrl(id))} onWeightRateEdit={rateId => - openModal("edit-rate", { - id: rateId - }) + navigate(shippingWeightRatesEditUrl(id, rateId)) } saveButtonBarState={updateShippingZoneOpts.status} shippingZone={data?.shippingZone} @@ -221,72 +183,20 @@ const ShippingZoneDetails: React.FC = ({ loading={searchWarehousesOpts.loading} onFetchMore={loadMore} onSearchChange={search} + selectedChannel={selectedChannel} /> - - updateShippingRate({ - variables: getUpdateShippingRateVariables( - submitData, - data?.shippingZone?.shippingMethods.find( - shippingMethod => shippingMethod.id === params.id - ), - id - ) - }) - } - open={params.action === "edit-rate"} - rate={rate} - variant={rate?.type} - /> - + handleConfirm={() => deleteShippingRate({ variables: { id: params.id } }) } + name={rate?.name} open={params.action === "remove-rate"} - title={intl.formatMessage({ - defaultMessage: "Delete Shipping Method", - description: "dialog header" - })} - variant="delete" - > - - - - - - createShippingRate({ - variables: getCreateShippingRateVariables(data, params, id) - }) - } - open={params.action === "add-rate"} - rate={undefined} - variant={params.type} /> = ({ ShippingZonesListUrlQueryParams >(navigate, shippingZonesListUrl, params); + const { + channelChoices, + handleChannelSelectConfirm, + selectedChannel + } = useChannelsSettings("shippingListChannel", { closeModal, openModal }); + const { data, loading, refetch } = useShippingZoneList({ displayLoader: true, variables: paginationState @@ -117,8 +125,16 @@ export const ShippingZonesList: React.FC = ({ ); return ( <> + shop.defaultWeightUnit)} + defaultWeightUnit={shop?.defaultWeightUnit} settings={settings} disabled={ loading || @@ -162,6 +178,8 @@ export const ShippingZonesList: React.FC = ({ } userPermissions={user?.userPermissions || []} + selectedChannel={selectedChannel} + onSettingsOpen={() => openModal("settings")} /> = ({ id }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data: channelsData, loading: channelsLoading } = useChannelsList({}); + + const [ + updateShippingMethodChannelListing, + updateShippingMethodChannelListingOpts + ] = useShippingMethodChannelListingUpdate({ + onCompleted: data => { + const errors = data.shippingMethodChannelListingUpdate.errors; + if (errors.length === 0) { + navigate( + shippingWeightRatesEditUrl( + id, + data.shippingMethodChannelListingUpdate.shippingMethod.id + ) + ); + } + } + }); + + const shippingChannels = createShippingChannels(channelsData?.channels); + const allChannels = createSortedShippingChannels(channelsData?.channels); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(shippingChannels); + + const [createShippingRate, createShippingRateOpts] = useShippingRateCreate( + {} + ); + + const handleSubmit = async (data: FormData) => { + const response = await createShippingRate({ + variables: getCreateShippingWeightRateVariables(data, id) + }); + const errors = response.data.shippingPriceCreate.errors; + if (errors.length === 0) { + updateShippingMethodChannelListing({ + variables: getShippingMethodChannelVariables( + response.data.shippingPriceCreate.shippingMethod.id, + data.noLimits, + data.channelListings + ) + }); + } else { + errors.map(err => + notify({ + status: "error", + text: getShippingErrorMessage(err, intl) + }) + ); + } + }; + + const handleBack = () => navigate(shippingZoneUrl(id)); + + return ( + <> + + {!!allChannels?.length && ( + + )} + + + ); +}; + +export default WeightRatesCreate; diff --git a/src/shipping/views/WeightRatesCreate/index.ts b/src/shipping/views/WeightRatesCreate/index.ts new file mode 100644 index 000000000..f710d2819 --- /dev/null +++ b/src/shipping/views/WeightRatesCreate/index.ts @@ -0,0 +1,2 @@ +export * from "./WeightRatesCreate"; +export { default } from "./WeightRatesCreate"; diff --git a/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx new file mode 100644 index 000000000..ebe8c19d7 --- /dev/null +++ b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx @@ -0,0 +1,188 @@ +import { useChannelsList } from "@saleor/channels/queries"; +import { + createShippingChannelsFromRate, + createSortedShippingChannels +} from "@saleor/channels/utils"; +import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useChannels from "@saleor/hooks/useChannels"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { sectionNames } from "@saleor/intl"; +import { commonMessages } from "@saleor/intl"; +import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; +import ShippingZoneRatesPage, { + FormData +} from "@saleor/shipping/components/ShippingZoneRatesPage"; +import { + getShippingMethodChannelVariables, + getUpdateShippingWeightRateVariables +} from "@saleor/shipping/handlers"; +import { + useShippingRateDelete, + useShippingRateUpdate +} from "@saleor/shipping/mutations"; +import { useShippingMethodChannelListingUpdate } from "@saleor/shipping/mutations"; +import { useShippingZone } from "@saleor/shipping/queries"; +import { shippingZoneUrl } from "@saleor/shipping/urls"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import getShippingErrorMessage from "@saleor/utils/errors/shipping"; +import React from "react"; +import { useIntl } from "react-intl"; + +export interface WeightRatesUpdateProps { + id: string; + rateId: string; +} + +export const WeightRatesUpdate: React.FC = ({ + id, + rateId +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + + const { data, loading } = useShippingZone({ + displayLoader: true, + variables: { id } + }); + + const rate = data?.shippingZone?.shippingMethods.find( + rate => rate.id === rateId + ); + + const { data: channelsData } = useChannelsList({}); + const [ + updateShippingMethodChannelListing, + updateShippingMethodChannelListingOpts + ] = useShippingMethodChannelListingUpdate({}); + const shippingChannels = createShippingChannelsFromRate( + rate?.channelListings + ); + const allChannels = createSortedShippingChannels(channelsData?.channels); + + const { + channelListElements, + channelsToggle, + currentChannels, + handleChannelsConfirm, + handleChannelsModalClose, + handleChannelsModalOpen, + isChannelSelected, + isChannelsModalOpen, + setCurrentChannels, + toggleAllChannels + } = useChannels(shippingChannels); + + const [openModal, setOpenModal] = React.useState(false); + + const [updateShippingRate, updateShippingRateOpts] = useShippingRateUpdate( + {} + ); + + const handleSuccess = () => { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges) + }); + }; + + const [deleteShippingRate, deleteShippingRateOpts] = useShippingRateDelete({ + onCompleted: data => { + if (data.shippingPriceDelete.errors.length === 0) { + handleSuccess(); + navigate(shippingZoneUrl(id)); + } + } + }); + + const handleDelete = () => setOpenModal(true); + const handleSubmit = async (data: FormData) => { + const response = await updateShippingRate({ + variables: getUpdateShippingWeightRateVariables(data, id, rateId) + }); + const errors = response.data.shippingPriceUpdate.errors; + if (errors.length === 0) { + handleSuccess(); + updateShippingMethodChannelListing({ + variables: getShippingMethodChannelVariables( + rateId, + data.noLimits, + data.channelListings, + shippingChannels + ) + }); + } else { + errors.map(err => + notify({ + status: "error", + text: getShippingErrorMessage(err, intl) + }) + ); + } + }; + + const handleBack = () => navigate(shippingZoneUrl(id)); + + return ( + <> + + {!!allChannels?.length && ( + + )} + setOpenModal(false)} + handleConfirm={() => + deleteShippingRate({ + variables: { + id: rateId + } + }) + } + open={openModal} + name={rate?.name} + /> + + + ); +}; + +export default WeightRatesUpdate; diff --git a/src/shipping/views/WeightRatesUpdate/index.ts b/src/shipping/views/WeightRatesUpdate/index.ts new file mode 100644 index 000000000..63e415555 --- /dev/null +++ b/src/shipping/views/WeightRatesUpdate/index.ts @@ -0,0 +1,2 @@ +export * from "./WeightRatesUpdate"; +export { default } from "./WeightRatesUpdate"; diff --git a/src/staff/components/StaffListPage/StaffListPage.tsx b/src/staff/components/StaffListPage/StaffListPage.tsx index e637936a6..47515ecc4 100644 --- a/src/staff/components/StaffListPage/StaffListPage.tsx +++ b/src/staff/components/StaffListPage/StaffListPage.tsx @@ -34,7 +34,6 @@ export interface StaffListPageProps } const StaffListPage: React.FC = ({ - currencySymbol, currentTab, filterOpts, initialSearch, @@ -72,7 +71,6 @@ const StaffListPage: React.FC = ({ defaultMessage: "All Staff Members", description: "tab name" })} - currencySymbol={currencySymbol} currentTab={currentTab} filterStructure={structure} initialSearch={initialSearch} diff --git a/src/staff/views/StaffList/StaffList.tsx b/src/staff/views/StaffList/StaffList.tsx index 5b1c6ce45..290c2a145 100644 --- a/src/staff/views/StaffList/StaffList.tsx +++ b/src/staff/views/StaffList/StaffList.tsx @@ -11,7 +11,6 @@ import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import { getStringOrPlaceholder } from "@saleor/misc"; import usePermissionGroupSearch from "@saleor/searches/usePermissionGroupSearch"; @@ -61,10 +60,8 @@ export const StaffList: React.FC = ({ params }) => { ListViews.STAFF_MEMBERS_LIST ); const intl = useIntl(); - const shop = useShop(); const paginationState = createPaginationState(settings.rowNumber, params); - const currencySymbol = shop?.defaultCurrency || "USD"; const queryVariables = React.useMemo( () => ({ ...paginationState, @@ -171,7 +168,6 @@ export const StaffList: React.FC = ({ params }) => { return ( <> - Visibility + Availability
+ > + +
-
- - -
+ Available at 3 out of 4 channels

- - -
-
-
- Set availability date -
-
-
- - - -

+

- Show in product listings -

- - Disabling this checkbox will remove product from search and category pages. It will be available on collection pages. - - - + Visible since 07/14/2020 +
+
+
+
+
+
+
+ Channel2 +
+ +
+
+ Will become available on 07/30/2020 +
+
+
+
@@ -1658,6 +1455,299 @@ exports[`Storyshots Generics / Card menu default 1`] = ` `; +exports[`Storyshots Generics / ChannelsAvailability default 1`] = ` +
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 3 out of 4 channels +
+
+
+
+
+ Channel1 +
+
+
+
+
+
+
+ Channel2 +
+
+
+
+
+
+
+`; + +exports[`Storyshots Generics / ChannelsAvailability with onChange 1`] = ` +
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 3 out of 4 channels +
+
+
+
+
+
+ Channel1 +
+ +
+
+
+
+
+
+
+
+
+ Channel2 +
+ +
+
+
+
+
+
+
+
+`; + +exports[`Storyshots Generics / ChannelsAvailabilityDialog default 1`] = ` +
+`; + +exports[`Storyshots Generics / ChannelsAvailabilityDialog disabled 1`] = ` +
+`; + +exports[`Storyshots Generics / ChannelsAvailabilityDialog with text 1`] = ` +
+`; + +exports[`Storyshots Generics / ChannelsAvailabilityDropdown default 1`] = ` +
+
+
+
+ Available in 3/6 +
+
+
+
+`; + +exports[`Storyshots Generics / ChannelsSelect default 1`] = ` +
+
+ Channel: +
+ + + Channel1 + + + +
+`; + exports[`Storyshots Generics / Checkbox checked 1`] = `
`; +exports[`Storyshots Orders / Draft order channel section default 1`] = ` +
+
+
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
+
+
+`; + +exports[`Storyshots Orders / Draft order channel section loading 1`] = ` +
+
+
+
+
+ + Sales channel + +
+
+
+
+
+ + ‌ + +
+
+
+
+
+`; + +exports[`Storyshots Orders / Order details channel section default 1`] = ` +
+
+
+
+
+ + Sales channel + +
+
+
+
+
+
+ International store +
+
+
+
+
+
+`; + +exports[`Storyshots Orders / Order details channel section loading 1`] = ` +
+
+
+
+
+ + Sales channel + +
+
+
+
+
+ + ‌ + +
+
+
+
+
+`; + exports[`Storyshots Orders / OrderAddressEditDialog billing address 1`] = `
`; -exports[`Storyshots Shipping / Rate details default 1`] = ` +exports[`Storyshots Shipping / DeleteShippingRateDialog default 1`] = `
`; -exports[`Storyshots Shipping / Rate details form errors 1`] = ` +exports[`Storyshots Shipping / DeleteShippingRateDialog loading 1`] = `
`; -exports[`Storyshots Shipping / Rate details loading 1`] = ` +exports[`Storyshots Shipping / Order value rates default 1`] = `
+> +
+
+ + Order value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ Channel USD +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
`; -exports[`Storyshots Shipping / Rate details new 1`] = ` +exports[`Storyshots Shipping / Order value rates loading 1`] = `
+> +
+
+ + Order value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ Channel USD +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
`; -exports[`Storyshots Shipping / Rate details weight 1`] = ` +exports[`Storyshots Shipping / Order weight rates default 1`] = `
+> +
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / Order weight rates form errors 1`] = ` +
+
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / Order weight rates loading 1`] = ` +
+
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / Order weight rates new 1`] = ` +
+
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / Pricing Card default 1`] = ` +
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ Channel USD +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / Pricing Card loading 1`] = ` +
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ Channel USD +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+`; + +exports[`Storyshots Shipping / ShippingZoneRates page default price 1`] = ` +
+
+
+
+
+ Price Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 1 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRates page default weight 1`] = ` +
+
+
+
+
+ Weight Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 1 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRates page loading 1`] = ` +
+
+
+
+
+ Price Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 1 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRates page update price 1`] = ` +
+
+
+
+
+ UPS +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ test +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ test +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 2 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+ test +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRates page update weight 1`] = ` +
+
+
+
+
+ DB Schenker +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ test +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 2 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+ test +
+
+
+
+
+
+
+
+
+ +
`; exports[`Storyshots SiteSettings / Add key dialog default 1`] = ` @@ -34795,6 +39240,40 @@ exports[`Storyshots Views / Categories / Update category no products 1`] = `
+
+
+ Channel: +
+ + + Channel1 + + + +
@@ -34845,7 +39324,7 @@ exports[`Storyshots Views / Categories / Update category no products 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id CategoryProductList-colPublished-id" scope="col" > - Published + Availability +
+
+ Channel: +
+ + + Channel1 + + + +
@@ -36037,7 +40550,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id CategoryProductList-colPublished-id" scope="col" > - Published + Availability -
- Published +
+
+
+ Available in 2/2 +
+
- $3.00 - $8.00 + $1.00 -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - -
- Published +
+
+
+ Available in 1/2 +
+
- $3.00 - $8.00 + - @@ -36916,6 +41509,2555 @@ exports[`Storyshots Views / Categories / Update category products 1`] = `
`; +exports[`Storyshots Views / Channels / Channel details default 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Inactive +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel details disabled 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Inactive +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel details loading 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Inactive +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel details with data 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Active +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel details with errors 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+

+ Slug must be unique +

+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Inactive +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel details without editable currency code 1`] = ` +
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+ Selected Currency +
+
+ zl +
+
+
+
+
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Active +
+ +
+
+
+
+ +
+`; + +exports[`Storyshots Views / Channels / Channel form default 1`] = ` +
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+ Selected Currency +
+
+ euro +
+
+
+
+`; + +exports[`Storyshots Views / Channels / Channel form disabled 1`] = ` +
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+ Selected Currency +
+
+ euro +
+
+
+
+`; + +exports[`Storyshots Views / Channels / Channel form with errors 1`] = ` +
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+ Copy +
+ +
+

+ Slug must be unique +

+
+
+
+
+
+
+
+ + Channel Settings + +
+
+
+
+
+
+ Selected Currency +
+
+ euro +
+
+
+
+`; + +exports[`Storyshots Views / Channels / Channel status active 1`] = ` +
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Active +
+ +
+
+
+`; + +exports[`Storyshots Views / Channels / Channel status inactive 1`] = ` +
+
+
+ + Channel Status + +
+
+
+
+
+
+ Status +
+
+ Inactive +
+ +
+
+
+`; + +exports[`Storyshots Views / Channels / Channels list default 1`] = ` +
+
+
+
+ Channels +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Channel Name +
+
+
+ Actions +
+ + Test + + + +
+ + Channel + + + +
+ + Channel test + + + +
+ + Channel USD + + + +
+ + Channel + + + +
+ + Channel test + + + +
+ + Channel USD + + + +
+
+
+
+
+`; + +exports[`Storyshots Views / Channels / Channels list empty 1`] = ` +
+
+
+
+ Channels +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + +
+
+
+ Channel Name +
+
+
+ Actions +
+ No channels found +
+
+
+
+
+`; + +exports[`Storyshots Views / Channels / Delete channel default 1`] = ` +
+`; + +exports[`Storyshots Views / Channels / Delete channel without channels to choose 1`] = ` +
+`; + +exports[`Storyshots Views / Channels / Settings dialog default 1`] = ` +
+`; + exports[`Storyshots Views / Collections / Collection details default 1`] = `
- Published + Availability -
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
- Visibility + Availability
+ > + +
+ Available at 1 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
-
-
-
- -
+
@@ -38939,7 +46000,7 @@ exports[`Storyshots Views / Collections / Collection details form errors 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id CollectionProducts-colPublished-id" scope="col" > - Published + Availability -
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
-
- Published +
+
+
+ Available in 2/2 +
+
- Visibility + Availability
+ > + +
+ Available at 1 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
-
-
-
-
-
- -
@@ -40045,7 +47019,7 @@ exports[`Storyshots Views / Collections / Collection details loading 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id CollectionProducts-colPublished-id" scope="col" > - Published + Availability - Visibility + Availability
+ > + +
+ Available at 1 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
-
- Set publication date -
-
-
-
- -
+
@@ -41122,7 +47976,7 @@ exports[`Storyshots Views / Collections / Collection details no products 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id CollectionProducts-colPublished-id" scope="col" > - Published + Availability - Visibility + Availability
+ > + +
+ Available at 1 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
-
-
-
- -
+
@@ -41564,21 +48303,8 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
-
- -
@@ -41887,13 +48613,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -41949,13 +48683,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -42011,13 +48753,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -42073,13 +48823,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -42135,13 +48893,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -42197,13 +48963,21 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = ` -
- Published +
+
+
+ Available in 1/2 +
+
@@ -42293,21 +49067,8 @@ exports[`Storyshots Views / Collections / Collection list loading 1`] = `
-
- -
@@ -42628,7 +49389,8 @@ exports[`Storyshots Views / Collections / Collection list loading 1`] = `
-
- -
@@ -43367,11 +50116,23 @@ exports[`Storyshots Views / Collections / Create collection default 1`] = ` - Visibility + Availability
+ > + +
+ Available at 7 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
+
- Set publication date +
+
+
+ Channel +
+ +
+
+ Hidden +
+
+
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
@@ -43916,11 +50852,23 @@ exports[`Storyshots Views / Collections / Create collection form errors 1`] = ` - Visibility + Availability
+ > + +
+ Available at 7 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
+
- Set publication date +
+
+
+ Channel +
+ +
+
+ Hidden +
+
-
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
@@ -44465,11 +51582,23 @@ exports[`Storyshots Views / Collections / Create collection loading 1`] = ` - Visibility + Availability
+ > + +
+ Available at 7 out of 2 channels +
+
+
- -
+
- - - -
- - -
-
-
- -

- Hidden -

-
- + Hidden +
+
- Set publication date +
+
+
+ Channel +
+ +
+
+ Hidden +
+
+
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel test +
+ +
+
+ Hidden +
+
+
+
+
+
+
+
+ Channel USD +
+ +
+
+ Hidden +
+
+
+
@@ -55799,6 +63099,166 @@ exports[`Storyshots Views / Discounts / Sale create default 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
@@ -56193,6 +63653,166 @@ exports[`Storyshots Views / Discounts / Sale create form errors 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
@@ -56579,6 +64199,166 @@ exports[`Storyshots Views / Discounts / Sale create loading 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
@@ -56839,42 +64619,429 @@ exports[`Storyshots Views / Discounts / Sale details collections 1`] = ` class="CardTitle-hr-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - % - + + + + Channel name + + + + + Value + + + + + + + +
+ Test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + +
@@ -57361,7 +65528,7 @@ exports[`Storyshots Views / Discounts / Sale details collections 1`] = `
- 30% + 1%
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -57658,42 +65986,429 @@ exports[`Storyshots Views / Discounts / Sale details default 1`] = ` class="CardTitle-hr-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - % - + + + + Channel name + + + + + Value + + + + + + + +
+ Test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + +
@@ -58180,7 +66895,7 @@ exports[`Storyshots Views / Discounts / Sale details default 1`] = `
- 30% + 1%
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -58482,48 +67358,430 @@ exports[`Storyshots Views / Discounts / Sale details form errors 1`] = ` class="CardTitle-hr-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - % - + + + + Channel name + + + + + Value + + + + + + + +
+ Test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + +
-

- Invalid value -

@@ -59019,7 +68277,7 @@ exports[`Storyshots Views / Discounts / Sale details form errors 1`] = `
- 30% + 1%
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -59326,43 +68745,436 @@ exports[`Storyshots Views / Discounts / Sale details loading 1`] = ` class="CardTitle-hr-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - USD - + + + + Channel name + + + + + Value + + + + + + + +
+ Test +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + euro + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + euro + +
+
+ + + +
@@ -59917,6 +69729,167 @@ exports[`Storyshots Views / Discounts / Sale details loading 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -60178,42 +70151,429 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="CardTitle-hr-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - % - + + + + Channel name + + + + + Value + + + + + + + +
+ Test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + % + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + % + +
+
+ + + +
@@ -60358,7 +70718,7 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="MuiTableCell-root-id MuiTableCell-head-id DiscountProducts-colPublished-id" scope="col" > - Published + Availability -
- Published +
+
+
+ Available in 1/7 +
+
-
- Published +
+
+
+ Available in 1/7 +
+
-
- Published +
+
+
+ Available in 1/7 +
+
-
- Published +
+
+
+ Available in 1/7 +
+
- 30% + 1%
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -61096,6 +71649,33 @@ exports[`Storyshots Views / Discounts / Sale list default 1`] = `
+
+ +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ Name +
+ +
+
+
+
+ Starts +
+
+
+
+
+ Ends +
+
+
+
+
+ Value +
+
+
+ + + + + + + + Happy front day! + + + + - + + _ +
+ + + + + + + + Happy minute day! + + + + - + + _ +
+ + + + + + + + Happy class day! + + + + - + + _ +
+ + + + + + + + Happy human day! + + + + - + + _ +
+ + + + + + + + Happy year day! + + + + - + + _ +
+
+
+
+
+`; + exports[`Storyshots Views / Discounts / Sale list no data 1`] = `
+
+ +
+
@@ -62874,56 +74201,487 @@ exports[`Storyshots Views / Discounts / Voucher create default 1`] = ` class="MuiCardContent-root-id" >
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- -
- - USD - -
- + + +
+ Test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + +
-
@@ -63417,6 +75175,166 @@ exports[`Storyshots Views / Discounts / Voucher create default 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
@@ -63728,6 +75646,9 @@ exports[`Storyshots Views / Discounts / Voucher create form errors 1`] = `
+
@@ -63753,56 +75674,487 @@ exports[`Storyshots Views / Discounts / Voucher create form errors 1`] = ` class="MuiCardContent-root-id" >
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- -
- - USD - -
- + + +
+ Test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + +
-
@@ -64306,6 +76658,166 @@ exports[`Storyshots Views / Discounts / Voucher create form errors 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 7 out of 7 channels +
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
@@ -64624,48 +77136,544 @@ exports[`Storyshots Views / Discounts / Voucher details default 1`] = ` class="MuiCardContent-root-id" >
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- -
- - USD - -
- + + +
+ Channel1 +
+ + +
+ +
+ +
+ + USD + +
+ +
+
+ + + + +
+ Test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + +
-
@@ -65041,38 +78046,480 @@ exports[`Storyshots Views / Discounts / Voucher details default 1`] = ` class="FormSpacer-spacer-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - + + + + Channel name + + + + + Value + + + + + + + +
+ Channel1 +
+ + +
+ +
+ + + +
+
+ + + + +
+ Test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + + +
+
+ + + +
@@ -65389,7 +78836,7 @@ exports[`Storyshots Views / Discounts / Voucher details default 1`] = `
- $25.00 + $1.00
- $200.00 + $1.00
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 8 out of 8 channels +
+
+
+
+
+ Channel1 +
+
+
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -65786,54 +79410,545 @@ exports[`Storyshots Views / Discounts / Voucher details form errors 1`] = ` class="MuiCardContent-root-id" >
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- -
- - USD - -
- + + +
+ Channel1 +
+ + +
+ +
+ +
+ + USD + +
+ +
+
+ + + + +
+ Test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + +
-

- Invalid value -

-
@@ -66213,44 +80325,481 @@ exports[`Storyshots Views / Discounts / Voucher details form errors 1`] = ` class="FormSpacer-spacer-id" />
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- - + + + + Channel name + + + + + Value + + + + + + + +
+ Channel1 +
+ + +
+ +
+ + + +
+
+ + + + +
+ Test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ + + +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ + + +
+
+ + + +
-

- Invalid value -

@@ -66576,7 +81125,7 @@ exports[`Storyshots Views / Discounts / Voucher details form errors 1`] = `
- $25.00 + $1.00
- $200.00 + $1.00
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 8 out of 8 channels +
+
+
+
+
+ Channel1 +
+
+
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -66974,49 +81700,552 @@ exports[`Storyshots Views / Discounts / Voucher details loading 1`] = ` class="MuiCardContent-root-id" >
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
-
- -
- - USD - -
- + + +
+ Channel1 +
+ + +
+ +
+ +
+ + USD + +
+ +
+
+ + + + +
+ Test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel test +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + + +
+ Channel USD +
+ + +
+ +
+ +
+ + euro + +
+ +
+
+ + + +
-
@@ -67812,6 +83038,183 @@ exports[`Storyshots Views / Discounts / Voucher details loading 1`] = `
+
+
+
+ + Availability + +
+ +
+
+
+
+
+
+ Available at 8 out of 8 channels +
+
+
+
+
+ Channel1 +
+
+
+
+
+
+
+ Test +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
+
+ Channel +
+
+
+
+
+
+
+ Channel test +
+
+
+
+
+
+
+ Channel USD +
+
+
+
+
+
@@ -67820,6 +83223,1096 @@ exports[`Storyshots Views / Discounts / Voucher details loading 1`] = ` `; exports[`Storyshots Views / Discounts / Voucher list default 1`] = ` +
+
+
+
+ Vouchers +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ Code +
+ +
+
+
+
+ Min. Spent +
+
+
+
+
+ Starts +
+
+
+
+
+ Ends +
+
+
+
+
+ Value +
+
+
+
+
+ Uses +
+
+
+ + + + + + + + FREE2019 + + $1.00 + + + + - + + 1% + + - +
+ + + + + + + + FREE2020 + + $1.00 + + + + - + + $1.00 + + 150 +
+
+
+
+
+`; + +exports[`Storyshots Views / Discounts / Voucher list loading 1`] = ` +
+
+
+
+ Vouchers +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ Code +
+ +
+
+
+
+ Min. Spent +
+
+
+
+
+ Starts +
+
+
+
+
+ Ends +
+
+
+
+
+ Value +
+
+
+
+
+ Uses +
+
+
+ + + + + + + + + ‌ + + + + ‌ + + + + ‌ + + + + ‌ + + + + ‌ + + + + ‌ + +
+
+
+
+
+`; + +exports[`Storyshots Views / Discounts / Voucher list no channels 1`] = `
@@ -68254,7 +84747,11 @@ exports[`Storyshots Views / Discounts / Voucher list default 1`] = ` - - + + ‌ + - 100% + + ‌ + - $200.00 + + ‌ + - $25.00 + + ‌ + `; -exports[`Storyshots Views / Discounts / Voucher list loading 1`] = ` -
-
-
-
- Vouchers -
-
-
- -
-
-
-
-
-
-
- - -
-
-
-
-
- -
-
-
- - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
-
- Code -
- -
-
-
-
- Min. Spent -
-
-
-
-
- Starts -
-
-
-
-
- Ends -
-
-
-
-
- Value -
-
-
-
-
- Uses -
-
-
- - - - - - - - - ‌ - - - - ‌ - - - - ‌ - - - - ‌ - - - - ‌ - - - - ‌ - -
-
-
-
-
-`; - exports[`Storyshots Views / Discounts / Voucher list no data 1`] = `
+
+ +
+
+
+
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -80813,9 +97319,6 @@ exports[`Storyshots Views / Orders / Order details default 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -82430,9 +98967,6 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -83674,9 +100242,6 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+ + ‌ + +
+
+
@@ -84754,9 +101353,6 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -86371,9 +103001,6 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -87988,9 +104649,6 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -89605,9 +106297,6 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -91222,9 +107945,6 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -92839,9 +109593,6 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -94456,9 +111241,6 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -96073,9 +112889,6 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -97690,9 +114537,6 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -99307,9 +116185,6 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = `
-
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
+
@@ -100744,6 +117653,40 @@ exports[`Storyshots Views / Orders / Order draft default 1`] = `
+
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
@@ -100831,19 +117774,7 @@ exports[`Storyshots Views / Orders / Order draft loading 1`] = `
- -
+ />
+
+
+
+ + Sales channel + +
+
+
+
+
+ + ‌ + +
+
@@ -101751,6 +118716,40 @@ exports[`Storyshots Views / Orders / Order draft no user permissions 1`] = `
+
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
@@ -102087,6 +119086,40 @@ exports[`Storyshots Views / Orders / Order draft without lines 1`] = `
+
+
+
+ + Sales channel + +
+
+
+
+
+
+ Default Channel +
+
+
@@ -107492,10 +124525,10 @@ exports[`Storyshots Views / Permission Groups / Permission Group Create default class="MuiIconButton-label-id" > @@ -107520,7 +124553,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group Create default
@@ -108386,7 +125419,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group Create errors 1
@@ -109250,7 +126283,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group Create loading
@@ -110645,7 +127678,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group Details default
@@ -112289,7 +129322,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group Details no memb