diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb784847..f8caf1fad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ All notable, unreleased changes to this project will be documented in this file. - Filter column ids before send it to GridAttributes operation - #476 by @gabmartinez - Display Is Published column correctly in main Product Listing - #475 by @gabmartinez - Add Permission Groups section - #406 by @krzysztofwolski +- Add warehouse management - #390 by @dominik-zeglen ## 2.0.0 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index f656ec321..cf600f637 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1,8 +1,4 @@ { - "OrderCancelDialogButton": { - "context": "button", - "string": "Cancel Order" - }, "OrderCancelDialogHeader": { "context": "dialog header", "string": "Cancel Order" @@ -34,6 +30,9 @@ "configurationMenuTaxes": { "string": "Manage how your store charges tax" }, + "configurationMenuWarehouses": { + "string": "Manage and update your warehouse information" + }, "configurationPluginsPages": { "string": "View and update your plugins and their settings." }, @@ -206,10 +205,6 @@ "context": "vat not included in order price", "string": "does not apply" }, - "prodictStockInventoryLabel": { - "context": "product stock", - "string": "Inventory" - }, "productStatusLabel": { "context": "product", "string": "Published" @@ -240,13 +235,17 @@ "context": "variant price", "string": "Price" }, - "productVariantCreatePricesSetStockPlaceholder": { - "context": "variant stock", - "string": "Stock" - }, "productVariantCreatePricesStockInputLabel": { "string": "Stock" }, + "productVariantCreatorStockSectionHeader": { + "context": "variant stock, header", + "string": "Stock" + }, + "productVariantCreatorWarehouseSectionHeader": { + "context": "header", + "string": "Warehouses" + }, "productVariantPriceOptionalCostPriceField": { "context": "optional field", "string": "Optional" @@ -287,10 +286,18 @@ "context": "delete shipping zone", "string": "Are you sure you want to delete {name}?" }, + "shippingZoneWarehouses_dot_autocomplete_dot_label": { + "context": "autocomplete select label", + "string": "Warehouse" + }, "siteSettingsMailingHelperText": { "context": "helper text", "string": "Mailing Configuration" }, + "src_dot_accept": { + "context": "button", + "string": "Accept" + }, "src_dot_attributes": { "context": "attributes section name", "string": "Attributes" @@ -1020,6 +1027,30 @@ "context": "pick columns to display", "string": "{numberOfSelected} columns selected out of {numberOfTotal}" }, + "src_dot_components_dot_CompanyAddressInput_dot_1139500589": { + "string": "Country" + }, + "src_dot_components_dot_CompanyAddressInput_dot_1271289966": { + "string": "Phone" + }, + "src_dot_components_dot_CompanyAddressInput_dot_1363074570": { + "string": "Address line 1" + }, + "src_dot_components_dot_CompanyAddressInput_dot_253031977": { + "string": "City" + }, + "src_dot_components_dot_CompanyAddressInput_dot_2965971965": { + "string": "ZIP / Postal code" + }, + "src_dot_components_dot_CompanyAddressInput_dot_3121963259": { + "string": "Address line 2" + }, + "src_dot_components_dot_CompanyAddressInput_dot_3570415321": { + "string": "Company" + }, + "src_dot_components_dot_CompanyAddressInput_dot_944851093": { + "string": "Country area" + }, "src_dot_components_dot_ConfirmButton_dot_2845142593": { "context": "button", "string": "Error" @@ -2191,6 +2222,10 @@ "src_dot_no": { "string": "No" }, + "src_dot_ok": { + "context": "button", + "string": "OK" + }, "src_dot_optionalField": { "context": "field is optional", "string": "Optional" @@ -2211,19 +2246,18 @@ "context": "dialog header", "string": "Cancel Orders" }, - "src_dot_orders_dot_components_dot_OrderBulkCancelDialog_dot_187921539": { - "context": "switch button", - "string": "Release all stock allocated to these orders" - }, "src_dot_orders_dot_components_dot_OrderBulkCancelDialog_dot_4224885638": { "string": "{counter,plural,one{Are you sure you want to cancel this order?} other{Are you sure you want to cancel {displayQuantity} orders?}}" }, - "src_dot_orders_dot_components_dot_OrderCancelDialog_dot_3981375672": { - "string": "Are you sure you want to cancel order #{orderNumber}?" + "src_dot_orders_dot_components_dot_OrderCancelDialog_dot_4059738695": { + "string": "Cancelling this order will release unfulfilled stocks, so they can be bought by other customers. Order will not be refunded when cancelling order - You need to do it manually. Are you sure you want to cancel this order?" }, - "src_dot_orders_dot_components_dot_OrderCancelDialog_dot_944150063": { - "context": "switch button", - "string": "Release all stock allocated to this order" + "src_dot_orders_dot_components_dot_OrderCannotCancelOrderDialog_dot_1561587911": { + "context": "dialog header", + "string": "Saleor couldn’t cancel order" + }, + "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_OrderCustomerNote_dot_1505053535": { "string": "No notes from customer" @@ -2392,16 +2426,51 @@ "context": "button", "string": "Finalize" }, - "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_2569854889": { - "string": "Are you sure you want to cancel this fulfillment?" + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_1608534452": { + "context": "page header", + "string": "Order no. {orderNumber} - Add Fulfillment" }, - "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_3515223857": { - "context": "switch button", - "string": "Restock items?" + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_2094985970": { + "context": "quantity of fulfilled products", + "string": "Quantity to fulfill" }, - "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_675709443": { - "context": "button", - "string": "Cancel fulfillment" + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_2095687440": { + "context": "fulfill order, button", + "string": "Fulfill" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_2588284040": { + "context": "no variant stock in warehouse", + "string": "No Stock" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_3244948255": { + "context": "header", + "string": "Items ready to ship" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_3620521256": { + "context": "page header", + "string": "Order" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_4046223826": { + "string": "Product name" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_4251997263": { + "context": "checkbox", + "string": "Send shipment details to customer" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_580490159": { + "context": "page header with order number", + "string": "Order #{orderNumber}" + }, + "src_dot_orders_dot_components_dot_OrderFulfillPage_dot_693960049": { + "context": "product's sku", + "string": "SKU" + }, + "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_1097287358": { + "string": "Are you sure you want to cancel fulfillment? Canceling a fulfillment will restock products at a selected warehouse." + }, + "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_46197273": { + "context": "select warehouse to restock items", + "string": "Select Warehouse" }, "src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_732594284": { "context": "dialog header", @@ -2456,9 +2525,17 @@ "context": "section header", "string": "Fulfilled ({quantity})" }, - "src_dot_orders_dot_components_dot_OrderFulfillment_dot_662203348": { + "src_dot_orders_dot_components_dot_OrderFulfillment_dot_4039425374": { + "context": "cancelled fulfillment, section header", + "string": "Cancelled ({quantity})" + }, + "src_dot_orders_dot_components_dot_OrderFulfillment_dot_732594284": { "context": "button", - "string": "Cancel shipment" + "string": "Cancel Fulfillment" + }, + "src_dot_orders_dot_components_dot_OrderFulfillment_dot_77179533": { + "context": "fulfillment group", + "string": "Fulfilled from: {warehouseName}" }, "src_dot_orders_dot_components_dot_OrderFulfillment_dot_878013594": { "context": "order line total price", @@ -2766,9 +2843,6 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": { "string": "Order successfully updated" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_4245651107": { - "string": "Items successfully fulfilled" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_580490159": { "context": "window title", "string": "Order #{orderNumber}" @@ -2800,16 +2874,21 @@ "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this order draft?} other{Are you sure you want to delete {displayQuantity} order drafts?}}" }, - "src_dot_orders_dot_views_dot_OrderList_dot_1136302661": { - "string": "Orders cancelled" + "src_dot_orders_dot_views_dot_OrderFulfill_dot_1186021594": { + "context": "window title", + "string": "Fulfill Order #{orderNumber}" + }, + "src_dot_orders_dot_views_dot_OrderFulfill_dot_1892156526": { + "context": "order fulfilled success message", + "string": "Fulfilled Items" + }, + "src_dot_orders_dot_views_dot_OrderFulfill_dot_3754102871": { + "context": "window title", + "string": "Fulfill Order" }, "src_dot_orders_dot_views_dot_OrderList_dot_1738939038": { "string": "Order draft successfully created" }, - "src_dot_orders_dot_views_dot_OrderList_dot_3528672691": { - "context": "cancel orders, button", - "string": "Cancel" - }, "src_dot_pages": { "context": "pages section name", "string": "Pages" @@ -3520,13 +3599,35 @@ "src_dot_products_dot_components_dot_ProductPricing_dot_3015886868": { "string": "Charge taxes for this item" }, - "src_dot_products_dot_components_dot_ProductStock_dot_1680952454": { - "context": "allocated product stock", - "string": "Allocated: {quantity}" - }, - "src_dot_products_dot_components_dot_ProductStock_dot_2585918415": { + "src_dot_products_dot_components_dot_ProductStocks_dot_2585918415": { "string": "SKU (Stock Keeping Unit)" }, + "src_dot_products_dot_components_dot_ProductStocks_dot_2622674857": { + "context": "tabel column header", + "string": "Warehouse Name" + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_2729628316": { + "context": "tabel column header", + "string": "Quantity Available" + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_2796503714": { + "context": "header", + "string": "Quantity" + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_3143542809": { + "string": "This product doesn't have any stock. You can add it here." + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_3482985373": { + "context": "button", + "string": "Edit Warehouses" + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_3633706025": { + "context": "product inventory, checkbox", + "string": "Track Inventory" + }, + "src_dot_products_dot_components_dot_ProductStocks_dot_849869830": { + "string": "Active inventory tracking will automatically calculate changes of stock" + }, "src_dot_products_dot_components_dot_ProductUpdatePage_dot_1815688500": { "context": "product", "string": "since {date}" @@ -3546,81 +3647,6 @@ "context": "product attribute error", "string": "This variant already exists" }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_1009678918": { - "context": "header", - "string": "You will create variants below" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_1134347598": { - "context": "variant price", - "string": "Price" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_168343345": { - "context": "variant attribute", - "string": "Attribute" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_2478977538": { - "context": "attribute values, variant creation step", - "string": "Select Values" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_2670525734": { - "context": "variant attribute", - "string": "Choose attribute" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_2745385064": { - "context": "variant creation step", - "string": "Summary" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_2783195765": { - "string": "Apply single price to all SKUs" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3387090508": { - "string": "Apply unique stock by attribute to each SKU" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3490038570": { - "context": "variant stock amount", - "string": "Inventory" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3528672691": { - "context": "button", - "string": "Cancel" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3570949907": { - "string": "Apply unique prices by attribute to each SKU" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3601538615": { - "string": "Apply single stock to all SKUs" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3673120330": { - "context": "button", - "string": "Next" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3841616483": { - "context": "variant stock, header", - "string": "Stock" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_3922579741": { - "context": "dialog header", - "string": "Assign Attribute" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_4120989039": { - "context": "create multiple variants, button", - "string": "Create" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_693960049": { - "string": "SKU" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_705096461": { - "context": "variant creation step", - "string": "Prices and SKU" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_904693740": { - "context": "previous step, button", - "string": "Previous" - }, - "src_dot_products_dot_components_dot_ProductVariantCreateDialog_dot_998917294": { - "context": "variant name", - "string": "Variant" - }, "src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_2853608829": { "context": "button", "string": "Save variant" @@ -3629,6 +3655,96 @@ "context": "button", "string": "Delete Variant" }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1134347598": { + "context": "variant price", + "string": "Price" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1346828628": { + "string": "Selected values will be used to create variants for the configurable product." + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_168343345": { + "context": "variant attribute", + "string": "Attribute" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_1808330403": { + "context": "variant attribute", + "string": "Select Attribute" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2002684673": { + "context": "page title", + "string": "Price and SKUs" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2020618004": { + "context": "variant stock, header", + "string": "Stock and Warehousing" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2158649399": { + "context": "product attribute values, page title", + "string": "Choose Values" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2478977538": { + "context": "attribute values, variant creation step", + "string": "Select Values" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2670525734": { + "context": "variant attribute", + "string": "Choose attribute" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2745385064": { + "context": "variant creation step", + "string": "Summary" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_2783195765": { + "string": "Apply single price to all SKUs" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3387090508": { + "string": "Apply unique stock by attribute to each SKU" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3570949907": { + "string": "Apply unique prices by attribute to each SKU" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3601538615": { + "string": "Apply single stock to all SKUs" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3673120330": { + "context": "button", + "string": "Next" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3699389906": { + "string": "Here is the summary of variants that will be created. You can change prices, stocks an SKU for each one created." + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_3896519183": { + "string": "Skip stock for now" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_4120989039": { + "context": "create multiple variants, button", + "string": "Create" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_520980261": { + "context": "variant creator summary card header", + "string": "Created Variants" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_551319747": { + "string": "Based on your selections we will create {numberOfProducts} products. Use this step to customize price and stocks for your new products" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_693960049": { + "string": "SKU" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_705096461": { + "context": "variant creation step", + "string": "Prices and SKU" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_860603977": { + "string": "Based on your selections we will create 8 products. Use this step to customize price and stocks for your new products." + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_904693740": { + "context": "previous step, button", + "string": "Previous" + }, + "src_dot_products_dot_components_dot_ProductVariantCreatorPage_dot_998917294": { + "context": "variant name", + "string": "Variant" + }, "src_dot_products_dot_components_dot_ProductVariantDeleteDialog_dot_1583616500": { "context": "button", "string": "Delete variant" @@ -3678,23 +3794,12 @@ "src_dot_products_dot_components_dot_ProductVariantPrice_dot_2238565650": { "string": "Selling price override" }, - "src_dot_products_dot_components_dot_ProductVariantStock_dot_1680952454": { - "context": "variant allocated stock", - "string": "Allocated: {quantity}" - }, - "src_dot_products_dot_components_dot_ProductVariantStock_dot_2585918415": { - "string": "SKU (Stock Keeping Unit)" - }, - "src_dot_products_dot_components_dot_ProductVariantStock_dot_3490038570": { - "context": "product variant stock", - "string": "Inventory" - }, - "src_dot_products_dot_components_dot_ProductVariantStock_dot_3841616483": { - "context": "product variant stock, section header", - "string": "Stock" + "src_dot_products_dot_components_dot_ProductVariants_dot_1001303107": { + "context": "product variant inventory", + "string": "Unavailable in all locations" }, "src_dot_products_dot_components_dot_ProductVariants_dot_1033175132": { - "context": "product variant status", + "context": "product variant inventory", "string": "Unavailable" }, "src_dot_products_dot_components_dot_ProductVariants_dot_1134347598": { @@ -3705,32 +3810,55 @@ "context": "button", "string": "Create variants" }, - "src_dot_products_dot_components_dot_ProductVariants_dot_1756106276": { - "context": "product variant status", - "string": "Status" - }, "src_dot_products_dot_components_dot_ProductVariants_dot_2153006789": { "context": "section header", "string": "Variants" }, - "src_dot_products_dot_components_dot_ProductVariants_dot_2157131639": { - "context": "product variant status", - "string": "Available" + "src_dot_products_dot_components_dot_ProductVariants_dot_2496096212": { + "context": "product variant inventory", + "string": "{numLocations,plural,one{{numAvailable} available at {numLocations} location} other{{numAvailable} available at {numLocations} locations}}" }, "src_dot_products_dot_components_dot_ProductVariants_dot_277989856": { "string": "Use variants for products that come in a variety of versions for example different sizes or colors" }, + "src_dot_products_dot_components_dot_ProductVariants_dot_3203451491": { + "context": "filtering option", + "string": "All Warehouses" + }, + "src_dot_products_dot_components_dot_ProductVariants_dot_3284706946": { + "context": "variant stock status", + "string": "Available inventoty at:" + }, + "src_dot_products_dot_components_dot_ProductVariants_dot_3490038570": { + "context": "product variant inventory status", + "string": "Inventory" + }, + "src_dot_products_dot_components_dot_ProductVariants_dot_387066717": { + "context": "product variant inventory", + "string": "{stockQuantity,plural,other{{stockQuantity} available}}" + }, "src_dot_products_dot_components_dot_ProductVariants_dot_3989383405": { "context": "button", "string": "Create variant" }, - "src_dot_products_dot_components_dot_ProductVariants_dot_636461959": { - "context": "product variant name", - "string": "Name" + "src_dot_products_dot_components_dot_ProductVariants_dot_4017567521": { + "context": "product variant inventory", + "string": "Not stocked" }, "src_dot_products_dot_components_dot_ProductVariants_dot_693960049": { "string": "SKU" }, + "src_dot_products_dot_components_dot_ProductVariants_dot_998917294": { + "context": "product variant name", + "string": "Variant" + }, + "src_dot_products_dot_components_dot_ProductWarehousesDialog_dot_3482985373": { + "context": "dialog header", + "string": "Edit Warehouses" + }, + "src_dot_products_dot_components_dot_ProductWarehousesDialog_dot_4112406382": { + "string": "Select warehouses that stock selected product" + }, "src_dot_products_dot_views_dot_1542417144": { "context": "window title", "string": "Create Product" @@ -3811,6 +3939,14 @@ "context": "dialog header", "string": "Delete Product" }, + "src_dot_products_dot_views_dot_ProductVariantCreator_dot_2292700443": { + "context": "success message", + "string": "Successfully created variants" + }, + "src_dot_products_dot_views_dot_ProductVariantCreator_dot_830692292": { + "context": "window title", + "string": "Create Variants" + }, "src_dot_properties": { "string": "Properties" }, @@ -4014,6 +4150,13 @@ "src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_549146363": { "string": "Shipping Weight Unit" }, + "src_dot_shipping_dot_components_dot_ShippingZoneAddWarehouseDialog_dot_2622674857": { + "string": "Warehouse Name" + }, + "src_dot_shipping_dot_components_dot_ShippingZoneAddWarehouseDialog_dot_3173942020": { + "context": "header, dialog", + "string": "Create New Warehouse" + }, "src_dot_shipping_dot_components_dot_ShippingZoneCountriesAssignDialog_dot_1003092716": { "string": "Rest of the World" }, @@ -4184,6 +4327,21 @@ "context": "shipping method name", "string": "Name" }, + "src_dot_shipping_dot_components_dot_ShippingZoneWarehouses_dot_1221560277": { + "context": "section header", + "string": "Warehouse" + }, + "src_dot_shipping_dot_components_dot_ShippingZoneWarehouses_dot_2304484478": { + "context": "button", + "string": "Add New Warehouse" + }, + "src_dot_shipping_dot_components_dot_ShippingZoneWarehouses_dot_3852102652": { + "string": "Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes." + }, + "src_dot_shipping_dot_components_dot_ShippingZoneWarehouses_dot_46197273": { + "context": "input placeholder", + "string": "Select Warehouse" + }, "src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1325966144": { "context": "header", "string": "Shipping" @@ -4245,34 +4403,6 @@ "context": "site settings section name", "string": "Site Settings" }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_1139500589": { - "string": "Country" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_1271289966": { - "string": "Phone" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_1363074570": { - "string": "Address line 1" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_229184360": { - "context": "section header", - "string": "Store Information" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_253031977": { - "string": "City" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_2965971965": { - "string": "ZIP / Postal code" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_3121963259": { - "string": "Address line 2" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_3570415321": { - "string": "Company" - }, - "src_dot_siteSettings_dot_components_dot_SiteSettingsAddress_dot_944851093": { - "string": "Country area" - }, "src_dot_siteSettings_dot_components_dot_SiteSettingsDetails_dot_1008586926": { "string": "Name of your store is shown on tab in web browser" }, @@ -4359,6 +4489,10 @@ "src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_1214877701": { "string": "Authentication method defines additional ways that customers can log in to your ecommerce." }, + "src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_229184360": { + "context": "section header", + "string": "Store Information" + }, "src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_2768400497": { "context": "section header", "string": "Company Information" @@ -4980,6 +5114,10 @@ "context": "bulk variant create error", "string": "SKUs must be unique" }, + "src_dot_utils_dot_errors_dot_slugUnique": { + "context": "error message", + "string": "Slug must be unique for each warehouse" + }, "src_dot_utils_dot_errors_dot_tooCommon": { "string": "This password is too commonly used" }, @@ -5002,6 +5140,68 @@ "context": "vouchers section name", "string": "Vouchers" }, + "src_dot_warehouses": { + "context": "warehouses section name", + "string": "Warehouses" + }, + "src_dot_warehouses_dot_components_dot_WarehouseCreatePage_dot_1967111456": { + "context": "warehouse", + "string": "Address Information" + }, + "src_dot_warehouses_dot_components_dot_WarehouseCreatePage_dot_934572530": { + "context": "header", + "string": "Create Warehouse" + }, + "src_dot_warehouses_dot_components_dot_WarehouseDeleteDialog_dot_1355303260": { + "context": "dialog content", + "string": "Are you sure you want to delete {warehouseName}?" + }, + "src_dot_warehouses_dot_components_dot_WarehouseDeleteDialog_dot_3857661071": { + "context": "dialog title", + "string": "Delete Warehouse" + }, + "src_dot_warehouses_dot_components_dot_WarehouseDetailsPage_dot_1967111456": { + "context": "warehouse", + "string": "Address Information" + }, + "src_dot_warehouses_dot_components_dot_WarehouseInfo_dot_2622674857": { + "string": "Warehouse Name" + }, + "src_dot_warehouses_dot_components_dot_WarehouseListPage_dot_2304765290": { + "string": "Search Warehouse" + }, + "src_dot_warehouses_dot_components_dot_WarehouseListPage_dot_3203451491": { + "context": "tab name", + "string": "All Warehouses" + }, + "src_dot_warehouses_dot_components_dot_WarehouseListPage_dot_934572530": { + "context": "button", + "string": "Create Warehouse" + }, + "src_dot_warehouses_dot_components_dot_WarehouseList_dot_1225932315": { + "string": "Shipping Zones" + }, + "src_dot_warehouses_dot_components_dot_WarehouseList_dot_3787396469": { + "string": "No warehouses found" + }, + "src_dot_warehouses_dot_components_dot_WarehouseList_dot_4190792473": { + "string": "Actions" + }, + "src_dot_warehouses_dot_components_dot_WarehouseList_dot_636461959": { + "context": "warehouse", + "string": "Name" + }, + "src_dot_warehouses_dot_components_dot_WarehouseZones_dot_1225932315": { + "context": "zones that warehouse sends to", + "string": "Shipping Zones" + }, + "src_dot_warehouses_dot_components_dot_WarehouseZones_dot_2423245141": { + "string": "This warehouse has no shipping zones assigned." + }, + "src_dot_warehouses_dot_views_dot_WarehouseCreate_dot_934572530": { + "context": "header", + "string": "Create Warehouse" + }, "src_dot_webhooks": { "context": "webhooks section name", "string": "Webhooks" diff --git a/package-lock.json b/package-lock.json index 0dd6faa25..158ec5659 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,56 +16,56 @@ } }, "@apollo/react-common": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.3.tgz", - "integrity": "sha512-Q7ZjDOeqjJf/AOGxUMdGxKF+JVClRXrYBGVq+SuVFqANRpd68MxtVV2OjCWavsFAN0eqYnRqRUrl7vtUCiJqeg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.4.tgz", + "integrity": "sha512-X5Kyro73bthWSCBJUC5XYQqMnG0dLWuDZmVkzog9dynovhfiVCV4kPSdgSIkqnb++cwCzOVuQ4rDKVwo2XRzQA==", "requires": { "ts-invariant": "^0.4.4", "tslib": "^1.10.0" } }, "@apollo/react-components": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@apollo/react-components/-/react-components-3.1.3.tgz", - "integrity": "sha512-H0l2JKDQMz+LkM93QK7j3ThbNXkWQCduN3s3eKxFN3Rdg7rXsrikJWvx2wQ868jmqy0VhwJbS1vYdRLdh114uQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@apollo/react-components/-/react-components-3.1.5.tgz", + "integrity": "sha512-c82VyUuE9VBnJB7bnX+3dmwpIPMhyjMwyoSLyQWPHxz8jK4ak30XszJtqFf4eC4hwvvLYa+Ou6X73Q8V8e2/jg==", "requires": { - "@apollo/react-common": "^3.1.3", - "@apollo/react-hooks": "^3.1.3", + "@apollo/react-common": "^3.1.4", + "@apollo/react-hooks": "^3.1.5", "prop-types": "^15.7.2", "ts-invariant": "^0.4.4", "tslib": "^1.10.0" } }, "@apollo/react-hoc": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@apollo/react-hoc/-/react-hoc-3.1.3.tgz", - "integrity": "sha512-oCPma0uBVPTcYTR5sOvtMbpaWll4xDBvYfKr6YkDorUcQVeNzFu1LK1kmQjJP64bKsaziKYji5ibFaeCnVptmA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@apollo/react-hoc/-/react-hoc-3.1.5.tgz", + "integrity": "sha512-jlZ2pvEnRevLa54H563BU0/xrYSgWQ72GksarxUzCHQW85nmn9wQln0kLBX7Ua7SBt9WgiuYQXQVechaaCulfQ==", "requires": { - "@apollo/react-common": "^3.1.3", - "@apollo/react-components": "^3.1.3", + "@apollo/react-common": "^3.1.4", + "@apollo/react-components": "^3.1.5", "hoist-non-react-statics": "^3.3.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0" } }, "@apollo/react-hooks": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.3.tgz", - "integrity": "sha512-reIRO9xKdfi+B4gT/o/hnXuopUnm7WED/ru8VQydPw+C/KG/05Ssg1ZdxFKHa3oxwiTUIDnevtccIH35POanbA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.5.tgz", + "integrity": "sha512-y0CJ393DLxIIkksRup4nt+vSjxalbZBXnnXxYbviq/woj+zKa431zy0yT4LqyRKpFy9ahMIwxBnBwfwIoupqLQ==", "requires": { - "@apollo/react-common": "^3.1.3", + "@apollo/react-common": "^3.1.4", "@wry/equality": "^0.1.9", "ts-invariant": "^0.4.4", "tslib": "^1.10.0" } }, "@apollo/react-ssr": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.3.tgz", - "integrity": "sha512-fUTmEYHxSTX1GA43B8vICxXXplpcEBnDwn0IgdAc3eG0p2YK97ZrJDRFCJ5vD7fyDZsrYhMf+rAI3sd+H2SS+A==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.5.tgz", + "integrity": "sha512-wuLPkKlctNn3u8EU8rlECyktpOUCeekFfb0KhIKknpGY6Lza2Qu0bThx7D9MIbVEzhKadNNrzLcpk0Y8/5UuWg==", "requires": { - "@apollo/react-common": "^3.1.3", - "@apollo/react-hooks": "^3.1.3", + "@apollo/react-common": "^3.1.4", + "@apollo/react-hooks": "^3.1.5", "tslib": "^1.10.0" } }, @@ -17593,15 +17593,15 @@ } }, "react-apollo": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-3.1.3.tgz", - "integrity": "sha512-orCZNoAkgveaK5b75y7fw1MSqSHOU/Wuu9rRFOGmRQBSQVZjvV4DI+hj604rHmuN9+WDABxb5W48wTa0F/xNZQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-3.1.5.tgz", + "integrity": "sha512-xOxMqxORps+WHrUYbjVHPliviomefOpu5Sh35oO3osuOyPTxvrljdfTLGCggMhcXBsDljtS5Oy4g+ijWg3D4JQ==", "requires": { - "@apollo/react-common": "^3.1.3", - "@apollo/react-components": "^3.1.3", - "@apollo/react-hoc": "^3.1.3", - "@apollo/react-hooks": "^3.1.3", - "@apollo/react-ssr": "^3.1.3" + "@apollo/react-common": "^3.1.4", + "@apollo/react-components": "^3.1.5", + "@apollo/react-hoc": "^3.1.5", + "@apollo/react-hooks": "^3.1.5", + "@apollo/react-ssr": "^3.1.5" } }, "react-clientside-effect": { diff --git a/package.json b/package.json index e11e138a5..5550d1f81 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "moment-timezone": "^0.5.26", "qs": "^6.9.0", "react": "^16.12.0", - "react-apollo": "^3.1.3", + "react-apollo": "^3.1.4", "react-dom": "^16.9.0", "react-dropzone": "^8.2.0", "react-error-boundary": "^1.2.5", diff --git a/schema.graphql b/schema.graphql index 710daf06a..98993d3bb 100644 --- a/schema.graphql +++ b/schema.graphql @@ -4,40 +4,28 @@ schema { } type AccountAddressCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address } type AccountAddressDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address } type AccountAddressUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address } type AccountDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -64,6 +52,7 @@ enum AccountErrorCode { LEFT_NOT_MANAGEABLE_PERMISSION INVALID_CREDENTIALS NOT_FOUND + OUT_OF_SCOPE_SERVICE_ACCOUNT OUT_OF_SCOPE_USER OUT_OF_SCOPE_GROUP OUT_OF_SCOPE_PERMISSION @@ -83,10 +72,7 @@ input AccountInput { } type AccountRegister { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") requiresConfirmation: Boolean accountErrors: [AccountError!]! user: User @@ -99,36 +85,24 @@ input AccountRegisterInput { } type AccountRequestDeletion { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! } type AccountSetDefaultAddress { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } type AccountUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } type AccountUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -151,20 +125,14 @@ type Address implements Node { } type AddressCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address } type AddressDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address @@ -185,10 +153,7 @@ input AddressInput { } type AddressSetDefault { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } @@ -199,10 +164,7 @@ enum AddressTypeEnum { } type AddressUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! address: Address @@ -228,39 +190,126 @@ type AddressValidationData { postalCodePrefix: String } +type App implements Node & ObjectWithMetadata { + id: ID! + name: String + created: DateTime + isActive: Boolean + permissions: [Permission] + tokens: [AppToken] + privateMetadata: [MetadataItem]! + metadata: [MetadataItem]! + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + webhooks: [Webhook] +} + +type AppCountableConnection { + pageInfo: PageInfo! + edges: [AppCountableEdge!]! + totalCount: Int +} + +type AppCountableEdge { + node: App! + cursor: String! +} + +type AppCreate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + authToken: String + appErrors: [AppError!]! + app: App +} + +type AppDelete { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + appErrors: [AppError!]! + app: App +} + +type AppError { + field: String + message: String + code: AppErrorCode! + permissions: [PermissionEnum!] +} + +enum AppErrorCode { + GRAPHQL_ERROR + INVALID + NOT_FOUND + REQUIRED + UNIQUE + OUT_OF_SCOPE_APP + OUT_OF_SCOPE_PERMISSION +} + +input AppFilterInput { + search: String + isActive: Boolean +} + +input AppInput { + name: String + isActive: Boolean + permissions: [PermissionEnum] +} + +enum AppSortField { + NAME + CREATION_DATE +} + +input AppSortingInput { + direction: OrderDirection! + field: AppSortField! +} + +type AppToken implements Node { + name: String + authToken: String + id: ID! +} + +type AppTokenCreate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + authToken: String + appErrors: [AppError!]! + appToken: AppToken +} + +type AppTokenDelete { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + appErrors: [AppError!]! + appToken: AppToken +} + +input AppTokenInput { + name: String + app: ID! +} + +type AppUpdate { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + appErrors: [AppError!]! + app: App +} + type AssignNavigation { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menu: Menu menuErrors: [MenuError!]! } type Attribute implements Node & ObjectWithMetadata { id: ID! - productTypes( - before: String - after: String - first: Int - last: Int - ): ProductTypeCountableConnection! - productVariantTypes( - before: String - after: String - first: Int - last: Int - ): ProductTypeCountableConnection! + productTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! + productVariantTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") inputType: AttributeInputTypeEnum name: String slug: String @@ -275,10 +324,7 @@ type Attribute implements Node & ObjectWithMetadata { } type AttributeAssign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productType: ProductType productErrors: [ProductAttributeError!]! } @@ -289,28 +335,19 @@ input AttributeAssignInput { } type AttributeBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type AttributeClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! attribute: Attribute } type AttributeClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! attribute: Attribute } @@ -327,10 +364,7 @@ type AttributeCountableEdge { } type AttributeCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! } @@ -350,10 +384,7 @@ input AttributeCreateInput { } type AttributeDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! attribute: Attribute } @@ -383,10 +414,7 @@ enum AttributeInputTypeEnum { } type AttributeReorderValues { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! } @@ -416,10 +444,7 @@ type AttributeTranslatableContent implements Node { } type AttributeTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! attribute: Attribute } @@ -436,19 +461,13 @@ enum AttributeTypeEnum { } type AttributeUnassign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productType: ProductType productErrors: [ProductError!]! } type AttributeUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! } @@ -468,19 +487,13 @@ input AttributeUpdateInput { } type AttributeUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! attribute: Attribute } type AttributeUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! attribute: Attribute } @@ -495,19 +508,13 @@ type AttributeValue implements Node { } type AttributeValueBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type AttributeValueCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! attributeValue: AttributeValue @@ -518,10 +525,7 @@ input AttributeValueCreateInput { } type AttributeValueDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! attributeValue: AttributeValue @@ -540,10 +544,7 @@ type AttributeValueTranslatableContent implements Node { } type AttributeValueTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! attributeValue: AttributeValue } @@ -562,10 +563,7 @@ enum AttributeValueType { } type AttributeValueUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") attribute: Attribute productErrors: [ProductError!]! attributeValue: AttributeValue @@ -577,20 +575,14 @@ type AuthorizationKey { } type AuthorizationKeyAdd { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") authorizationKey: AuthorizationKey shop: Shop shopErrors: [ShopError!]! } type AuthorizationKeyDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") authorizationKey: AuthorizationKey shop: Shop shopErrors: [ShopError!]! @@ -611,6 +603,7 @@ type BulkProductError { message: String code: ProductErrorCode! index: Int + warehouses: [ID!] } type BulkStockError { @@ -638,61 +631,30 @@ type Category implements Node & ObjectWithMetadata { level: Int! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) - ancestors( - before: String - after: String - first: Int - last: Int - ): CategoryCountableConnection - products( - 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 + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + ancestors(before: String, after: String, first: Int, last: Int): CategoryCountableConnection + products(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 translation(languageCode: LanguageCodeEnum!): CategoryTranslation } type CategoryBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type CategoryClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } type CategoryClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } @@ -709,19 +671,13 @@ type CategoryCountableEdge { } type CategoryCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } type CategoryDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } @@ -764,10 +720,7 @@ type CategoryTranslatableContent implements Node { } type CategoryTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! category: Category } @@ -783,28 +736,19 @@ type CategoryTranslation implements Node { } type CategoryUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } type CategoryUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } type CategoryUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! category: Category } @@ -827,14 +771,8 @@ type Checkout implements Node & ObjectWithMetadata { id: ID! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") availableShippingMethods: [ShippingMethod]! availablePaymentGateways: [PaymentGateway]! email: String! @@ -846,46 +784,31 @@ type Checkout implements Node & ObjectWithMetadata { } type CheckoutAddPromoCode { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutBillingAddressUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkoutErrors: [CheckoutError!]! checkout: Checkout } type CheckoutClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkoutErrors: [CheckoutError!]! checkout: Checkout } type CheckoutComplete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order confirmationNeeded: Boolean! checkoutErrors: [CheckoutError!]! @@ -903,10 +826,7 @@ type CheckoutCountableEdge { } type CheckoutCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") created: Boolean checkoutErrors: [CheckoutError!]! checkout: Checkout @@ -920,28 +840,19 @@ input CheckoutCreateInput { } type CheckoutCustomerAttach { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutCustomerDetach { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutEmailUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } @@ -993,10 +904,7 @@ type CheckoutLineCountableEdge { } type CheckoutLineDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } @@ -1007,74 +915,50 @@ input CheckoutLineInput { } type CheckoutLinesAdd { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutLinesUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutPaymentCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout payment: Payment paymentErrors: [PaymentError!]! } type CheckoutRemovePromoCode { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutShippingAddressUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutShippingMethodUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkout: Checkout checkoutErrors: [CheckoutError!]! } type CheckoutUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkoutErrors: [CheckoutError!]! checkout: Checkout } type CheckoutUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") checkoutErrors: [CheckoutError!]! checkout: Checkout } @@ -1096,65 +980,39 @@ type Collection implements Node & ObjectWithMetadata { slug: String! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) - products( - before: String - after: String - first: Int - last: Int - ): ProductCountableConnection + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + products(before: String, after: String, first: Int, last: Int): ProductCountableConnection backgroundImage(size: Int): Image translation(languageCode: LanguageCodeEnum!): CollectionTranslation } type CollectionAddProducts { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection productErrors: [ProductError!]! } type CollectionBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type CollectionBulkPublish { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type CollectionClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } type CollectionClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } @@ -1171,10 +1029,7 @@ type CollectionCountableEdge { } type CollectionCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } @@ -1193,10 +1048,7 @@ input CollectionCreateInput { } type CollectionDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } @@ -1225,19 +1077,13 @@ enum CollectionPublished { } type CollectionRemoveProducts { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection productErrors: [ProductError!]! } type CollectionReorderProducts { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") collection: Collection productErrors: [ProductError!]! } @@ -1265,10 +1111,7 @@ type CollectionTranslatableContent implements Node { } type CollectionTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! collection: Collection } @@ -1284,28 +1127,19 @@ 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." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } type CollectionUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } type CollectionUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! collection: Collection } @@ -1331,19 +1165,13 @@ enum ConfigurationTypeFieldEnum { } type ConfirmAccount { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } type ConfirmEmailChange { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } @@ -1609,10 +1437,7 @@ type CountryDisplay { type CreateToken { token: String - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -1626,28 +1451,19 @@ type CreditCard { } type CustomerBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! accountErrors: [AccountError!]! } type CustomerCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } type CustomerDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -1698,10 +1514,7 @@ input CustomerInput { } type CustomerUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -1723,19 +1536,13 @@ input DateTimeRangeInput { scalar Decimal type DeleteMetadata { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") metadataErrors: [MetadataError!]! item: ObjectWithMetadata } type DeletePrivateMetadata { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") metadataErrors: [MetadataError!]! item: ObjectWithMetadata } @@ -1751,14 +1558,8 @@ type DigitalContent implements Node & ObjectWithMetadata { id: ID! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") } type DigitalContentCountableConnection { @@ -1773,20 +1574,14 @@ type DigitalContentCountableEdge { } type DigitalContentCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") variant: ProductVariant content: DigitalContent productErrors: [ProductError!]! } type DigitalContentDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") variant: ProductVariant productErrors: [ProductError!]! } @@ -1799,10 +1594,7 @@ input DigitalContentInput { } type DigitalContentUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") variant: ProductVariant content: DigitalContent productErrors: [ProductError!]! @@ -1826,10 +1618,7 @@ type DigitalContentUrl implements Node { } type DigitalContentUrlCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! digitalContentUrl: DigitalContentUrl } @@ -1871,28 +1660,19 @@ type Domain { } type DraftOrderBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! orderErrors: [OrderError!]! } type DraftOrderComplete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } type DraftOrderCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") orderErrors: [OrderError!]! order: Order } @@ -1910,10 +1690,7 @@ input DraftOrderCreateInput { } type DraftOrderDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") orderErrors: [OrderError!]! order: Order } @@ -1930,49 +1707,34 @@ input DraftOrderInput { } type DraftOrderLineDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderLine: OrderLine orderErrors: [OrderError!]! } type DraftOrderLineUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! orderLine: OrderLine } type DraftOrderLinesBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! orderErrors: [OrderError!]! } type DraftOrderLinesCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderLines: [OrderLine!] orderErrors: [OrderError!]! } type DraftOrderUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") orderErrors: [OrderError!]! order: Order } @@ -1990,101 +1752,57 @@ type Fulfillment implements Node & ObjectWithMetadata { created: DateTime! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") lines: [FulfillmentLine] statusDisplay: String + warehouse: Warehouse } type FulfillmentCancel { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment order: Order orderErrors: [OrderError!]! } input FulfillmentCancelInput { - restock: Boolean + warehouseId: ID! } type FulfillmentClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment } type FulfillmentClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment } -type FulfillmentCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) - fulfillment: Fulfillment - order: Order - orderErrors: [OrderError!]! -} - -input FulfillmentCreateInput { - trackingNumber: String - notifyCustomer: Boolean - lines: [FulfillmentLineInput]! -} - type FulfillmentLine implements Node { id: ID! quantity: Int! orderLine: OrderLine } -input FulfillmentLineInput { - orderLineId: ID - quantity: Int -} - enum FulfillmentStatus { FULFILLED CANCELED } type FulfillmentUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment } type FulfillmentUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment } type FulfillmentUpdateTracking { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") fulfillment: Fulfillment order: Order orderErrors: [OrderError!]! @@ -2121,10 +1839,7 @@ type GiftCard implements Node { } type GiftCardActivate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") giftCard: GiftCard giftCardErrors: [GiftCardError!]! } @@ -2141,10 +1856,7 @@ type GiftCardCountableEdge { } type GiftCardCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") giftCardErrors: [GiftCardError!]! giftCard: GiftCard } @@ -2158,10 +1870,7 @@ input GiftCardCreateInput { } type GiftCardDeactivate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") giftCard: GiftCard giftCardErrors: [GiftCardError!]! } @@ -2182,10 +1891,7 @@ enum GiftCardErrorCode { } type GiftCardUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") giftCardErrors: [GiftCardError!]! giftCard: GiftCard } @@ -2217,10 +1923,7 @@ type GroupCountableEdge { } type HomepageCollectionUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop shopErrors: [ShopError!]! } @@ -2300,10 +2003,7 @@ type Menu implements Node { } type MenuBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! menuErrors: [MenuError!]! } @@ -2320,10 +2020,7 @@ type MenuCountableEdge { } type MenuCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menu: Menu } @@ -2334,10 +2031,7 @@ input MenuCreateInput { } type MenuDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menu: Menu } @@ -2383,10 +2077,7 @@ type MenuItem implements Node { } type MenuItemBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! menuErrors: [MenuError!]! } @@ -2403,10 +2094,7 @@ type MenuItemCountableEdge { } type MenuItemCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menuItem: MenuItem } @@ -2422,10 +2110,7 @@ input MenuItemCreateInput { } type MenuItemDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menuItem: MenuItem } @@ -2443,10 +2128,7 @@ input MenuItemInput { } type MenuItemMove { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menu: Menu menuErrors: [MenuError!]! } @@ -2470,10 +2152,7 @@ type MenuItemTranslatableContent implements Node { } type MenuItemTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! menuItem: MenuItem } @@ -2485,10 +2164,7 @@ type MenuItemTranslation implements Node { } type MenuItemUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menuItem: MenuItem } @@ -2508,10 +2184,7 @@ input MenuSortingInput { } type MenuUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") menuErrors: [MenuError!]! menu: Menu } @@ -2569,10 +2242,7 @@ type MetadataItem { type Money { currency: String! amount: Float! - localized: String! - @deprecated( - reason: "Price formatting according to the current locale should be handled by the frontend client. This field will be removed after 2020-07-31." - ) + localized: String! @deprecated(reason: "Price formatting according to the current locale should be handled by the frontend client. This field will be removed after 2020-07-31.") } type MoneyRange { @@ -2592,324 +2262,108 @@ type Mutation { createWarehouse(input: WarehouseCreateInput!): WarehouseCreate updateWarehouse(id: ID!, input: WarehouseUpdateInput!): WarehouseUpdate deleteWarehouse(id: ID!): WarehouseDelete - assignWarehouseShippingZone( - id: ID! - shippingZoneIds: [ID!]! - ): WarehouseShippingZoneAssign - unassignWarehouseShippingZone( - id: ID! - shippingZoneIds: [ID!]! - ): WarehouseShippingZoneUnassign - authorizationKeyAdd( - input: AuthorizationKeyInput! - keyType: AuthorizationKeyType! - ): AuthorizationKeyAdd + assignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneAssign + unassignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneUnassign + authorizationKeyAdd(input: AuthorizationKeyInput!, keyType: AuthorizationKeyType!): AuthorizationKeyAdd authorizationKeyDelete(keyType: AuthorizationKeyType!): AuthorizationKeyDelete - staffNotificationRecipientCreate( - input: StaffNotificationRecipientInput! - ): StaffNotificationRecipientCreate - staffNotificationRecipientUpdate( - id: ID! - input: StaffNotificationRecipientInput! - ): StaffNotificationRecipientUpdate + 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 + shopSettingsTranslate(input: ShopSettingsTranslationInput!, languageCode: LanguageCodeEnum!): ShopSettingsTranslate shopAddressUpdate(input: AddressInput): ShopAddressUpdate shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate shippingPriceDelete(id: ID!): ShippingPriceDelete shippingPriceBulkDelete(ids: [ID]!): ShippingPriceBulkDelete shippingPriceUpdate(id: ID!, input: ShippingPriceInput!): ShippingPriceUpdate - shippingPriceTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): ShippingPriceTranslate + shippingPriceTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ShippingPriceTranslate shippingZoneCreate(input: ShippingZoneCreateInput!): ShippingZoneCreate shippingZoneDelete(id: ID!): ShippingZoneDelete shippingZoneBulkDelete(ids: [ID]!): ShippingZoneBulkDelete - shippingZoneUpdate( - id: ID! - input: ShippingZoneUpdateInput! - ): ShippingZoneUpdate + shippingZoneUpdate(id: ID!, input: ShippingZoneUpdateInput!): ShippingZoneUpdate attributeCreate(input: AttributeCreateInput!): AttributeCreate attributeDelete(id: ID!): AttributeDelete attributeBulkDelete(ids: [ID]!): AttributeBulkDelete - attributeAssign( - operations: [AttributeAssignInput]! - productTypeId: ID! - ): AttributeAssign + attributeAssign(operations: [AttributeAssignInput]!, productTypeId: ID!): AttributeAssign attributeUnassign(attributeIds: [ID]!, productTypeId: ID!): AttributeUnassign attributeUpdate(id: ID!, input: AttributeUpdateInput!): AttributeUpdate - attributeTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): AttributeTranslate - attributeUpdateMetadata(id: ID!, input: MetaInput!): AttributeUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - attributeClearMetadata(id: ID!, input: MetaPath!): AttributeClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - attributeUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): AttributeUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - attributeClearPrivateMetadata( - id: ID! - input: MetaPath! - ): AttributeClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - attributeValueCreate( - attribute: ID! - input: AttributeValueCreateInput! - ): AttributeValueCreate + attributeTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeTranslate + attributeUpdateMetadata(id: ID!, input: MetaInput!): AttributeUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + attributeClearMetadata(id: ID!, input: MetaPath!): AttributeClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + attributeUpdatePrivateMetadata(id: ID!, input: MetaInput!): AttributeUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + attributeClearPrivateMetadata(id: ID!, input: MetaPath!): AttributeClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + attributeValueCreate(attribute: ID!, input: AttributeValueCreateInput!): AttributeValueCreate attributeValueDelete(id: ID!): AttributeValueDelete attributeValueBulkDelete(ids: [ID]!): AttributeValueBulkDelete - attributeValueUpdate( - id: ID! - input: AttributeValueCreateInput! - ): AttributeValueUpdate - attributeValueTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): AttributeValueTranslate - attributeReorderValues( - attributeId: ID! - moves: [ReorderInput]! - ): AttributeReorderValues + attributeValueUpdate(id: ID!, input: AttributeValueCreateInput!): AttributeValueUpdate + attributeValueTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): AttributeValueTranslate + attributeReorderValues(attributeId: ID!, moves: [ReorderInput]!): AttributeReorderValues categoryCreate(input: CategoryInput!, parent: ID): CategoryCreate categoryDelete(id: ID!): CategoryDelete categoryBulkDelete(ids: [ID]!): CategoryBulkDelete categoryUpdate(id: ID!, input: CategoryInput!): CategoryUpdate - categoryTranslate( - id: ID! - input: TranslationInput! - languageCode: LanguageCodeEnum! - ): CategoryTranslate - categoryUpdateMetadata(id: ID!, input: MetaInput!): CategoryUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - categoryClearMetadata(id: ID!, input: MetaPath!): CategoryClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - categoryUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): CategoryUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - categoryClearPrivateMetadata( - id: ID! - input: MetaPath! - ): CategoryClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - collectionAddProducts( - collectionId: ID! - products: [ID]! - ): CollectionAddProducts + categoryTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): CategoryTranslate + categoryUpdateMetadata(id: ID!, input: MetaInput!): CategoryUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + categoryClearMetadata(id: ID!, input: MetaPath!): CategoryClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + categoryUpdatePrivateMetadata(id: ID!, input: MetaInput!): CategoryUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + categoryClearPrivateMetadata(id: ID!, input: MetaPath!): CategoryClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + collectionAddProducts(collectionId: ID!, products: [ID]!): CollectionAddProducts collectionCreate(input: CollectionCreateInput!): CollectionCreate collectionDelete(id: ID!): CollectionDelete - collectionReorderProducts( - collectionId: ID! - moves: [MoveProductInput]! - ): CollectionReorderProducts + collectionReorderProducts(collectionId: ID!, moves: [MoveProductInput]!): CollectionReorderProducts collectionBulkDelete(ids: [ID]!): CollectionBulkDelete - collectionBulkPublish( - ids: [ID]! - isPublished: Boolean! - ): CollectionBulkPublish - collectionRemoveProducts( - collectionId: ID! - products: [ID]! - ): CollectionRemoveProducts + 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 - collectionUpdateMetadata(id: ID!, input: MetaInput!): CollectionUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - collectionClearMetadata(id: ID!, input: MetaPath!): CollectionClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - collectionUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): CollectionUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - collectionClearPrivateMetadata( - id: ID! - input: MetaPath! - ): CollectionClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) + collectionTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): CollectionTranslate + collectionUpdateMetadata(id: ID!, input: MetaInput!): CollectionUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + collectionClearMetadata(id: ID!, input: MetaPath!): CollectionClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + collectionUpdatePrivateMetadata(id: ID!, input: MetaInput!): CollectionUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + collectionClearPrivateMetadata(id: ID!, input: MetaPath!): CollectionClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") 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 - productUpdateMetadata(id: ID!, input: MetaInput!): ProductUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productClearMetadata(id: ID!, input: MetaPath!): ProductClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): ProductUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productClearPrivateMetadata( - id: ID! - input: MetaPath! - ): ProductClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) + productTranslate(id: ID!, input: TranslationInput!, languageCode: LanguageCodeEnum!): ProductTranslate + productUpdateMetadata(id: ID!, input: MetaInput!): ProductUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productClearMetadata(id: ID!, input: MetaPath!): ProductClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + productUpdatePrivateMetadata(id: ID!, input: MetaInput!): ProductUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productClearPrivateMetadata(id: ID!, input: MetaPath!): ProductClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") productImageCreate(input: ProductImageCreateInput!): ProductImageCreate productImageDelete(id: ID!): ProductImageDelete productImageBulkDelete(ids: [ID]!): ProductImageBulkDelete productImageReorder(imagesIds: [ID]!, productId: ID!): ProductImageReorder - productImageUpdate( - id: ID! - input: ProductImageUpdateInput! - ): ProductImageUpdate + productImageUpdate(id: ID!, input: ProductImageUpdateInput!): ProductImageUpdate productTypeCreate(input: ProductTypeInput!): ProductTypeCreate productTypeDelete(id: ID!): ProductTypeDelete productTypeBulkDelete(ids: [ID]!): ProductTypeBulkDelete productTypeUpdate(id: ID!, input: ProductTypeInput!): ProductTypeUpdate - productTypeReorderAttributes( - moves: [ReorderInput]! - productTypeId: ID! - type: AttributeTypeEnum! - ): ProductTypeReorderAttributes - productTypeUpdateMetadata(id: ID!, input: MetaInput!): ProductTypeUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productTypeClearMetadata(id: ID!, input: MetaPath!): ProductTypeClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productTypeUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): ProductTypeUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productTypeClearPrivateMetadata( - id: ID! - input: MetaPath! - ): ProductTypeClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - digitalContentCreate( - input: DigitalContentUploadInput! - variantId: ID! - ): DigitalContentCreate + productTypeReorderAttributes(moves: [ReorderInput]!, productTypeId: ID!, type: AttributeTypeEnum!): ProductTypeReorderAttributes + productTypeUpdateMetadata(id: ID!, input: MetaInput!): ProductTypeUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productTypeClearMetadata(id: ID!, input: MetaPath!): ProductTypeClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + productTypeUpdatePrivateMetadata(id: ID!, input: MetaInput!): ProductTypeUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productTypeClearPrivateMetadata(id: ID!, input: MetaPath!): ProductTypeClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + digitalContentCreate(input: DigitalContentUploadInput!, variantId: ID!): DigitalContentCreate digitalContentDelete(variantId: ID!): DigitalContentDelete - digitalContentUpdate( - input: DigitalContentInput! - variantId: ID! - ): DigitalContentUpdate - digitalContentUrlCreate( - input: DigitalContentUrlCreateInput! - ): DigitalContentUrlCreate + digitalContentUpdate(input: DigitalContentInput!, variantId: ID!): DigitalContentUpdate + digitalContentUrlCreate(input: DigitalContentUrlCreateInput!): DigitalContentUrlCreate productVariantCreate(input: ProductVariantCreateInput!): ProductVariantCreate productVariantDelete(id: ID!): ProductVariantDelete - productVariantBulkCreate( - product: ID! - variants: [ProductVariantBulkCreateInput]! - ): ProductVariantBulkCreate + productVariantBulkCreate(product: ID!, variants: [ProductVariantBulkCreateInput]!): ProductVariantBulkCreate productVariantBulkDelete(ids: [ID]!): ProductVariantBulkDelete - productVariantStocksCreate( - stocks: [StockInput!]! - variantId: ID! - ): ProductVariantStocksCreate - productVariantStocksDelete( - variantId: ID! - warehouseIds: [ID!] - ): ProductVariantStocksDelete - productVariantStocksUpdate( - stocks: [StockInput!]! - variantId: ID! - ): ProductVariantStocksUpdate - productVariantUpdate( - id: ID! - input: ProductVariantInput! - ): ProductVariantUpdate - productVariantTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): ProductVariantTranslate - productVariantUpdateMetadata( - id: ID! - input: MetaInput! - ): ProductVariantUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productVariantClearMetadata( - id: ID! - input: MetaPath! - ): ProductVariantClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productVariantUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): ProductVariantUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - productVariantClearPrivateMetadata( - id: ID! - input: MetaPath! - ): ProductVariantClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) + productVariantStocksCreate(stocks: [StockInput!]!, variantId: ID!): ProductVariantStocksCreate + productVariantStocksDelete(variantId: ID!, warehouseIds: [ID!]): ProductVariantStocksDelete + productVariantStocksUpdate(stocks: [StockInput!]!, variantId: ID!): ProductVariantStocksUpdate + productVariantUpdate(id: ID!, input: ProductVariantInput!): ProductVariantUpdate + productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate + productVariantUpdateMetadata(id: ID!, input: MetaInput!): ProductVariantUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productVariantClearMetadata(id: ID!, input: MetaPath!): ProductVariantClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + productVariantUpdatePrivateMetadata(id: ID!, input: MetaInput!): ProductVariantUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + productVariantClearPrivateMetadata(id: ID!, input: MetaPath!): ProductVariantClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") variantImageAssign(imageId: ID!, variantId: ID!): VariantImageAssign variantImageUnassign(imageId: ID!, variantId: ID!): VariantImageUnassign paymentCapture(amount: Decimal, paymentId: ID!): PaymentCapture @@ -2921,92 +2375,40 @@ type Mutation { pageBulkDelete(ids: [ID]!): PageBulkDelete pageBulkPublish(ids: [ID]!, isPublished: Boolean!): PageBulkPublish pageUpdate(id: ID!, input: PageInput!): PageUpdate - pageTranslate( - id: ID! - input: PageTranslationInput! - languageCode: LanguageCodeEnum! - ): PageTranslate + pageTranslate(id: ID!, input: PageTranslationInput!, languageCode: LanguageCodeEnum!): PageTranslate draftOrderComplete(id: ID!): DraftOrderComplete draftOrderCreate(input: DraftOrderCreateInput!): DraftOrderCreate draftOrderDelete(id: ID!): DraftOrderDelete draftOrderBulkDelete(ids: [ID]!): DraftOrderBulkDelete draftOrderLinesBulkDelete(ids: [ID]!): DraftOrderLinesBulkDelete - draftOrderLinesCreate( - id: ID! - input: [OrderLineCreateInput]! - ): DraftOrderLinesCreate + draftOrderLinesCreate(id: ID!, input: [OrderLineCreateInput]!): DraftOrderLinesCreate draftOrderLineDelete(id: ID!): DraftOrderLineDelete draftOrderLineUpdate(id: ID!, input: OrderLineInput!): DraftOrderLineUpdate draftOrderUpdate(id: ID!, input: DraftOrderInput!): DraftOrderUpdate orderAddNote(order: ID!, input: OrderAddNoteInput!): OrderAddNote - orderCancel(id: ID!, restock: Boolean!): OrderCancel + orderCancel(id: ID!): OrderCancel orderCapture(amount: Decimal!, id: ID!): OrderCapture - orderClearPrivateMeta(id: ID!, input: MetaPath!): OrderClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderClearMeta(input: MetaPath!, token: UUID!): OrderClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderFulfillmentCancel( - id: ID! - input: FulfillmentCancelInput! - ): FulfillmentCancel - orderFulfillmentCreate( - input: FulfillmentCreateInput! - order: ID - ): FulfillmentCreate - orderFulfillmentUpdateTracking( - id: ID! - input: FulfillmentUpdateTrackingInput! - ): FulfillmentUpdateTracking - orderFulfillmentClearMeta(id: ID!, input: MetaPath!): FulfillmentClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderFulfillmentClearPrivateMeta( - id: ID! - input: MetaPath! - ): FulfillmentClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderFulfillmentUpdateMeta(id: ID!, input: MetaInput!): FulfillmentUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderFulfillmentUpdatePrivateMeta( - id: ID! - input: MetaInput! - ): FulfillmentUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) + orderClearPrivateMeta(id: ID!, input: MetaPath!): OrderClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderClearMeta(input: MetaPath!, token: UUID!): OrderClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderFulfill(input: OrderFulfillInput!, order: ID): OrderFulfill + orderFulfillmentCancel(id: ID!, input: FulfillmentCancelInput!): FulfillmentCancel + orderFulfillmentUpdateTracking(id: ID!, input: FulfillmentUpdateTrackingInput!): FulfillmentUpdateTracking + orderFulfillmentClearMeta(id: ID!, input: MetaPath!): FulfillmentClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderFulfillmentClearPrivateMeta(id: ID!, input: MetaPath!): FulfillmentClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderFulfillmentUpdateMeta(id: ID!, input: MetaInput!): FulfillmentUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderFulfillmentUpdatePrivateMeta(id: ID!, input: MetaInput!): FulfillmentUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") orderMarkAsPaid(id: ID!): OrderMarkAsPaid orderRefund(amount: Decimal!, id: ID!): OrderRefund orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate - orderUpdateMeta(input: MetaInput!, token: UUID!): OrderUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderUpdatePrivateMeta(id: ID!, input: MetaInput!): OrderUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31." - ) - orderUpdateShipping( - order: ID! - input: OrderUpdateShippingInput - ): OrderUpdateShipping + orderUpdateMeta(input: MetaInput!, token: UUID!): OrderUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderUpdatePrivateMeta(id: ID!, input: MetaInput!): OrderUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") + orderUpdateShipping(order: ID!, input: OrderUpdateShippingInput): OrderUpdateShipping orderVoid(id: ID!): OrderVoid - orderBulkCancel(ids: [ID]!, restock: Boolean!): OrderBulkCancel + orderBulkCancel(ids: [ID]!): OrderBulkCancel deleteMetadata(id: ID!, keys: [String!]!): DeleteMetadata deletePrivateMetadata(id: ID!, keys: [String!]!): DeletePrivateMetadata updateMetadata(id: ID!, input: [MetadataInput!]!): UpdateMetadata - updatePrivateMetadata( - id: ID! - input: [MetadataInput!]! - ): UpdatePrivateMetadata + updatePrivateMetadata(id: ID!, input: [MetadataInput!]!): UpdatePrivateMetadata assignNavigation(menu: ID, navigationType: NavigationType!): AssignNavigation menuCreate(input: MenuCreateInput!): MenuCreate menuDelete(id: ID!): MenuDelete @@ -3016,11 +2418,7 @@ type Mutation { menuItemDelete(id: ID!): MenuItemDelete menuItemBulkDelete(ids: [ID]!): MenuItemBulkDelete menuItemUpdate(id: ID!, input: MenuItemInput!): MenuItemUpdate - menuItemTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): MenuItemTranslate + menuItemTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): MenuItemTranslate menuItemMove(menu: ID!, moves: [MenuItemMoveInput]!): MenuItemMove giftCardActivate(id: ID!): GiftCardActivate giftCardCreate(input: GiftCardCreateInput!): GiftCardCreate @@ -3033,134 +2431,59 @@ type Mutation { saleUpdate(id: ID!, input: SaleInput!): SaleUpdate saleCataloguesAdd(id: ID!, input: CatalogueInput!): SaleAddCatalogues saleCataloguesRemove(id: ID!, input: CatalogueInput!): SaleRemoveCatalogues - saleTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): SaleTranslate + saleTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): SaleTranslate voucherCreate(input: VoucherInput!): VoucherCreate voucherDelete(id: ID!): VoucherDelete voucherBulkDelete(ids: [ID]!): VoucherBulkDelete voucherUpdate(id: ID!, input: VoucherInput!): VoucherUpdate voucherCataloguesAdd(id: ID!, input: CatalogueInput!): VoucherAddCatalogues - voucherCataloguesRemove( - id: ID! - input: CatalogueInput! - ): VoucherRemoveCatalogues - voucherTranslate( - id: ID! - input: NameTranslationInput! - languageCode: LanguageCodeEnum! - ): VoucherTranslate + voucherCataloguesRemove(id: ID!, input: CatalogueInput!): VoucherRemoveCatalogues + voucherTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): VoucherTranslate tokenCreate(email: String!, password: String!): CreateToken - tokenRefresh(token: String!): Refresh + tokenRefresh(token: String!): RefreshToken tokenVerify(token: String!): VerifyToken - checkoutAddPromoCode( - checkoutId: ID! - promoCode: String! - ): CheckoutAddPromoCode - checkoutBillingAddressUpdate( - billingAddress: AddressInput! - checkoutId: ID! - ): CheckoutBillingAddressUpdate - checkoutComplete( - checkoutId: ID! - redirectUrl: String - storeSource: Boolean = false - ): CheckoutComplete + checkoutAddPromoCode(checkoutId: ID!, promoCode: String!): CheckoutAddPromoCode + checkoutBillingAddressUpdate(billingAddress: AddressInput!, checkoutId: ID!): CheckoutBillingAddressUpdate + checkoutComplete(checkoutId: ID!, redirectUrl: String, storeSource: Boolean = false): CheckoutComplete checkoutCreate(input: CheckoutCreateInput!): CheckoutCreate - checkoutCustomerAttach( - checkoutId: ID! - customerId: ID - ): CheckoutCustomerAttach + checkoutCustomerAttach(checkoutId: ID!, customerId: ID): CheckoutCustomerAttach checkoutCustomerDetach(checkoutId: ID!): CheckoutCustomerDetach checkoutEmailUpdate(checkoutId: ID, email: String!): CheckoutEmailUpdate checkoutLineDelete(checkoutId: ID!, lineId: ID): CheckoutLineDelete - checkoutLinesAdd( - checkoutId: ID! - lines: [CheckoutLineInput]! - ): CheckoutLinesAdd - checkoutLinesUpdate( - checkoutId: ID! - lines: [CheckoutLineInput]! - ): CheckoutLinesUpdate - checkoutRemovePromoCode( - checkoutId: ID! - promoCode: String! - ): CheckoutRemovePromoCode - checkoutPaymentCreate( - checkoutId: ID! - input: PaymentInput! - ): CheckoutPaymentCreate - checkoutShippingAddressUpdate( - checkoutId: ID! - shippingAddress: AddressInput! - ): CheckoutShippingAddressUpdate - checkoutShippingMethodUpdate( - checkoutId: ID - shippingMethodId: ID! - ): CheckoutShippingMethodUpdate - checkoutUpdateMetadata(id: ID!, input: MetaInput!): CheckoutUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31." - ) - checkoutClearMetadata(id: ID!, input: MetaPath!): CheckoutClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation. This field will be removed after 2020-07-31." - ) - checkoutUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): CheckoutUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - checkoutClearPrivateMetadata( - id: ID! - input: MetaPath! - ): CheckoutClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - requestPasswordReset( - email: String! - redirectUrl: String! - ): RequestPasswordReset + checkoutLinesAdd(checkoutId: ID!, lines: [CheckoutLineInput]!): CheckoutLinesAdd + checkoutLinesUpdate(checkoutId: ID!, lines: [CheckoutLineInput]!): CheckoutLinesUpdate + checkoutRemovePromoCode(checkoutId: ID!, promoCode: String!): CheckoutRemovePromoCode + checkoutPaymentCreate(checkoutId: ID!, input: PaymentInput!): CheckoutPaymentCreate + checkoutShippingAddressUpdate(checkoutId: ID!, shippingAddress: AddressInput!): CheckoutShippingAddressUpdate + checkoutShippingMethodUpdate(checkoutId: ID, shippingMethodId: ID!): CheckoutShippingMethodUpdate + checkoutUpdateMetadata(id: ID!, input: MetaInput!): CheckoutUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31.") + checkoutClearMetadata(id: ID!, input: MetaPath!): CheckoutClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation. This field will be removed after 2020-07-31.") + checkoutUpdatePrivateMetadata(id: ID!, input: MetaInput!): CheckoutUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation. This field will be removed after 2020-07-31.") + checkoutClearPrivateMetadata(id: ID!, input: MetaPath!): CheckoutClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation. This field will be removed after 2020-07-31.") + appCreate(input: AppInput!): AppCreate + appUpdate(id: ID!, input: AppInput!): AppUpdate + appDelete(id: ID!): AppDelete + appTokenCreate(input: AppTokenInput!): AppTokenCreate + appTokenDelete(id: ID!): AppTokenDelete + requestPasswordReset(email: String!, redirectUrl: String!): RequestPasswordReset confirmAccount(email: String!, token: String!): ConfirmAccount setPassword(token: String!, email: String!, password: String!): SetPassword passwordChange(newPassword: String!, oldPassword: String!): PasswordChange - requestEmailChange( - newEmail: String! - password: String! - redirectUrl: String! - ): RequestEmailChange + requestEmailChange(newEmail: String!, password: String!, redirectUrl: String!): RequestEmailChange confirmEmailChange(token: String!): ConfirmEmailChange - accountAddressCreate( - input: AddressInput! - type: AddressTypeEnum - ): AccountAddressCreate + accountAddressCreate(input: AddressInput!, type: AddressTypeEnum): AccountAddressCreate accountAddressUpdate(id: ID!, input: AddressInput!): AccountAddressUpdate accountAddressDelete(id: ID!): AccountAddressDelete - accountSetDefaultAddress( - id: ID! - type: AddressTypeEnum! - ): AccountSetDefaultAddress + accountSetDefaultAddress(id: ID!, type: AddressTypeEnum!): AccountSetDefaultAddress accountRegister(input: AccountRegisterInput!): AccountRegister accountUpdate(input: AccountInput!): AccountUpdate accountRequestDeletion(redirectUrl: String!): AccountRequestDeletion accountDelete(token: String!): AccountDelete - accountUpdateMeta(input: MetaInput!): AccountUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31." - ) + accountUpdateMeta(input: MetaInput!): AccountUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31.") addressCreate(input: AddressInput!, userId: ID!): AddressCreate addressUpdate(id: ID!, input: AddressInput!): AddressUpdate addressDelete(id: ID!): AddressDelete - addressSetDefault( - addressId: ID! - type: AddressTypeEnum! - userId: ID! - ): AddressSetDefault + addressSetDefault(addressId: ID!, type: AddressTypeEnum!, userId: ID!): AddressSetDefault customerCreate(input: UserCreateInput!): CustomerCreate customerUpdate(id: ID!, input: CustomerInput!): CustomerUpdate customerDelete(id: ID!): CustomerDelete @@ -3172,53 +2495,19 @@ type Mutation { userAvatarUpdate(image: Upload!): UserAvatarUpdate userAvatarDelete: UserAvatarDelete userBulkSetActive(ids: [ID]!, isActive: Boolean!): UserBulkSetActive - userUpdateMetadata(id: ID!, input: MetaInput!): UserUpdateMeta - @deprecated( - reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31." - ) - userClearMetadata(id: ID!, input: MetaPath!): UserClearMeta - @deprecated( - reason: "Use the `deleteMetadata` mutation. This field will be removed after 2020-07-31." - ) - userUpdatePrivateMetadata(id: ID!, input: MetaInput!): UserUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - userClearPrivateMetadata(id: ID!, input: MetaPath!): UserClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - serviceAccountCreate(input: ServiceAccountInput!): ServiceAccountCreate - serviceAccountUpdate( - id: ID! - input: ServiceAccountInput! - ): ServiceAccountUpdate - serviceAccountDelete(id: ID!): ServiceAccountDelete - serviceAccountUpdatePrivateMetadata( - id: ID! - input: MetaInput! - ): ServiceAccountUpdatePrivateMeta - @deprecated( - reason: "Use the `updatePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - serviceAccountClearPrivateMetadata( - id: ID! - input: MetaPath! - ): ServiceAccountClearPrivateMeta - @deprecated( - reason: "Use the `deletePrivateMetadata` mutation. This field will be removed after 2020-07-31." - ) - serviceAccountTokenCreate( - input: ServiceAccountTokenInput! - ): ServiceAccountTokenCreate - serviceAccountTokenDelete(id: ID!): ServiceAccountTokenDelete - permissionGroupCreate( - input: PermissionGroupCreateInput! - ): PermissionGroupCreate - permissionGroupUpdate( - id: ID! - input: PermissionGroupUpdateInput! - ): PermissionGroupUpdate + userUpdateMetadata(id: ID!, input: MetaInput!): UserUpdateMeta @deprecated(reason: "Use the `updateMetadata` mutation. This field will be removed after 2020-07-31.") + userClearMetadata(id: ID!, input: MetaPath!): UserClearMeta @deprecated(reason: "Use the `deleteMetadata` mutation. This field will be removed after 2020-07-31.") + userUpdatePrivateMetadata(id: ID!, input: MetaInput!): UserUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation. This field will be removed after 2020-07-31.") + userClearPrivateMetadata(id: ID!, input: MetaPath!): UserClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation. This field will be removed after 2020-07-31.") + serviceAccountCreate(input: ServiceAccountInput!): ServiceAccountCreate @deprecated(reason: "Use the `appCreate` mutation instead. This field will be removed after 2020-07-31.") + serviceAccountUpdate(id: ID!, input: ServiceAccountInput!): ServiceAccountUpdate @deprecated(reason: "Use the `appUpdate` mutation instead. This field will be removed after 2020-07-31.") + serviceAccountDelete(id: ID!): ServiceAccountDelete @deprecated(reason: "Use the `appDelete` mutation instead. This field will be removed after 2020-07-31.") + serviceAccountUpdatePrivateMetadata(id: ID!, input: MetaInput!): ServiceAccountUpdatePrivateMeta @deprecated(reason: "Use the `updatePrivateMetadata` mutation with App instead.This field will be removed after 2020-07-31.") + serviceAccountClearPrivateMetadata(id: ID!, input: MetaPath!): ServiceAccountClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation with App instead.This field will be removed after 2020-07-31.") + serviceAccountTokenCreate(input: ServiceAccountTokenInput!): ServiceAccountTokenCreate @deprecated(reason: "Use the `appTokenCreate` mutation instead. This field will be removed after 2020-07-31.") + serviceAccountTokenDelete(id: ID!): ServiceAccountTokenDelete @deprecated(reason: "Use the `appTokenDelete` mutation instead. This field will be removed after 2020-07-31.") + permissionGroupCreate(input: PermissionGroupCreateInput!): PermissionGroupCreate + permissionGroupUpdate(id: ID!, input: PermissionGroupUpdateInput!): PermissionGroupUpdate permissionGroupDelete(id: ID!): PermissionGroupDelete } @@ -3243,14 +2532,8 @@ interface Node { interface ObjectWithMetadata { privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") } type Order implements Node & ObjectWithMetadata { @@ -3276,14 +2559,8 @@ type Order implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") fulfillments: [Fulfillment]! lines: [OrderLine]! actions: [OrderAction]! @@ -3313,10 +2590,7 @@ enum OrderAction { } type OrderAddNote { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order event: OrderEvent orderErrors: [OrderError!]! @@ -3327,45 +2601,30 @@ input OrderAddNoteInput { } type OrderBulkCancel { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! orderErrors: [OrderError!]! } type OrderCancel { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } type OrderCapture { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } type OrderClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order } type OrderClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order } @@ -3395,6 +2654,8 @@ type OrderError { field: String message: String code: OrderErrorCode! + warehouse: ID + orderLine: ID } enum OrderErrorCode { @@ -3419,6 +2680,7 @@ enum OrderErrorCode { VOID_INACTIVE_PAYMENT ZERO_QUANTITY INSUFFICIENT_STOCK + DUPLICATED_INPUT_ITEM } type OrderEvent implements Node { @@ -3438,6 +2700,7 @@ type OrderEvent implements Node { oversoldItems: [String] lines: [OrderEventOrderLineObject] fulfilledItems: [FulfillmentLine] + warehouse: Warehouse } type OrderEventCountableConnection { @@ -3498,6 +2761,28 @@ input OrderFilterInput { search: String } +type OrderFulfill { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + fulfillments: [Fulfillment] + order: Order + orderErrors: [OrderError!]! +} + +input OrderFulfillInput { + lines: [OrderFulfillLineInput!]! + notifyCustomer: Boolean +} + +input OrderFulfillLineInput { + orderLineId: ID + stocks: [OrderFulfillStockInput!]! +} + +input OrderFulfillStockInput { + quantity: Int + warehouse: ID +} + type OrderLine implements Node { id: ID! productName: String! @@ -3525,19 +2810,13 @@ input OrderLineInput { } type OrderMarkAsPaid { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } type OrderRefund { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } @@ -3574,10 +2853,7 @@ enum OrderStatusFilter { } type OrderUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") orderErrors: [OrderError!]! order: Order } @@ -3589,26 +2865,17 @@ input OrderUpdateInput { } type OrderUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order } type OrderUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order } type OrderUpdateShipping { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } @@ -3618,10 +2885,7 @@ input OrderUpdateShippingInput { } type OrderVoid { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") order: Order orderErrors: [OrderError!]! } @@ -3641,19 +2905,13 @@ type Page implements Node { } type PageBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! pageErrors: [PageError!]! } type PageBulkPublish { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! pageErrors: [PageError!]! } @@ -3670,19 +2928,13 @@ type PageCountableEdge { } type PageCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") pageErrors: [PageError!]! page: Page } type PageDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") pageErrors: [PageError!]! page: Page } @@ -3747,10 +2999,7 @@ type PageTranslatableContent implements Node { } type PageTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! page: PageTranslatableContent } @@ -3774,19 +3023,13 @@ input PageTranslationInput { } type PageUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") pageErrors: [PageError!]! page: Page } type PasswordChange { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } @@ -3815,10 +3058,7 @@ type Payment implements Node { } type PaymentCapture { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") payment: Payment paymentErrors: [PaymentError!]! } @@ -3872,19 +3112,13 @@ input PaymentInput { } type PaymentRefund { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") payment: Payment paymentErrors: [PaymentError!]! } type PaymentSecureConfirm { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") payment: Payment paymentErrors: [PaymentError!]! } @@ -3895,10 +3129,7 @@ type PaymentSource { } type PaymentVoid { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") payment: Payment paymentErrors: [PaymentError!]! } @@ -3928,10 +3159,7 @@ enum PermissionEnum { } type PermissionGroupCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") group: Group permissionGroupErrors: [PermissionGroupError!]! } @@ -3943,10 +3171,7 @@ input PermissionGroupCreateInput { } type PermissionGroupDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") permissionGroupErrors: [PermissionGroupError!]! group: Group } @@ -3984,10 +3209,7 @@ input PermissionGroupSortingInput { } type PermissionGroupUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") group: Group permissionGroupErrors: [PermissionGroupError!]! } @@ -4050,10 +3272,7 @@ input PluginSortingInput { } type PluginUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") plugin: Plugin pluginsErrors: [PluginError!]! } @@ -4085,16 +3304,9 @@ type Product implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) - url: String! - @deprecated(reason: "This field will be removed after 2020-07-31.") + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + url: String! @deprecated(reason: "This field will be removed after 2020-07-31.") thumbnail(size: Int): Image pricing: ProductPricingInfo isAvailable: Boolean @@ -4119,37 +3331,25 @@ type ProductAttributeError { } type ProductBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type ProductBulkPublish { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type ProductClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } type ProductClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } @@ -4166,10 +3366,7 @@ type ProductCountableEdge { } type ProductCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } @@ -4190,17 +3387,13 @@ input ProductCreateInput { seo: SeoInput weight: WeightScalar sku: String - quantity: Int trackInventory: Boolean productType: ID! stocks: [StockInput!] } type ProductDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } @@ -4249,19 +3442,13 @@ type ProductImage implements Node { } type ProductImageBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type ProductImageCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") product: Product image: ProductImage productErrors: [ProductError!]! @@ -4274,30 +3461,21 @@ input ProductImageCreateInput { } type ProductImageDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") product: Product image: ProductImage productErrors: [ProductError!]! } type ProductImageReorder { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") product: Product images: [ProductImage] productErrors: [ProductError!]! } type ProductImageUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") product: Product image: ProductImage productErrors: [ProductError!]! @@ -4323,7 +3501,6 @@ input ProductInput { seo: SeoInput weight: WeightScalar sku: String - quantity: Int trackInventory: Boolean } @@ -4368,10 +3545,7 @@ type ProductTranslatableContent implements Node { } type ProductTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! product: Product } @@ -4396,56 +3570,30 @@ type ProductType implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) - products( - before: String - after: String - first: Int - last: Int - ): ProductCountableConnection + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + products(before: String, after: String, first: Int, last: Int): ProductCountableConnection taxRate: TaxRateType taxType: TaxType variantAttributes: [Attribute] productAttributes: [Attribute] - availableAttributes( - filter: AttributeFilterInput - before: String - after: String - first: Int - last: Int - ): AttributeCountableConnection + availableAttributes(filter: AttributeFilterInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection } type ProductTypeBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type ProductTypeClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } type ProductTypeClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } @@ -4467,19 +3615,13 @@ type ProductTypeCountableEdge { } type ProductTypeCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } type ProductTypeDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } @@ -4509,10 +3651,7 @@ input ProductTypeInput { } type ProductTypeReorderAttributes { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productType: ProductType productErrors: [ProductError!]! } @@ -4529,55 +3668,37 @@ input ProductTypeSortingInput { } type ProductTypeUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } type ProductTypeUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } type ProductTypeUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productType: ProductType } type ProductUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } type ProductUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } type ProductUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! product: Product } @@ -4591,32 +3712,14 @@ type ProductVariant implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) - 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 stock field instead. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") + 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 stock field instead. This field will be removed after 2020-07-31.") priceOverride: Money pricing: VariantPricingInfo - isAvailable: Boolean - @deprecated( - reason: "Use the stock field instead. This field will be removed after 2020-07-31." - ) + isAvailable: Boolean @deprecated(reason: "Use the stock field instead. This field will be removed after 2020-07-31.") attributes: [SelectedAttribute!]! costPrice: Money margin: Int @@ -4629,10 +3732,7 @@ type ProductVariant implements Node & ObjectWithMetadata { } type ProductVariantBulkCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productVariants: [ProductVariant!]! bulkProductErrors: [BulkProductError!]! @@ -4643,34 +3743,25 @@ input ProductVariantBulkCreateInput { costPrice: Decimal priceOverride: Decimal sku: String! - quantity: Int trackInventory: Boolean weight: WeightScalar + stocks: [StockInput!] } type ProductVariantBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! productErrors: [ProductError!]! } type ProductVariantClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } type ProductVariantClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } @@ -4687,10 +3778,7 @@ type ProductVariantCountableEdge { } type ProductVariantCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } @@ -4700,7 +3788,6 @@ input ProductVariantCreateInput { costPrice: Decimal priceOverride: Decimal sku: String - quantity: Int trackInventory: Boolean weight: WeightScalar product: ID! @@ -4708,10 +3795,7 @@ input ProductVariantCreateInput { } type ProductVariantDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } @@ -4721,34 +3805,24 @@ input ProductVariantInput { costPrice: Decimal priceOverride: Decimal sku: String - quantity: Int trackInventory: Boolean weight: WeightScalar } type ProductVariantStocksCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant bulkStockErrors: [BulkStockError!]! } type ProductVariantStocksDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant stockErrors: [StockError!]! } type ProductVariantStocksUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant bulkStockErrors: [BulkStockError!]! } @@ -4761,10 +3835,7 @@ type ProductVariantTranslatableContent implements Node { } type ProductVariantTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! productVariant: ProductVariant } @@ -4776,301 +3847,90 @@ type ProductVariantTranslation implements Node { } type ProductVariantUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } type ProductVariantUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } type ProductVariantUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productErrors: [ProductError!]! productVariant: ProductVariant } type Query { webhook(id: ID!): Webhook - webhooks( - sortBy: WebhookSortingInput - filter: WebhookFilterInput - before: String - after: String - first: Int - last: Int - ): WebhookCountableConnection + webhooks(sortBy: WebhookSortingInput, filter: WebhookFilterInput, before: String, after: String, first: Int, last: Int): WebhookCountableConnection @deprecated(reason: "Use webhooks field on app(s) query instead. This field will be removed after 2020-07-31.") webhookEvents: [WebhookEvent] webhookSamplePayload(eventType: WebhookSampleEventTypeEnum!): JSONString warehouse(id: ID!): Warehouse - warehouses( - filter: WarehouseFilterInput - sortBy: WarehouseSortingInput - before: String - after: String - first: Int - last: Int - ): WarehouseCountableConnection - translations( - kind: TranslatableKinds! - before: String - after: String - first: Int - last: Int - ): TranslatableItemConnection + warehouses(filter: WarehouseFilterInput, sortBy: WarehouseSortingInput, before: String, after: String, first: Int, last: Int): WarehouseCountableConnection + translations(kind: TranslatableKinds!, before: String, after: String, first: Int, last: Int): TranslatableItemConnection translation(id: ID!, kind: TranslatableKinds!): TranslatableItem stock(id: ID!): Stock - stocks( - filter: StockFilterInput - before: String - after: String - first: Int - last: Int - ): StockCountableConnection + 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 + shippingZones(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 + 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 + categories(filter: CategoryFilterInput, sortBy: CategorySortingInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection category(id: ID!): Category collection(id: ID!): Collection - collections( - filter: CollectionFilterInput - sortBy: CollectionSortingInput - before: String - after: String - first: Int - last: Int - ): CollectionCountableConnection + collections(filter: CollectionFilterInput, sortBy: CollectionSortingInput, before: String, after: String, first: Int, last: Int): CollectionCountableConnection product(id: ID!): Product - products( - filter: ProductFilterInput - sortBy: ProductOrder - stockAvailability: StockAvailability - before: String - after: String - first: Int - last: Int - ): ProductCountableConnection + products(filter: ProductFilterInput, sortBy: ProductOrder, stockAvailability: StockAvailability, 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 + productTypes(filter: ProductTypeFilterInput, sortBy: ProductTypeSortingInput, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection productVariant(id: ID!): ProductVariant - productVariants( - ids: [ID] - before: String - after: String - first: Int - last: Int - ): ProductVariantCountableConnection - reportProductSales( - period: ReportingPeriod! - before: String - after: String - first: Int - last: Int - ): ProductVariantCountableConnection + productVariants(ids: [ID], before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection + reportProductSales(period: ReportingPeriod!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection payment(id: ID!): Payment - payments( - before: String - after: String - first: Int - last: Int - ): PaymentCountableConnection + 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 + 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 - draftOrders( - sortBy: OrderSortingInput - filter: OrderDraftFilterInput - created: ReportingPeriod - before: String - after: String - first: Int - last: Int - ): OrderCountableConnection + orders(sortBy: OrderSortingInput, filter: OrderFilterInput, created: ReportingPeriod, status: OrderStatusFilter, 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 orderByToken(token: UUID!): Order menu(id: ID, name: String): Menu - menus( - sortBy: MenuSortingInput - filter: MenuFilterInput - before: String - after: String - first: Int - last: Int - ): MenuCountableConnection + 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 + menuItems(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 + 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 + 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 + 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 + vouchers(filter: VoucherFilterInput, sortBy: VoucherSortingInput, query: String, before: String, after: String, first: Int, last: Int): VoucherCountableConnection taxTypes: [TaxType] checkout(token: UUID): Checkout - checkouts( - before: String - after: String - first: Int - last: Int - ): CheckoutCountableConnection + checkouts(before: String, after: String, first: Int, last: Int): CheckoutCountableConnection checkoutLine(id: ID): CheckoutLine - checkoutLines( - before: String - after: String - first: Int - last: Int - ): CheckoutLineCountableConnection - addressValidationRules( - countryCode: CountryCode! - countryArea: String - city: String - cityArea: String - ): AddressValidationData + checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection + apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection + app(id: ID!): App + addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData address(id: ID!): Address - customers( - filter: CustomerFilterInput - sortBy: UserSortingInput - before: String - after: String - first: Int - last: Int - ): UserCountableConnection - permissionGroups( - filter: PermissionGroupFilterInput - sortBy: PermissionGroupSortingInput - before: String - after: String - first: Int - last: Int - ): GroupCountableConnection + customers(filter: CustomerFilterInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection + permissionGroups(filter: PermissionGroupFilterInput, sortBy: PermissionGroupSortingInput, before: String, after: String, first: Int, last: Int): GroupCountableConnection permissionGroup(id: ID!): Group me: User - staffUsers( - filter: StaffUserInput - sortBy: UserSortingInput - before: String - after: String - first: Int - last: Int - ): UserCountableConnection - serviceAccounts( - filter: ServiceAccountFilterInput - sortBy: ServiceAccountSortingInput - before: String - after: String - first: Int - last: Int - ): ServiceAccountCountableConnection - serviceAccount(id: ID!): ServiceAccount + staffUsers(filter: StaffUserInput, sortBy: UserSortingInput, before: String, after: String, first: Int, last: Int): UserCountableConnection + serviceAccounts(filter: ServiceAccountFilterInput, sortBy: ServiceAccountSortingInput, before: String, after: String, first: Int, last: Int): ServiceAccountCountableConnection @deprecated(reason: "Use the `apps` query instead. This field will be removed after 2020-07-31.") + serviceAccount(id: ID!): ServiceAccount @deprecated(reason: "Use the `app` query instead. This field will be removed after 2020-07-31.") user(id: ID!): User _entities(representations: [_Any]): [_Entity] _service: _Service @@ -5081,7 +3941,7 @@ type ReducedRate { rateType: TaxRateType! } -type Refresh { +type RefreshToken { token: String payload: GenericScalar } @@ -5097,19 +3957,13 @@ enum ReportingPeriod { } type RequestEmailChange { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } type RequestPasswordReset { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! } @@ -5120,41 +3974,20 @@ type Sale implements Node { 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 + 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 } type SaleAddCatalogues { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") sale: Sale discountErrors: [DiscountError!]! } type SaleBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! discountErrors: [DiscountError!]! } @@ -5171,19 +4004,13 @@ type SaleCountableEdge { } type SaleCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! sale: Sale } type SaleDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! sale: Sale } @@ -5207,10 +4034,7 @@ input SaleInput { } type SaleRemoveCatalogues { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") sale: Sale discountErrors: [DiscountError!]! } @@ -5236,10 +4060,7 @@ type SaleTranslatableContent implements Node { } type SaleTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! sale: Sale } @@ -5256,10 +4077,7 @@ enum SaleType { } type SaleUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! sale: Sale } @@ -5283,21 +4101,12 @@ type ServiceAccount implements Node & ObjectWithMetadata { tokens: [ServiceAccountToken] privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") } type ServiceAccountClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! serviceAccount: ServiceAccount } @@ -5314,20 +4123,14 @@ type ServiceAccountCountableEdge { } type ServiceAccountCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") authToken: String accountErrors: [AccountError!]! serviceAccount: ServiceAccount } type ServiceAccountDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! serviceAccount: ServiceAccount } @@ -5360,20 +4163,14 @@ type ServiceAccountToken implements Node { } type ServiceAccountTokenCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") authToken: String accountErrors: [AccountError!]! serviceAccountToken: ServiceAccountToken } type ServiceAccountTokenDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! serviceAccountToken: ServiceAccountToken } @@ -5384,29 +4181,20 @@ input ServiceAccountTokenInput { } type ServiceAccountUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! serviceAccount: ServiceAccount } type ServiceAccountUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! serviceAccount: ServiceAccount } type SetPassword { token: String - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -5460,29 +4248,20 @@ enum ShippingMethodTypeEnum { } type ShippingPriceBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! shippingErrors: [ShippingError!]! } type ShippingPriceCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + 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 } type ShippingPriceDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingMethod: ShippingMethod shippingZone: ShippingZone shippingErrors: [ShippingError!]! @@ -5500,19 +4279,13 @@ input ShippingPriceInput { } type ShippingPriceTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! shippingMethod: ShippingMethod } type ShippingPriceUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + 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 @@ -5529,10 +4302,7 @@ type ShippingZone implements Node { } type ShippingZoneBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! shippingErrors: [ShippingError!]! } @@ -5549,10 +4319,7 @@ type ShippingZoneCountableEdge { } type ShippingZoneCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingZone: ShippingZone shippingErrors: [ShippingError!]! } @@ -5565,19 +4332,13 @@ input ShippingZoneCreateInput { } type ShippingZoneDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingErrors: [ShippingError!]! shippingZone: ShippingZone } type ShippingZoneUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shippingZone: ShippingZone shippingErrors: [ShippingError!]! } @@ -5623,19 +4384,13 @@ type Shop { } type ShopAddressUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop shopErrors: [ShopError!]! } type ShopDomainUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop shopErrors: [ShopError!]! } @@ -5657,10 +4412,7 @@ enum ShopErrorCode { } type ShopFetchTaxRates { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop shopErrors: [ShopError!]! } @@ -5682,10 +4434,7 @@ input ShopSettingsInput { } type ShopSettingsTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop translationErrors: [TranslationError!]! } @@ -5696,10 +4445,7 @@ input ShopSettingsTranslationInput { } type ShopSettingsUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shop: Shop shopErrors: [ShopError!]! } @@ -5717,19 +4463,13 @@ input SiteDomainInput { } type StaffBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! staffErrors: [StaffError!]! } type StaffCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") staffErrors: [StaffError!]! user: User } @@ -5745,10 +4485,7 @@ input StaffCreateInput { } type StaffDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") staffErrors: [StaffError!]! user: User } @@ -5775,19 +4512,13 @@ type StaffNotificationRecipient implements Node { } type StaffNotificationRecipientCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shopErrors: [ShopError!]! staffNotificationRecipient: StaffNotificationRecipient } type StaffNotificationRecipientDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shopErrors: [ShopError!]! staffNotificationRecipient: StaffNotificationRecipient } @@ -5799,19 +4530,13 @@ input StaffNotificationRecipientInput { } type StaffNotificationRecipientUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") shopErrors: [ShopError!]! staffNotificationRecipient: StaffNotificationRecipient } type StaffUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") staffErrors: [StaffError!]! user: User } @@ -5835,9 +4560,9 @@ type Stock implements Node { warehouse: Warehouse! productVariant: ProductVariant! quantity: Int! - quantityAllocated: Int! id: ID! stockQuantity: Int! + quantityAllocated: Int! } enum StockAvailability { @@ -5873,7 +4598,6 @@ enum StockErrorCode { input StockFilterInput { quantity: Float - quantityAllocated: Float search: String } @@ -5960,18 +4684,7 @@ enum TransactionKind { CONFIRM } -union TranslatableItem = - ProductTranslatableContent - | CollectionTranslatableContent - | CategoryTranslatableContent - | AttributeTranslatableContent - | AttributeValueTranslatableContent - | ProductVariantTranslatableContent - | PageTranslatableContent - | ShippingMethodTranslatableContent - | SaleTranslatableContent - | VoucherTranslatableContent - | MenuItemTranslatableContent +union TranslatableItem = ProductTranslatableContent | CollectionTranslatableContent | CategoryTranslatableContent | AttributeTranslatableContent | AttributeValueTranslatableContent | ProductVariantTranslatableContent | PageTranslatableContent | ShippingMethodTranslatableContent | SaleTranslatableContent | VoucherTranslatableContent | MenuItemTranslatableContent type TranslatableItemConnection { pageInfo: PageInfo! @@ -6021,19 +4734,13 @@ input TranslationInput { scalar UUID type UpdateMetadata { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") metadataErrors: [MetadataError!]! item: ObjectWithMetadata } type UpdatePrivateMetadata { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") metadataErrors: [MetadataError!]! item: ObjectWithMetadata } @@ -6054,32 +4761,13 @@ type User implements Node & ObjectWithMetadata { defaultBillingAddress: Address privateMetadata: [MetadataItem]! metadata: [MetadataItem]! - privateMeta: [MetaStore]! - @deprecated( - reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31." - ) - meta: [MetaStore]! - @deprecated( - reason: "Use the `metadata` field. This field will be removed after 2020-07-31." - ) + privateMeta: [MetaStore]! @deprecated(reason: "Use the `privetaMetadata` field. This field will be removed after 2020-07-31.") + meta: [MetaStore]! @deprecated(reason: "Use the `metadata` field. This field will be removed after 2020-07-31.") addresses: [Address] checkout: Checkout - giftCards( - before: String - after: String - first: Int - last: Int - ): GiftCardCountableConnection - orders( - before: String - after: String - first: Int - last: Int - ): OrderCountableConnection - permissions: [Permission] - @deprecated( - reason: "Will be removed in Saleor 2.11.Use the `userPermissions` instead." - ) + giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection + orders(before: String, after: String, first: Int, last: Int): OrderCountableConnection + permissions: [Permission] @deprecated(reason: "Will be removed in Saleor 2.11.Use the `userPermissions` instead.") userPermissions: [UserPermission] permissionGroups: [Group] editableGroups: [Group] @@ -6089,46 +4777,31 @@ type User implements Node & ObjectWithMetadata { } type UserAvatarDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } type UserAvatarUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") user: User accountErrors: [AccountError!]! } type UserBulkSetActive { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! accountErrors: [AccountError!]! } type UserClearMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } type UserClearPrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -6174,19 +4847,13 @@ input UserSortingInput { } type UserUpdateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } type UserUpdatePrivateMeta { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") accountErrors: [AccountError!]! user: User } @@ -6198,20 +4865,14 @@ type VAT { } type VariantImageAssign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant image: ProductImage productErrors: [ProductError!]! } type VariantImageUnassign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant image: ProductImage productErrors: [ProductError!]! @@ -6246,42 +4907,21 @@ type Voucher implements Node { 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 + 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 } type VoucherAddCatalogues { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") voucher: Voucher discountErrors: [DiscountError!]! } type VoucherBulkDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") count: Int! discountErrors: [DiscountError!]! } @@ -6298,19 +4938,13 @@ type VoucherCountableEdge { } type VoucherCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! voucher: Voucher } type VoucherDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! voucher: Voucher } @@ -6349,10 +4983,7 @@ input VoucherInput { } type VoucherRemoveCatalogues { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") voucher: Voucher discountErrors: [DiscountError!]! } @@ -6380,10 +5011,7 @@ type VoucherTranslatableContent implements Node { } type VoucherTranslate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") translationErrors: [TranslationError!]! voucher: Voucher } @@ -6401,10 +5029,7 @@ enum VoucherTypeEnum { } type VoucherUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") discountErrors: [DiscountError!]! voucher: Voucher } @@ -6414,12 +5039,7 @@ type Warehouse implements Node { name: String! slug: String! companyName: String! - shippingZones( - before: String - after: String - first: Int - last: Int - ): ShippingZoneCountableConnection! + shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection! address: Address! email: String! } @@ -6447,10 +5067,7 @@ type WarehouseCountableEdge { } type WarehouseCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") warehouseErrors: [WarehouseError!]! warehouse: Warehouse } @@ -6465,10 +5082,7 @@ input WarehouseCreateInput { } type WarehouseDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") warehouseErrors: [WarehouseError!]! warehouse: Warehouse } @@ -6494,19 +5108,13 @@ input WarehouseFilterInput { } type WarehouseShippingZoneAssign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") warehouse: Warehouse warehouseErrors: [WarehouseError!]! } type WarehouseShippingZoneUnassign { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") warehouse: Warehouse warehouseErrors: [WarehouseError!]! } @@ -6521,10 +5129,7 @@ input WarehouseSortingInput { } type WarehouseUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") warehouseErrors: [WarehouseError!]! warehouse: Warehouse } @@ -6539,12 +5144,13 @@ input WarehouseUpdateInput { type Webhook implements Node { name: String! - serviceAccount: ServiceAccount! targetUrl: String! isActive: Boolean! secretKey: String id: ID! events: [WebhookEvent!]! + serviceAccount: ServiceAccount! @deprecated(reason: "Use the `app` field instead. This field will be removed after 2020-07-31.") + app: App! } type WebhookCountableConnection { @@ -6559,10 +5165,7 @@ type WebhookCountableEdge { } type WebhookCreate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") webhookErrors: [WebhookError!]! webhook: Webhook } @@ -6572,15 +5175,13 @@ input WebhookCreateInput { targetUrl: String events: [WebhookEventTypeEnum] serviceAccount: ID + app: ID isActive: Boolean secretKey: String } type WebhookDelete { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") webhook: Webhook webhookErrors: [WebhookError!]! } @@ -6638,6 +5239,7 @@ enum WebhookSortField { NAME SERVICE_ACCOUNT TARGET_URL + APP } input WebhookSortingInput { @@ -6646,10 +5248,7 @@ input WebhookSortingInput { } type WebhookUpdate { - errors: [Error!]! - @deprecated( - reason: "Use typed errors with error codes. This field will be removed after 2020-07-31." - ) + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") webhook: Webhook webhookErrors: [WebhookError!]! } @@ -6659,6 +5258,7 @@ input WebhookUpdateInput { targetUrl: String events: [WebhookEventTypeEnum] serviceAccount: ID + app: ID isActive: Boolean secretKey: String } @@ -6679,17 +5279,7 @@ enum WeightUnitsEnum { scalar _Any -union _Entity = - Address - | ServiceAccount - | User - | Group - | ProductVariant - | Product - | ProductType - | Collection - | Category - | ProductImage +union _Entity = Address | User | Group | ServiceAccount | App | ProductVariant | Product | ProductType | Collection | Category | ProductImage type _Service { sdl: String diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index e33d91b96..5e8f5ab12 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -172,7 +172,7 @@ export const CategoryDetails: React.FC = ({ disabled={loading} errors={updateResult.data?.categoryUpdate.errors || []} onAddCategory={() => navigate(categoryAddUrl(id))} - onAddProduct={() => navigate(productAddUrl)} + onAddProduct={() => navigate(productAddUrl())} onBack={() => navigate( maybe( diff --git a/src/components/CompanyAddressInput/CompanyAddressForm.tsx b/src/components/CompanyAddressInput/CompanyAddressForm.tsx new file mode 100644 index 000000000..a3b58f8a0 --- /dev/null +++ b/src/components/CompanyAddressInput/CompanyAddressForm.tsx @@ -0,0 +1,197 @@ +import { makeStyles } from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; +import { useIntl, IntlShape } from "react-intl"; + +import FormSpacer from "@saleor/components/FormSpacer"; +import Grid from "@saleor/components/Grid"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; +import { AddressTypeInput } from "@saleor/customers/types"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import getShopErrorMessage from "@saleor/utils/errors/shop"; +import { getFormErrors } from "@saleor/utils/errors"; +import { ShopErrorFragment } from "@saleor/siteSettings/types/ShopErrorFragment"; +import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment"; +import getAccountErrorMessage from "@saleor/utils/errors/account"; +import getWarehouseErrorMessage from "@saleor/utils/errors/warehouse"; +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; + +export interface CompanyAddressFormProps { + countries: SingleAutocompleteChoiceType[]; + data: AddressTypeInput; + displayCountry: string; + errors: Array< + AccountErrorFragment | ShopErrorFragment | WarehouseErrorFragment + >; + disabled: boolean; + onChange: (event: ChangeEvent) => void; + onCountryChange: (event: ChangeEvent) => void; +} + +const useStyles = makeStyles( + { + root: {} + }, + { name: "CompanyAddressForm" } +); + +function getErrorMessage( + err: AccountErrorFragment | ShopErrorFragment | WarehouseErrorFragment, + intl: IntlShape +): string { + switch (err?.__typename) { + case "AccountError": + return getAccountErrorMessage(err, intl); + case "WarehouseError": + return getWarehouseErrorMessage(err, intl); + default: + return getShopErrorMessage(err, intl); + } +} + +const CompanyAddressForm: React.FC = props => { + const { + countries, + data, + disabled, + displayCountry, + errors, + onChange, + onCountryChange + } = props; + + const classes = useStyles(props); + const intl = useIntl(); + + const formFields = [ + "companyName", + "streetAddress1", + "streetAddress2", + "city", + "postalCode", + "country", + "companyArea", + "phone" + ]; + const formErrors = getFormErrors(formFields, errors); + + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); +}; +CompanyAddressForm.displayName = "CompanyAddressForm"; +export default CompanyAddressForm; diff --git a/src/components/CompanyAddressInput/CompanyAddressInput.tsx b/src/components/CompanyAddressInput/CompanyAddressInput.tsx new file mode 100644 index 000000000..87ecaf788 --- /dev/null +++ b/src/components/CompanyAddressInput/CompanyAddressInput.tsx @@ -0,0 +1,40 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { makeStyles } from "@material-ui/core/styles"; +import React from "react"; +import classNames from "classnames"; + +import CardTitle from "../CardTitle"; +import CompanyAddressForm, { + CompanyAddressFormProps +} from "./CompanyAddressForm"; + +interface CompanyAddressInputProps extends CompanyAddressFormProps { + className?: string; + header: string; +} + +const useStyles = makeStyles( + { + root: { + overflow: "visible" + } + }, + { name: "CompanyAddressInput" } +); + +const CompanyAddressInput: React.FC = props => { + const { className, header, ...formProps } = props; + const classes = useStyles(props); + + return ( + + + + + + + ); +}; +CompanyAddressInput.displayName = "CompanyAddressInput"; +export default CompanyAddressInput; diff --git a/src/components/CompanyAddressInput/index.ts b/src/components/CompanyAddressInput/index.ts new file mode 100644 index 000000000..9abca5b78 --- /dev/null +++ b/src/components/CompanyAddressInput/index.ts @@ -0,0 +1,2 @@ +export { default } from "./CompanyAddressInput"; +export * from "./CompanyAddressInput"; diff --git a/src/components/ControlledCheckbox.tsx b/src/components/ControlledCheckbox.tsx index 4a79d9085..ebace41f9 100644 --- a/src/components/ControlledCheckbox.tsx +++ b/src/components/ControlledCheckbox.tsx @@ -25,6 +25,7 @@ export const ControlledCheckbox: React.FC = ({ control={ onChange({ target: { name, value: !checked } })} diff --git a/src/components/LinkChoice/LinkChoice.stories.tsx b/src/components/LinkChoice/LinkChoice.stories.tsx new file mode 100644 index 000000000..0b7f17610 --- /dev/null +++ b/src/components/LinkChoice/LinkChoice.stories.tsx @@ -0,0 +1,26 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { countries } from "@saleor/fixtures"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import Form from "../Form"; +import LinkChoice, { LinkChoiceProps } from "./LinkChoice"; + +const suggestions = countries.map(c => ({ label: c.name, value: c.code })); + +const props: Omit = { + choices: suggestions.slice(0, 10), + name: "country" +}; + +storiesOf("Generics / Link with choices", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ( +
+ {({ change, data }) => ( + + )} + + )); diff --git a/src/components/LinkChoice/LinkChoice.tsx b/src/components/LinkChoice/LinkChoice.tsx new file mode 100644 index 000000000..8916ea2fa --- /dev/null +++ b/src/components/LinkChoice/LinkChoice.tsx @@ -0,0 +1,159 @@ +import React from "react"; +import ClickAwayListener from "@material-ui/core/ClickAwayListener"; +import Paper from "@material-ui/core/Paper"; +import MenuItem from "@material-ui/core/MenuItem"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import Popper from "@material-ui/core/Popper"; +import { fade } from "@material-ui/core/styles/colorManipulator"; +import classNames from "classnames"; +import { codes } from "keycode"; + +import ArrowDropdown from "@saleor/icons/ArrowDropdown"; +import { FormChange } from "@saleor/hooks/useForm"; +import { SingleAutocompleteChoiceType } from "../SingleAutocompleteSelectField"; +import Link from "../Link"; + +const useStyles = makeStyles( + theme => ({ + arrow: { + position: "relative", + top: 6, + transition: theme.transitions.duration.short + "ms" + }, + highlighted: { + background: theme.palette.background.default + }, + menuItem: { + "&:not(:last-of-type)": { + marginBottom: theme.spacing() + } + }, + paper: { + padding: theme.spacing() + }, + popper: { + boxShadow: `0px 5px 10px 0 ${fade(theme.palette.common.black, 0.05)}`, + marginTop: theme.spacing(1), + zIndex: 2 + }, + root: { + "&:focus": { + textDecoration: "underline" + }, + outline: 0, + position: "relative" + }, + rotate: { + transform: "rotate(180deg)" + } + }), + { + name: "LinkChoice" + } +); + +export interface LinkChoiceProps { + className?: string; + choices: SingleAutocompleteChoiceType[]; + name?: string; + value: string; + onChange: FormChange; +} + +const LinkChoice: React.FC = ({ + className, + choices, + name, + value, + onChange +}) => { + const classes = useStyles({}); + const [open, setOpen] = React.useState(false); + const anchor = React.useRef(null); + const current = choices.find(c => c.value === value); + const [highlightedIndex, setHighlightedIndex] = React.useState(0); + + const handleChange = (value: string) => { + setOpen(false); + onChange({ + target: { + name, + value + } + }); + }; + + const handleKeyPress = (event: React.KeyboardEvent) => { + switch (event.keyCode) { + case codes.down: + setHighlightedIndex( + highlightedIndex => (highlightedIndex + 1) % choices.length + ); + break; + case codes.up: + setHighlightedIndex(highlightedIndex => + highlightedIndex === 0 + ? choices.length - 1 + : (highlightedIndex - 1) % choices.length + ); + break; + case codes.enter: + if (open) { + handleChange(choices[highlightedIndex].value); + } else { + setOpen(true); + } + break; + } + }; + + return ( + + setOpen(open => !open)}>{current.label} + + + + setOpen(false)} + mouseEvent="onClick" + > + + {choices.map((choice, choiceIndex) => ( + handleChange(choice.value)} + data-tc="select-option" + > + {choice.label} + + ))} + + + + + ); +}; + +LinkChoice.displayName = "LinkChoice"; +export default LinkChoice; diff --git a/src/components/LinkChoice/index.ts b/src/components/LinkChoice/index.ts new file mode 100644 index 000000000..9c927a90f --- /dev/null +++ b/src/components/LinkChoice/index.ts @@ -0,0 +1,2 @@ +export { default } from "./LinkChoice"; +export * from "./LinkChoice"; diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx index 31f07ccd6..d7aab9a97 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx @@ -12,7 +12,8 @@ import Debounce, { DebounceProps } from "@saleor/components/Debounce"; import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown"; import { FetchMoreProps } from "@saleor/types"; import MultiAutocompleteSelectFieldContent, { - MultiAutocompleteChoiceType + MultiAutocompleteChoiceType, + MultiAutocompleteActionType } from "./MultiAutocompleteSelectFieldContent"; const useStyles = makeStyles( @@ -71,6 +72,7 @@ const useStyles = makeStyles( export interface MultiAutocompleteSelectFieldProps extends Partial { + add?: MultiAutocompleteActionType; allowCustomValues?: boolean; displayValues: MultiAutocompleteChoiceType[]; error?: boolean; @@ -91,6 +93,7 @@ const DebounceAutocomplete: React.ComponentType = props => { const { + add, allowCustomValues, choices, displayValues, @@ -129,6 +132,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC ""} > {({ + closeMenu, getInputProps, getItemProps, isOpen, @@ -166,6 +170,13 @@ const MultiAutocompleteSelectFieldComponent: React.FC {isOpen && (!!inputValue || !!choices.length) && ( { + add.onClick(); + closeMenu(); + } + }} choices={choices.filter( choice => !value.includes(choice.value) )} diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectFieldContent.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectFieldContent.tsx index f58e680dd..9bc21a39a 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectFieldContent.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectFieldContent.tsx @@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl"; import chevronDown from "@assets/images/ChevronDown.svg"; import CircularProgress from "@material-ui/core/CircularProgress"; import MenuItem from "@material-ui/core/MenuItem"; +import Typography from "@material-ui/core/Typography"; import Paper from "@material-ui/core/Paper"; import { makeStyles } from "@material-ui/core/styles"; import AddIcon from "@material-ui/icons/Add"; @@ -22,6 +23,10 @@ const menuItemHeight = 46; const maxMenuItems = 5; const offset = 24; +export interface MultiAutocompleteActionType { + label: string; + onClick: () => void; +} export interface MultiAutocompleteChoiceType { label: string; value: any; @@ -29,6 +34,7 @@ export interface MultiAutocompleteChoiceType { } export interface MultiAutocompleteSelectFieldContentProps extends Partial { + add?: MultiAutocompleteActionType; choices: MultiAutocompleteChoiceType[]; displayCustomValue: boolean; displayValues: MultiAutocompleteChoiceType[]; @@ -39,6 +45,14 @@ export interface MultiAutocompleteSelectFieldContentProps const useStyles = makeStyles( theme => ({ + add: { + background: theme.palette.background.default, + border: `1px solid ${theme.palette.divider}`, + borderRadius: "100%", + height: 24, + margin: theme.spacing(), + width: 24 + }, addIcon: { height: 24, margin: 9, @@ -100,6 +114,7 @@ const useStyles = makeStyles( gridColumnGap: theme.spacing(1), gridTemplateColumns: "30px 1fr", height: "auto", + marginBottom: theme.spacing(0.5), padding: 0, whiteSpace: "normal" }, @@ -130,10 +145,11 @@ const useStyles = makeStyles( function getChoiceIndex( index: number, displayValues: MultiAutocompleteChoiceType[], - displayCustomValue: boolean + displayCustomValue: boolean, + add: boolean ) { let choiceIndex = index; - if (displayCustomValue) { + if (add || displayCustomValue) { choiceIndex += 2; } if (displayValues.length > 0) { @@ -145,6 +161,7 @@ function getChoiceIndex( const MultiAutocompleteSelectFieldContent: React.FC = props => { const { + add, choices, displayCustomValue, displayValues, @@ -156,6 +173,10 @@ const MultiAutocompleteSelectFieldContent: React.FC(); const scrollPosition = useElementScroll(anchor); @@ -183,6 +204,20 @@ const MultiAutocompleteSelectFieldContent: React.FC 0 || displayCustomValue ? ( <> + {add && ( + + + {add.label} + + )} {displayCustomValue && ( { - navigate(productAddUrl); + navigate(productAddUrl()); return false; } }, diff --git a/src/components/Navigator/modes/default/views.ts b/src/components/Navigator/modes/default/views.ts index c2cea32c5..c489ce6fb 100644 --- a/src/components/Navigator/modes/default/views.ts +++ b/src/components/Navigator/modes/default/views.ts @@ -21,6 +21,7 @@ import { staffListUrl } from "@saleor/staff/urls"; import { countryListUrl } from "@saleor/taxes/urls"; import { languageListUrl } from "@saleor/translations/urls"; import { webhookListUrl } from "@saleor/webhooks/urls"; +import { warehouseListUrl } from "@saleor/warehouses/urls"; import { QuickSearchActionInput } from "../../types"; interface View { @@ -116,6 +117,10 @@ function searchInViews( { label: intl.formatMessage(sectionNames.webhooks), url: webhookListUrl() + }, + { + label: intl.formatMessage(sectionNames.warehouses), + url: warehouseListUrl() } ]; diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx index 580386798..f07b2089c 100644 --- a/src/components/PageHeader/PageHeader.tsx +++ b/src/components/PageHeader/PageHeader.tsx @@ -28,7 +28,7 @@ interface PageHeaderProps { children?: React.ReactNode; className?: string; inline?: boolean; - title?: string; + title?: React.ReactNode; } const PageHeader: React.FC = props => { diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.stories.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.stories.tsx index ff1c03143..303bf4fa2 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.stories.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.stories.tsx @@ -69,6 +69,7 @@ const Story: React.FC ( )) + .add("with add", () => ( + undefined + }} + /> + )) .add("can load more", () => ( )) diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx index 48b6e0692..f140d4c05 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx @@ -10,7 +10,8 @@ import { FetchMoreProps } from "@saleor/types"; import ArrowDropdownIcon from "../../icons/ArrowDropdown"; import Debounce, { DebounceProps } from "../Debounce"; import SingleAutocompleteSelectFieldContent, { - SingleAutocompleteChoiceType + SingleAutocompleteChoiceType, + SingleAutocompleteActionType } from "./SingleAutocompleteSelectFieldContent"; const useStyles = makeStyles( @@ -25,6 +26,7 @@ const useStyles = makeStyles( export interface SingleAutocompleteSelectFieldProps extends Partial { + add?: SingleAutocompleteActionType; error?: boolean; name: string; displayValue: string; @@ -47,6 +49,7 @@ const DebounceAutocomplete: React.ComponentType = props => { const { + add, allowCustomValues, choices, disabled, @@ -144,6 +147,15 @@ const SingleAutocompleteSelectFieldComponent: React.FC {isOpen && (!!inputValue || !!choices.length) && ( { + add.onClick(); + closeMenu(); + } + } + } choices={choices} displayCustomValue={displayCustomValue} emptyOption={emptyOption} diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx index 602a24b47..1375deebf 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx @@ -1,5 +1,6 @@ import CircularProgress from "@material-ui/core/CircularProgress"; import MenuItem from "@material-ui/core/MenuItem"; +import Add from "@material-ui/icons/Add"; import Paper from "@material-ui/core/Paper"; import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; @@ -24,8 +25,13 @@ export interface SingleAutocompleteChoiceType { label: string; value: any; } +export interface SingleAutocompleteActionType { + label: string; + onClick: () => void; +} export interface SingleAutocompleteSelectFieldContentProps extends Partial { + add?: SingleAutocompleteActionType; choices: SingleAutocompleteChoiceType[]; displayCustomValue: boolean; emptyOption: boolean; @@ -38,6 +44,14 @@ export interface SingleAutocompleteSelectFieldContentProps const useStyles = makeStyles( theme => ({ + add: { + background: theme.palette.background.default, + border: `1px solid ${theme.palette.divider}`, + borderRadius: "100%", + height: 24, + marginRight: theme.spacing(), + width: 24 + }, arrowContainer: { position: "relative" }, @@ -96,23 +110,23 @@ const useStyles = makeStyles( function getChoiceIndex( index: number, emptyValue: boolean, - customValue: boolean + customValue: boolean, + add: boolean ) { let choiceIndex = index; if (emptyValue) { choiceIndex += 1; } - if (customValue) { + if (customValue || add) { choiceIndex += 2; } return choiceIndex; } -const SingleAutocompleteSelectFieldContent: React.FC< - SingleAutocompleteSelectFieldContentProps -> = props => { +const SingleAutocompleteSelectFieldContent: React.FC = props => { const { + add, choices, displayCustomValue, emptyOption, @@ -125,6 +139,10 @@ const SingleAutocompleteSelectFieldContent: React.FC< onFetchMore } = props; + if (!!add && !!displayCustomValue) { + throw new Error("Add and custom value cannot be displayed simultaneously"); + } + const classes = useStyles(props); const anchor = React.useRef(); const scrollPosition = useElementScroll(anchor); @@ -164,6 +182,20 @@ const SingleAutocompleteSelectFieldContent: React.FC< )} + {add && ( + + + {add.label} + + )} {displayCustomValue && ( )} - {choices.length > 0 && displayCustomValue && ( + {choices.length > 0 && (!!add || displayCustomValue) && (
)} {choices.map((suggestion, index) => { const choiceIndex = getChoiceIndex( index, emptyOption, - displayCustomValue + displayCustomValue, + !!add ); return ( diff --git a/src/config.ts b/src/config.ts index 83d7fcd5a..90b81e7c8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -34,6 +34,7 @@ export interface AppListViewSettings { [ListViews.STAFF_MEMBERS_LIST]: ListSettings; [ListViews.PERMISSION_GROUP_LIST]: ListSettings; [ListViews.VOUCHER_LIST]: ListSettings; + [ListViews.WAREHOUSE_LIST]: ListSettings; [ListViews.WEBHOOK_LIST]: ListSettings; } export const defaultListSettings: AppListViewSettings = { @@ -80,6 +81,9 @@ export const defaultListSettings: AppListViewSettings = { [ListViews.VOUCHER_LIST]: { rowNumber: PAGINATE_BY }, + [ListViews.WAREHOUSE_LIST]: { + rowNumber: PAGINATE_BY + }, [ListViews.WEBHOOK_LIST]: { rowNumber: PAGINATE_BY } diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 6b1c17a84..e597ad17c 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -31,6 +31,8 @@ import { permissionGroupListUrl } from "@saleor/permissionGroups/urls"; import { taxSection } from "@saleor/taxes/urls"; import { PermissionEnum } from "@saleor/types/globalTypes"; import { webhookListUrl } from "@saleor/webhooks/urls"; +import Warehouses from "@saleor/icons/Warehouses"; +import { warehouseSection } from "@saleor/warehouses/urls"; import ConfigurationPage, { MenuSection } from "./ConfigurationPage"; export function createConfigurationMenu(intl: IntlShape): MenuSection[] { @@ -67,16 +69,6 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { defaultMessage: "Product Settings" }), menuItems: [ - { - description: intl.formatMessage({ - defaultMessage: "Manage how you ship out orders", - id: "configurationMenuShipping" - }), - icon: , - permission: PermissionEnum.MANAGE_SHIPPING, - title: intl.formatMessage(sectionNames.shipping), - url: shippingZonesListUrl() - }, { description: intl.formatMessage({ defaultMessage: "Manage how your store charges tax", @@ -117,6 +109,33 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { } ] }, + { + label: intl.formatMessage({ + defaultMessage: "Product Settings" + }), + menuItems: [ + { + description: intl.formatMessage({ + defaultMessage: "Manage how you ship out orders", + id: "configurationMenuShipping" + }), + icon: , + permission: PermissionEnum.MANAGE_SHIPPING, + title: intl.formatMessage(sectionNames.shipping), + url: shippingZonesListUrl() + }, + { + description: intl.formatMessage({ + defaultMessage: "Manage and update your warehouse information", + id: "configurationMenuWarehouses" + }), + icon: , + permission: PermissionEnum.MANAGE_PRODUCTS, + title: intl.formatMessage(sectionNames.warehouses), + url: warehouseSection + } + ] + }, { label: intl.formatMessage({ defaultMessage: "Miscellaneous" diff --git a/src/fixtures.ts b/src/fixtures.ts index ebb678030..037506378 100644 --- a/src/fixtures.ts +++ b/src/fixtures.ts @@ -485,3 +485,23 @@ export const adminUserPermissions: User_userPermissions[] = [ name: "Manage customers." } ]; + +export const address = { + __typename: "Address" as "Address", + city: "Port Danielshire", + cityArea: "", + companyName: "", + country: { + __typename: "CountryDisplay" as "CountryDisplay", + code: "SE", + country: "Szwecja" + }, + countryArea: "", + firstName: "Elizabeth", + id: "QWRkcmVzczoy", + lastName: "Vaughn", + phone: "", + postalCode: "52203", + streetAddress1: "419 Ruiz Orchard Apt. 199", + streetAddress2: "0238 Cremin Freeway" +}; diff --git a/src/hooks/useFormset.ts b/src/hooks/useFormset.ts index 8891d48b5..eb506e272 100644 --- a/src/hooks/useFormset.ts +++ b/src/hooks/useFormset.ts @@ -18,8 +18,8 @@ export interface UseFormsetOutput { set: (data: FormsetData) => void; } function useFormset( - initial: FormsetData -): UseFormsetOutput { + initial: FormsetData +): UseFormsetOutput { const [data, setData] = useStateFromProps>( initial || [] ); diff --git a/src/hooks/useListSettings.ts b/src/hooks/useListSettings.ts index 75c1afd86..cac29fca9 100644 --- a/src/hooks/useListSettings.ts +++ b/src/hooks/useListSettings.ts @@ -1,3 +1,4 @@ +import { useEffect } from "react"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; import { AppListViewSettings, defaultListSettings } from "./../config"; import { ListSettings, ListViews } from "./../types"; @@ -14,6 +15,15 @@ export default function useListSettings( defaultListSettings ); + useEffect(() => { + if (settings[listName] === undefined) { + setListSettings(settings => ({ + ...settings, + [listName]: defaultListSettings[listName] + })); + } + }, []); + const updateListSettings = (key: keyof ListSettings, value: any) => setListSettings(settings => ({ ...settings, diff --git a/src/hooks/useWizard.ts b/src/hooks/useWizard.ts new file mode 100644 index 000000000..8e33d0d0b --- /dev/null +++ b/src/hooks/useWizard.ts @@ -0,0 +1,61 @@ +import { useState } from "react"; + +export interface UseWizardActions { + next: () => void; + prev: () => void; + set: (step: T) => void; +} +export interface UseWizardOpts { + onTransition: (prevStep: T, nextStep: T) => void; +} +export type UseWizard = [T, UseWizardActions]; +function useWizard( + initial: T, + steps: T[], + opts?: UseWizardOpts +): UseWizard { + const [stepIndex, setStepIndex] = useState(steps.indexOf(initial)); + + function goToStep(nextStepIndex) { + if (typeof opts?.onTransition === "function") { + opts.onTransition(steps[stepIndex], steps[nextStepIndex]); + } + setStepIndex(nextStepIndex); + } + + function next() { + if (stepIndex === steps.length - 1) { + console.error("This is the last step"); + } else { + goToStep(stepIndex + 1); + } + } + + function prev() { + if (stepIndex === 0) { + console.error("This is the first step"); + } else { + goToStep(stepIndex - 1); + } + } + + function set(step: T) { + const newStepIndex = steps.findIndex(s => s === step); + if (newStepIndex === -1) { + console.error("Step does not exist"); + } else { + goToStep(newStepIndex); + } + } + + return [ + steps[stepIndex], + { + next, + prev, + set + } + ]; +} + +export default useWizard; diff --git a/src/icons/Warehouses.tsx b/src/icons/Warehouses.tsx new file mode 100644 index 000000000..56170523f --- /dev/null +++ b/src/icons/Warehouses.tsx @@ -0,0 +1,16 @@ +import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; +import React from "react"; + +const Warehouses = createSvgIcon( + <> + + , + "Warehouses" +); + +export default Warehouses; diff --git a/src/index.tsx b/src/index.tsx index db050df08..12279e63b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -55,6 +55,8 @@ import TaxesSection from "./taxes"; import TranslationsSection from "./translations"; import { PermissionEnum } from "./types/globalTypes"; import WebhooksSection from "./webhooks"; +import { warehouseSection } from "./warehouses/urls"; +import WarehouseSection from "./warehouses"; interface ResponseError extends ErrorResponse { networkError?: Error & { @@ -263,6 +265,11 @@ const Routes: React.FC = () => { path={serviceSection} component={ServiceSection} /> + {createConfigurationMenu(intl).filter(menu => menu.menuItems.map(item => hasPermission(item.permission, user) diff --git a/src/intl.ts b/src/intl.ts index 8b7328cff..458961851 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -81,6 +81,10 @@ export const commonMessages = defineMessages({ }); export const buttonMessages = defineMessages({ + accept: { + defaultMessage: "Accept", + description: "button" + }, back: { defaultMessage: "Back", description: "button" @@ -117,6 +121,10 @@ export const buttonMessages = defineMessages({ defaultMessage: "Manage", description: "button" }, + ok: { + defaultMessage: "OK", + description: "button" + }, remove: { defaultMessage: "Remove", description: "button" @@ -224,6 +232,10 @@ export const sectionNames = defineMessages({ defaultMessage: "Vouchers", description: "vouchers section name" }, + warehouses: { + defaultMessage: "Warehouses", + description: "warehouses section name" + }, webhooks: { defaultMessage: "Webhooks", description: "webhooks section name" diff --git a/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx b/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx index bbbc81e28..d8d073963 100644 --- a/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx +++ b/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx @@ -4,14 +4,13 @@ import { FormattedMessage, useIntl } from "react-intl"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; export interface OrderBulkCancelDialogProps { confirmButtonState: ConfirmButtonTransitionState; numberOfOrders: string; open: boolean; onClose: () => void; - onConfirm: (restock: boolean) => void; + onConfirm: () => void; } const OrderBulkCancelDialog: React.FC = ({ @@ -22,7 +21,6 @@ const OrderBulkCancelDialog: React.FC = ({ onConfirm }) => { const intl = useIntl(); - const [restock, setRestock] = React.useState(true); return ( = ({ description: "dialog header" })} onClose={onClose} - onConfirm={() => onConfirm(restock)} + onConfirm={onConfirm} > = ({ }} /> - setRestock(event.target.value)} - /> ); }; diff --git a/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx b/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx index b65368c00..34748e33b 100644 --- a/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx +++ b/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx @@ -4,45 +4,25 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogContentText from "@material-ui/core/DialogContentText"; import DialogTitle from "@material-ui/core/DialogTitle"; -import { makeStyles } from "@material-ui/core/styles"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; -import Form from "@saleor/components/Form"; import { buttonMessages } from "@saleor/intl"; import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment"; import FormSpacer from "@saleor/components/FormSpacer"; import getOrderErrorMessage from "@saleor/utils/errors/order"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; -export interface FormData { - restock: boolean; -} - -const useStyles = makeStyles( - theme => ({ - deleteButton: { - "&:hover": { - backgroundColor: theme.palette.error.main - }, - backgroundColor: theme.palette.error.main, - color: theme.palette.error.contrastText - } - }), - { name: "OrderCancelDialog" } -); - export interface OrderCancelDialogProps { confirmButtonState: ConfirmButtonTransitionState; errors: OrderErrorFragment[]; number: string; open: boolean; - onClose?(); - onSubmit(data: FormData); + onClose: () => void; + onSubmit: () => void; } const OrderCancelDialog: React.FC = props => { @@ -55,76 +35,52 @@ const OrderCancelDialog: React.FC = props => { onClose } = props; - const classes = useStyles(props); const intl = useIntl(); const errors = useModalDialogErrors(apiErrors, open); return ( - -
- {({ data, change }) => ( + + + + + + + {chunks}, + orderNumber + }} + /> + + {errors.length > 0 && ( <> - - - - - - - + + {errors.map(err => ( + + {getOrderErrorMessage(err, intl)} - {errors.length > 0 && ( - <> - - {errors.map(err => ( - - {getOrderErrorMessage(err, intl)} - - ))} - - )} - - - - - - - + ))} )} - + + + + + + +
); }; diff --git a/src/orders/components/OrderCannotCancelOrderDialog/OrderCannotCancelOrderDialog.tsx b/src/orders/components/OrderCannotCancelOrderDialog/OrderCannotCancelOrderDialog.tsx new file mode 100644 index 000000000..b2a515ea2 --- /dev/null +++ b/src/orders/components/OrderCannotCancelOrderDialog/OrderCannotCancelOrderDialog.tsx @@ -0,0 +1,57 @@ +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 DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { buttonMessages } from "@saleor/intl"; +import { DialogProps } from "@saleor/types"; + +const useStyles = makeStyles( + theme => ({ + button: { + backgroundColor: theme.palette.error.main + } + }), + { + name: "OrderCannotCancelOrderDialog" + } +); + +const OrderCannotCancelOrderDialog: React.FC = ({ + open, + onClose +}) => { + const classes = useStyles({}); + + return ( + + + + + + + + + + + + + + ); +}; +OrderCannotCancelOrderDialog.displayName = "OrderCannotCancelOrderDialog"; +export default OrderCannotCancelOrderDialog; diff --git a/src/orders/components/OrderCannotCancelOrderDialog/index.ts b/src/orders/components/OrderCannotCancelOrderDialog/index.ts new file mode 100644 index 000000000..7a5eb78e1 --- /dev/null +++ b/src/orders/components/OrderCannotCancelOrderDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./OrderCannotCancelOrderDialog"; +export * from "./OrderCannotCancelOrderDialog"; diff --git a/src/orders/components/OrderFulfillPage/OrderFulfillPage.stories.tsx b/src/orders/components/OrderFulfillPage/OrderFulfillPage.stories.tsx new file mode 100644 index 000000000..cbd11bbdc --- /dev/null +++ b/src/orders/components/OrderFulfillPage/OrderFulfillPage.stories.tsx @@ -0,0 +1,44 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import Decorator from "@saleor/storybook/Decorator"; +import { warehouseList } from "@saleor/warehouses/fixtures"; +import { OrderErrorCode } from "@saleor/types/globalTypes"; +import OrderFulfillPage, { OrderFulfillPageProps } from "./OrderFulfillPage"; +import { orderToFulfill } from "./fixtures"; + +const props: OrderFulfillPageProps = { + disabled: false, + errors: [], + onBack: () => undefined, + onSubmit: () => undefined, + order: orderToFulfill, + saveButtonBar: "default", + warehouses: warehouseList +}; + +storiesOf("Views / Orders / Fulfill order", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("error", () => ( + + )); diff --git a/src/orders/components/OrderFulfillPage/OrderFulfillPage.tsx b/src/orders/components/OrderFulfillPage/OrderFulfillPage.tsx new file mode 100644 index 000000000..155eb421d --- /dev/null +++ b/src/orders/components/OrderFulfillPage/OrderFulfillPage.tsx @@ -0,0 +1,413 @@ +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import Card from "@material-ui/core/Card"; +import CardActions from "@material-ui/core/CardActions"; +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 TextField from "@material-ui/core/TextField"; +import classNames from "classnames"; +import Typography from "@material-ui/core/Typography"; + +import useFormset, { FormsetData } from "@saleor/hooks/useFormset"; +import { + OrderFulfillStockInput, + OrderErrorCode +} from "@saleor/types/globalTypes"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Form from "@saleor/components/Form"; +import { + OrderFulfillData_order, + OrderFulfillData_order_lines +} from "@saleor/orders/types/OrderFulfillData"; +import CardTitle from "@saleor/components/CardTitle"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import { update } from "@saleor/utils/lists"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import { renderCollection } from "@saleor/misc"; +import Skeleton from "@saleor/components/Skeleton"; +import AppHeader from "@saleor/components/AppHeader"; +import { FulfillOrder_orderFulfill_errors } from "@saleor/orders/types/FulfillOrder"; + +const useStyles = makeStyles( + theme => ({ + actionBar: { + flexDirection: "row", + paddingLeft: theme.spacing(2) + 2 + }, + colName: { + width: 250 + }, + colQuantity: { + width: 210 + }, + colQuantityContent: { + alignItems: "center", + display: "inline-flex" + }, + colQuantityHeader: { + textAlign: "right" + }, + colQuantityTotal: { + textAlign: "right", + width: 180 + }, + colSku: { + textAlign: "right", + width: 150 + }, + error: { + color: theme.palette.error.main + }, + full: { + fontWeight: 600 + }, + quantityInnerInput: { + padding: "16px 0 14px 12px" + }, + quantityInput: { + width: 100 + }, + remainingQuantity: { + marginLeft: theme.spacing() + }, + table: { + "&&": { + tableLayout: "fixed" + } + } + }), + { name: "OrderFulfillPage" } +); + +interface OrderFulfillFormData { + sendInfo: boolean; +} +interface OrderFulfillSubmitData extends OrderFulfillFormData { + items: FormsetData; +} +export interface OrderFulfillPageProps { + disabled: boolean; + errors: FulfillOrder_orderFulfill_errors[]; + order: OrderFulfillData_order; + saveButtonBar: ConfirmButtonTransitionState; + warehouses: WarehouseFragment[]; + onBack: () => void; + onSubmit: (data: OrderFulfillSubmitData) => void; +} + +const initialFormData: OrderFulfillFormData = { + sendInfo: true +}; + +function getRemainingQuantity(line: OrderFulfillData_order_lines): number { + return line.quantity - line.quantityFulfilled; +} + +const OrderFulfillPage: React.FC = ({ + disabled, + errors, + order, + saveButtonBar, + warehouses, + onBack, + onSubmit +}) => { + const intl = useIntl(); + const classes = useStyles({}); + + const { change: formsetChange, data: formsetData } = useFormset< + null, + OrderFulfillStockInput[] + >( + order?.lines + .filter(line => getRemainingQuantity(line) > 0) + .map(line => ({ + data: null, + id: line.id, + label: line.variant.attributes + .map(attribute => + attribute.values + .map(attributeValue => attributeValue.name) + .join(" , ") + ) + .join(" / "), + value: line.variant.stocks.map(stock => ({ + quantity: 0, + warehouse: stock.warehouse.id + })) + })) + ); + + const handleSubmit = (formData: OrderFulfillFormData) => + onSubmit({ + ...formData, + items: formsetData + }); + + return ( + + + {order?.number + ? intl.formatMessage( + { + defaultMessage: "Order #{orderNumber}", + description: "page header with order number" + }, + { + orderNumber: order.number + } + ) + : intl.formatMessage({ + defaultMessage: "Order", + description: "page header" + })} + + +
+ {({ change, data, submit }) => ( + <> + + + + + + + + + + + + {warehouses?.map(warehouse => ( + + {warehouse.name} + + ))} + + + + + + + {renderCollection( + order?.lines.filter(line => getRemainingQuantity(line) > 0), + (line, lineIndex) => { + if (!line) { + return ( + + + + + + + + {warehouses?.map(warehouse => ( + + + + ))} + + {" "} + + + + ); + } + + const remainingQuantity = getRemainingQuantity(line); + const quantityToFulfill = formsetData[ + lineIndex + ].value.reduce( + (quantityToFulfill, lineInput) => + quantityToFulfill + (lineInput.quantity || 0), + 0 + ); + const overfulfill = remainingQuantity < quantityToFulfill; + + return ( + + + {line.productName} + + {line.variant.attributes + .map(attribute => + attribute.values + .map(attributeValue => attributeValue.name) + .join(", ") + ) + .join(" / ")} + + + + {line.variant.sku} + + {warehouses?.map(warehouse => { + const warehouseStock = line.variant.stocks.find( + stock => stock.warehouse.id === warehouse.id + ); + const formsetStock = formsetData[ + lineIndex + ].value.find( + line => line.warehouse === warehouse.id + ); + + if (!warehouseStock) { + return ( + + + + ); + } + + const availableQuantity = + warehouseStock.quantity - + warehouseStock.quantityAllocated; + + return ( + +
+ + formsetChange( + line.id, + update( + { + quantity: parseInt( + event.target.value, + 10 + ), + warehouse: warehouse.id + }, + formsetData[lineIndex].value, + (a, b) => a.warehouse === b.warehouse + ) + ) + } + error={ + overfulfill || + formsetStock.quantity > + availableQuantity || + !!errors?.find( + err => + err.warehouse === warehouse.id && + err.orderLine === line.id && + err.code === + OrderErrorCode.INSUFFICIENT_STOCK + ) + } + /> +
+ / {availableQuantity} +
+
+
+ ); + })} + + + {quantityToFulfill} + {" "} + / {remainingQuantity} + +
+ ); + } + )} +
+
+ + + +
+ + + )} + +
+ ); +}; + +OrderFulfillPage.displayName = "OrderFulfillPage"; +export default OrderFulfillPage; diff --git a/src/orders/components/OrderFulfillPage/fixtures.ts b/src/orders/components/OrderFulfillPage/fixtures.ts new file mode 100644 index 000000000..fdb2857bc --- /dev/null +++ b/src/orders/components/OrderFulfillPage/fixtures.ts @@ -0,0 +1,172 @@ +/* eslint-disable sort-keys */ + +import { OrderFulfillData_order } from "@saleor/orders/types/OrderFulfillData"; +import { warehouseList } from "@saleor/warehouses/fixtures"; +import * as placeholderImage from "@assets/images/sample-product.jpg"; + +export const orderToFulfill: OrderFulfillData_order = { + __typename: "Order", + id: "T3JkZXI6Mg==", + lines: [ + { + __typename: "OrderLine", + id: "T3JkZXJMaW5lOjQ=", + isShippingRequired: true, + productName: "T-Shirt", + quantity: 3, + quantityFulfilled: 1, + variant: { + __typename: "ProductVariant", + id: "UHJvZHVjdFZhcmlhbnQ6Mjk2", + name: "S", + sku: "62783187", + attributes: [ + { + __typename: "SelectedAttribute", + values: [ + { + __typename: "AttributeValue", + id: "QXR0cmlidXRlVmFsdWU6MzY=", + name: "S" + } + ] + } + ], + stocks: [ + { + __typename: "Stock", + id: "U3RvY2s6NTIy", + warehouse: warehouseList[0], + quantity: 1217, + quantityAllocated: 10 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTIx", + warehouse: warehouseList[1], + quantity: 1217, + quantityAllocated: 20 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTIz", + warehouse: warehouseList[2], + quantity: 1217, + quantityAllocated: 4 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTI0", + warehouse: warehouseList[3], + quantity: 1220, + quantityAllocated: 7 + } + ] + }, + thumbnail: { + __typename: "Image", + url: placeholderImage + } + }, + { + __typename: "OrderLine", + id: "T3JkZXJMaW5lOjU=", + isShippingRequired: true, + productName: "Lemon Juice", + quantity: 4, + quantityFulfilled: 0, + variant: { + __typename: "ProductVariant", + id: "UHJvZHVjdFZhcmlhbnQ6MTgx", + name: "2.5l", + sku: "998323583", + attributes: [ + { + __typename: "SelectedAttribute", + values: [ + { + __typename: "AttributeValue", + id: "QXR0cmlidXRlVmFsdWU6NjE=", + name: "2.5l" + } + ] + } + ], + stocks: [ + { + __typename: "Stock", + id: "U3RvY2s6NTI=", + warehouse: warehouseList[1], + quantity: 760, + quantityAllocated: 2 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTE=", + warehouse: warehouseList[2], + quantity: 760, + quantityAllocated: 33 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTM=", + warehouse: warehouseList[3], + quantity: 760, + quantityAllocated: 4 + } + ] + }, + thumbnail: { + __typename: "Image", + url: placeholderImage + } + }, + { + __typename: "OrderLine", + id: "T3JkZXJMaW5lOjY=", + isShippingRequired: true, + productName: "Orange Juice", + quantity: 3, + quantityFulfilled: 2, + variant: { + __typename: "ProductVariant", + id: "UHJvZHVjdFZhcmlhbnQ6MTgy", + name: "5l", + sku: "998323584", + attributes: [ + { + __typename: "SelectedAttribute", + values: [ + { + __typename: "AttributeValue", + id: "QXR0cmlidXRlVmFsdWU6NjI=", + name: "5l" + } + ] + } + ], + stocks: [ + { + __typename: "Stock", + id: "U3RvY2s6NTc=", + warehouse: warehouseList[0], + quantity: 587, + quantityAllocated: 0 + }, + { + __typename: "Stock", + id: "U3RvY2s6NTY=", + warehouse: warehouseList[2], + quantity: 587, + quantityAllocated: 1 + } + ] + }, + thumbnail: { + __typename: "Image", + url: placeholderImage + } + } + ], + number: "9123" +}; diff --git a/src/orders/components/OrderFulfillPage/index.ts b/src/orders/components/OrderFulfillPage/index.ts new file mode 100644 index 000000000..064e689ba --- /dev/null +++ b/src/orders/components/OrderFulfillPage/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderFulfillPage"; +export { default } from "./OrderFulfillPage"; diff --git a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx index 2a43cd0c7..c52c85845 100644 --- a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx +++ b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx @@ -9,6 +9,7 @@ import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; +import classNames from "classnames"; import CardMenu from "@saleor/components/CardMenu"; import CardTitle from "@saleor/components/CardTitle"; @@ -19,7 +20,7 @@ import StatusLabel from "@saleor/components/StatusLabel"; import TableCellAvatar, { AVATAR_MARGIN } from "@saleor/components/TableCellAvatar"; -import { maybe, renderCollection } from "../../../misc"; +import { maybe, renderCollection, getStringOrPlaceholder } from "../../../misc"; import { FulfillmentStatus } from "../../../types/globalTypes"; import { OrderDetails_order_fulfillments } from "../../types/OrderDetails"; @@ -46,7 +47,15 @@ const useStyles = makeStyles( textAlign: "right", width: 120 }, - + infoLabel: { + display: "inline-block" + }, + infoLabelWithMargin: { + marginBottom: theme.spacing() + }, + infoRow: { + padding: theme.spacing(2, 3) + }, orderNumber: { display: "inline", marginLeft: theme.spacing(1) @@ -68,7 +77,7 @@ interface OrderFulfillmentProps { onTrackingCodeAdd: () => void; } -const numberOfColumns = 3; +const numberOfColumns = 4; const OrderFulfillment: React.FC = props => { const { @@ -107,8 +116,8 @@ const OrderFulfillment: React.FC = props => { ) : intl.formatMessage( { - defaultMessage: "Fulfilled ({quantity})", - description: "section header" + defaultMessage: "Cancelled ({quantity})", + description: "cancelled fulfillment, section header" }, { quantity @@ -135,7 +144,7 @@ const OrderFulfillment: React.FC = props => { menuItems={[ { label: intl.formatMessage({ - defaultMessage: "Cancel shipment", + defaultMessage: "Cancel Fulfillment", description: "button" }), onSelect: onOrderFulfillmentCancel @@ -216,18 +225,47 @@ const OrderFulfillment: React.FC = props => { ))} - {maybe(() => fulfillment.trackingNumber) && ( - - + + + + {getStringOrPlaceholder(fulfillment?.warehouse?.name)} + + ) }} /> - - - )} + + + {fulfillment?.trackingNumber && ( + + {fulfillment.trackingNumber} + + ) + }} + /> + )} + + + {status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && ( diff --git a/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx b/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx index 69eab23d3..653b865b1 100644 --- a/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx +++ b/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx @@ -11,25 +11,29 @@ import { FormattedMessage, useIntl } from "react-intl"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Form from "@saleor/components/Form"; import { buttonMessages } from "@saleor/intl"; import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment"; import FormSpacer from "@saleor/components/FormSpacer"; import getOrderErrorMessage from "@saleor/utils/errors/order"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; -export interface FormData { - restock: boolean; +export interface OrderFulfillmentCancelDialogFormData { + warehouseId: string; } const useStyles = makeStyles( theme => ({ - deleteButton: { - "&:hover": { - backgroundColor: theme.palette.error.main - }, - backgroundColor: theme.palette.error.main, - color: theme.palette.error.contrastText + enableOverflow: { + overflow: "visible" + }, + paragraph: { + marginBottom: theme.spacing(2) + }, + selectCcontainer: { + width: "60%" } }), { name: "OrderFulfillmentCancelDialog" } @@ -39,69 +43,99 @@ export interface OrderFulfillmentCancelDialogProps { confirmButtonState: ConfirmButtonTransitionState; errors: OrderErrorFragment[]; open: boolean; + warehouses: WarehouseFragment[]; onClose(); - onConfirm(data: FormData); + onConfirm(data: OrderFulfillmentCancelDialogFormData); } const OrderFulfillmentCancelDialog: React.FC = props => { - const { confirmButtonState, errors, open, onConfirm, onClose } = props; + const { + confirmButtonState, + errors, + open, + warehouses, + onConfirm, + onClose + } = props; const classes = useStyles(props); const intl = useIntl(); + const [displayValue, setDisplayValue] = React.useState(""); + + const choices = warehouses?.map(warehouse => ({ + label: warehouse.name, + value: warehouse.id + })); return ( - -
- {({ change, data, submit }) => ( - <> - - - - - - - - - {errors.length > 0 && ( - <> - - {errors.map(err => ( - - {getOrderErrorMessage(err, intl)} - - ))} - - )} - - - - + + + {({ change, data: formData, submit }) => { + const handleChange = createSingleAutocompleteSelectHandler( + change, + setDisplayValue, + choices + ); + return ( + <> + - - - - )} + + + + + +
+ +
+ {errors.length > 0 && ( + <> + + {errors.map(err => ( + + {getOrderErrorMessage(err, intl)} + + ))} + + )} +
+ + + + + + + + ); + }}
); diff --git a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx index e9755ed72..54183723b 100644 --- a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx +++ b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx @@ -54,7 +54,7 @@ const useStyles = makeStyles( width: 120 }, quantityInput: { - width: "4rem" + width: 100 }, remainingQuantity: { marginLeft: theme.spacing(), diff --git a/src/orders/components/OrderList/OrderList.tsx b/src/orders/components/OrderList/OrderList.tsx index 45a9f7cc9..b119bf771 100644 --- a/src/orders/components/OrderList/OrderList.tsx +++ b/src/orders/components/OrderList/OrderList.tsx @@ -3,16 +3,15 @@ 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 TableHead from "@material-ui/core/TableHead"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import Checkbox from "@saleor/components/Checkbox"; import { DateTime } from "@saleor/components/Date"; 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 TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; import { maybe, @@ -20,7 +19,7 @@ import { transformOrderStatus, transformPaymentStatus } from "@saleor/misc"; -import { ListActions, ListProps, SortPage } from "@saleor/types"; +import { ListProps, SortPage } from "@saleor/types"; import { OrderListUrlSortField } from "@saleor/orders/urls"; import TableCellHeader from "@saleor/components/TableCellHeader"; import { getArrowDirection } from "@saleor/utils/sort"; @@ -59,14 +58,11 @@ const useStyles = makeStyles( { name: "OrderList" } ); -interface OrderListProps - extends ListProps, - ListActions, - SortPage { +interface OrderListProps extends ListProps, SortPage { orders: OrderList_orders_edges_node[]; } -const numberOfColumns = 7; +const numberOfColumns = 6; export const OrderList: React.FC = props => { const { @@ -79,12 +75,7 @@ export const OrderList: React.FC = props => { onUpdateListSettings, onRowClick, onSort, - isChecked, - selected, - sort, - toggle, - toggleAll, - toolbar + sort } = props; const classes = useStyles(props); @@ -99,14 +90,7 @@ export const OrderList: React.FC = props => { : undefined; return ( - + = props => { {renderCollection( orderList, - order => { - const isSelected = order ? isChecked(order.id) : false; - - return ( - - - toggle(order.id)} - /> - - - {maybe(() => order.number) ? ( - "#" + order.number - ) : ( - - )} - - - {maybe(() => order.created) ? ( - - ) : ( - - )} - - - {maybe(() => order.billingAddress) ? ( - <> - {order.billingAddress.firstName} -   - {order.billingAddress.lastName} - - ) : maybe(() => order.userEmail) !== undefined ? ( - order.userEmail - ) : ( - - )} - - - {maybe(() => order.paymentStatus.status) !== undefined ? ( - order.paymentStatus.status === null ? null : ( - - ) - ) : ( - - )} - - - {maybe(() => order.status) ? ( + order => ( + + + {maybe(() => order.number) ? "#" + order.number : } + + + {maybe(() => order.created) ? ( + + ) : ( + + )} + + + {maybe(() => order.billingAddress) ? ( + <> + {order.billingAddress.firstName} +   + {order.billingAddress.lastName} + + ) : maybe(() => order.userEmail) !== undefined ? ( + order.userEmail + ) : ( + + )} + + + {maybe(() => order.paymentStatus.status) !== undefined ? ( + order.paymentStatus.status === null ? null : ( - ) : ( - - )} - - - {maybe(() => order.total.gross) ? ( - - ) : ( - - )} - - - ); - }, + ) + ) : ( + + )} + + + {maybe(() => order.status) ? ( + + ) : ( + + )} + + + {maybe(() => order.total.gross) ? ( + + ) : ( + + )} + + + ), () => ( diff --git a/src/orders/components/OrderListPage/OrderListPage.tsx b/src/orders/components/OrderListPage/OrderListPage.tsx index 525380a0d..aaf2e7141 100644 --- a/src/orders/components/OrderListPage/OrderListPage.tsx +++ b/src/orders/components/OrderListPage/OrderListPage.tsx @@ -7,12 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; import { sectionNames } from "@saleor/intl"; -import { - FilterPageProps, - ListActions, - PageListProps, - SortPage -} from "@saleor/types"; +import { FilterPageProps, PageListProps, SortPage } from "@saleor/types"; import { OrderListUrlSortField } from "@saleor/orders/urls"; import FilterBar from "@saleor/components/FilterBar"; import { OrderList_orders_edges_node } from "../../types/OrderList"; @@ -25,7 +20,6 @@ import { export interface OrderListPageProps extends PageListProps, - ListActions, FilterPageProps, SortPage { orders: OrderList_orders_edges_node[]; diff --git a/src/orders/containers/OrderOperations.tsx b/src/orders/containers/OrderOperations.tsx index ca6c727d9..c134b21aa 100644 --- a/src/orders/containers/OrderOperations.tsx +++ b/src/orders/containers/OrderOperations.tsx @@ -6,7 +6,6 @@ import { TypedOrderAddNoteMutation, TypedOrderCancelMutation, TypedOrderCaptureMutation, - TypedOrderCreateFulfillmentMutation, TypedOrderDraftCancelMutation, TypedOrderDraftFinalizeMutation, TypedOrderDraftUpdateMutation, @@ -24,10 +23,6 @@ import { import { OrderAddNote, OrderAddNoteVariables } from "../types/OrderAddNote"; import { OrderCancel, OrderCancelVariables } from "../types/OrderCancel"; import { OrderCapture, OrderCaptureVariables } from "../types/OrderCapture"; -import { - OrderCreateFulfillment, - OrderCreateFulfillmentVariables -} from "../types/OrderCreateFulfillment"; import { OrderDraftCancel, OrderDraftCancelVariables @@ -80,10 +75,6 @@ interface OrderOperationsProps { OrderCancel, OrderCancelVariables >; - orderCreateFulfillment: PartialMutationProviderOutput< - OrderCreateFulfillment, - OrderCreateFulfillmentVariables - >; orderFulfillmentCancel: PartialMutationProviderOutput< OrderFulfillmentCancel, OrderFulfillmentCancelVariables @@ -139,7 +130,6 @@ interface OrderOperationsProps { >; }) => React.ReactNode; onOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void; - onOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void; onOrderFulfillmentUpdate: (data: OrderFulfillmentUpdateTracking) => void; onOrderCancel: (data: OrderCancel) => void; onOrderVoid: (data: OrderVoid) => void; @@ -160,7 +150,6 @@ interface OrderOperationsProps { const OrderOperations: React.FC = ({ children, onDraftUpdate, - onOrderFulfillmentCreate, onNoteAdd, onOrderCancel, onOrderLinesAdd, @@ -185,151 +174,140 @@ const OrderOperations: React.FC = ({ {(...paymentCapture) => ( {(...paymentRefund) => ( - - {(...createFulfillment) => ( - - {(...addNote) => ( - - {(...update) => ( - + {(...addNote) => ( + + {(...update) => ( + + {(...updateDraft) => ( + - {(...updateDraft) => ( - ( + - {(...updateShippingMethod) => ( - ( + - {(...deleteOrderLine) => ( - ( + - {(...addOrderLine) => ( - ( + - {(...updateOrderLine) => ( - ( + - {(...cancelFulfillment) => ( - ( + - {( - ...updateTrackingNumber - ) => ( - ( + {( - ...finalizeDraft + ...cancelDraft ) => ( - {( - ...cancelDraft - ) => ( - - {( + ...markAsPaid + ) => + children({ + orderAddNote: getMutationProviderData( + ...addNote + ), + orderCancel: getMutationProviderData( + ...orderCancel + ), + orderDraftCancel: getMutationProviderData( + ...cancelDraft + ), + orderDraftFinalize: getMutationProviderData( + ...finalizeDraft + ), + orderDraftUpdate: getMutationProviderData( + ...updateDraft + ), + orderFulfillmentCancel: getMutationProviderData( + ...cancelFulfillment + ), + orderFulfillmentUpdateTracking: getMutationProviderData( + ...updateTrackingNumber + ), + orderLineDelete: getMutationProviderData( + ...deleteOrderLine + ), + orderLineUpdate: getMutationProviderData( + ...updateOrderLine + ), + orderLinesAdd: getMutationProviderData( + ...addOrderLine + ), + orderPaymentCapture: getMutationProviderData( + ...paymentCapture + ), + orderPaymentMarkAsPaid: getMutationProviderData( ...markAsPaid - ) => - children({ - orderAddNote: getMutationProviderData( - ...addNote - ), - orderCancel: getMutationProviderData( - ...orderCancel - ), - orderCreateFulfillment: getMutationProviderData( - ...createFulfillment - ), - orderDraftCancel: getMutationProviderData( - ...cancelDraft - ), - orderDraftFinalize: getMutationProviderData( - ...finalizeDraft - ), - orderDraftUpdate: getMutationProviderData( - ...updateDraft - ), - orderFulfillmentCancel: getMutationProviderData( - ...cancelFulfillment - ), - orderFulfillmentUpdateTracking: getMutationProviderData( - ...updateTrackingNumber - ), - orderLineDelete: getMutationProviderData( - ...deleteOrderLine - ), - orderLineUpdate: getMutationProviderData( - ...updateOrderLine - ), - orderLinesAdd: getMutationProviderData( - ...addOrderLine - ), - orderPaymentCapture: getMutationProviderData( - ...paymentCapture - ), - orderPaymentMarkAsPaid: getMutationProviderData( - ...markAsPaid - ), - orderPaymentRefund: getMutationProviderData( - ...paymentRefund - ), - orderShippingMethodUpdate: getMutationProviderData( - ...updateShippingMethod - ), - orderUpdate: getMutationProviderData( - ...update - ), - orderVoid: getMutationProviderData( - ...orderVoid - ) - }) - } - - )} - + ), + orderPaymentRefund: getMutationProviderData( + ...paymentRefund + ), + orderShippingMethodUpdate: getMutationProviderData( + ...updateShippingMethod + ), + orderUpdate: getMutationProviderData( + ...update + ), + orderVoid: getMutationProviderData( + ...orderVoid + ) + }) + } + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} )} diff --git a/src/orders/fixtures.ts b/src/orders/fixtures.ts index a07af34ac..41279fd34 100644 --- a/src/orders/fixtures.ts +++ b/src/orders/fixtures.ts @@ -1,5 +1,6 @@ import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers"; import { MessageDescriptor } from "react-intl"; +import { warehouseList } from "@saleor/warehouses/fixtures"; import { transformOrderStatus, transformPaymentStatus } from "../misc"; import { FulfillmentStatus, @@ -865,7 +866,8 @@ export const order = (placeholder: string): OrderDetails_order => ({ } ], status: FulfillmentStatus.FULFILLED, - trackingNumber: "" + trackingNumber: "", + warehouse: warehouseList[1] }, { __typename: "Fulfillment", @@ -905,7 +907,8 @@ export const order = (placeholder: string): OrderDetails_order => ({ } ], status: FulfillmentStatus.FULFILLED, - trackingNumber: "" + trackingNumber: "01nn12399su12nndfsy", + warehouse: warehouseList[0] } ], id: "T3JkZXI6OQ==", diff --git a/src/orders/index.tsx b/src/orders/index.tsx index 608cba793..c8db1246e 100644 --- a/src/orders/index.tsx +++ b/src/orders/index.tsx @@ -14,9 +14,11 @@ import { orderPath, OrderUrlQueryParams, OrderDraftListUrlSortField, - OrderListUrlSortField + OrderListUrlSortField, + orderFulfillPath } from "./urls"; import OrderDetailsComponent from "./views/OrderDetails"; +import OrderFulfillComponent from "./views/OrderFulfill"; import OrderDraftListComponent from "./views/OrderDraftList"; import OrderListComponent from "./views/OrderList"; @@ -57,6 +59,10 @@ const OrderDetails: React.FC> = ({ ); }; +const OrderFulfill: React.FC> = ({ match }) => ( + +); + const Component = () => { const intl = useIntl(); @@ -66,6 +72,7 @@ const Component = () => { + diff --git a/src/orders/mutations.ts b/src/orders/mutations.ts index 3ee96f82e..d94c3f8ff 100644 --- a/src/orders/mutations.ts +++ b/src/orders/mutations.ts @@ -8,20 +8,13 @@ import { fragmentOrderEvent } from "./queries"; import { OrderAddNote, OrderAddNoteVariables } from "./types/OrderAddNote"; -import { - OrderBulkCancel, - OrderBulkCancelVariables -} from "./types/OrderBulkCancel"; import { OrderCancel, OrderCancelVariables } from "./types/OrderCancel"; import { OrderCapture, OrderCaptureVariables } from "./types/OrderCapture"; -import { - OrderCreateFulfillment, - OrderCreateFulfillmentVariables -} from "./types/OrderCreateFulfillment"; import { OrderDraftBulkCancel, OrderDraftBulkCancelVariables } from "./types/OrderDraftBulkCancel"; +import { FulfillOrder, FulfillOrderVariables } from "./types/FulfillOrder"; import { OrderDraftCancel, OrderDraftCancelVariables @@ -74,8 +67,8 @@ export const orderErrorFragment = gql` const orderCancelMutation = gql` ${fragmentOrderDetails} ${orderErrorFragment} - mutation OrderCancel($id: ID!, $restock: Boolean!) { - orderCancel(id: $id, restock: $restock) { + mutation OrderCancel($id: ID!) { + orderCancel(id: $id) { errors: orderErrors { ...OrderErrorFragment } @@ -90,21 +83,6 @@ export const TypedOrderCancelMutation = TypedMutation< OrderCancelVariables >(orderCancelMutation); -const orderBulkCancelMutation = gql` - ${orderErrorFragment} - mutation OrderBulkCancel($ids: [ID]!, $restock: Boolean!) { - orderBulkCancel(ids: $ids, restock: $restock) { - errors: orderErrors { - ...OrderErrorFragment - } - } - } -`; -export const TypedOrderBulkCancelMutation = TypedMutation< - OrderBulkCancel, - OrderBulkCancelVariables ->(orderBulkCancelMutation); - const orderDraftCancelMutation = gql` ${fragmentOrderDetails} ${orderErrorFragment} @@ -234,28 +212,6 @@ export const TypedOrderCaptureMutation = TypedMutation< OrderCaptureVariables >(orderCaptureMutation); -const orderCreateFulfillmentMutation = gql` - ${fragmentOrderDetails} - ${orderErrorFragment} - mutation OrderCreateFulfillment( - $order: ID! - $input: FulfillmentCreateInput! - ) { - orderFulfillmentCreate(order: $order, input: $input) { - errors: orderErrors { - ...OrderErrorFragment - } - order { - ...OrderDetailsFragment - } - } - } -`; -export const TypedOrderCreateFulfillmentMutation = TypedMutation< - OrderCreateFulfillment, - OrderCreateFulfillmentVariables ->(orderCreateFulfillmentMutation); - const orderFulfillmentUpdateTrackingMutation = gql` ${fragmentOrderDetails} ${orderErrorFragment} @@ -477,3 +433,24 @@ export const TypedOrderLineUpdateMutation = TypedMutation< OrderLineUpdate, OrderLineUpdateVariables >(orderLineUpdateMutation); + +const fulfillOrder = gql` + ${fragmentOrderDetails} + ${orderErrorFragment} + mutation FulfillOrder($orderId: ID!, $input: OrderFulfillInput!) { + orderFulfill(order: $orderId, input: $input) { + errors: orderErrors { + ...OrderErrorFragment + warehouse + orderLine + } + order { + ...OrderDetailsFragment + } + } + } +`; +export const useOrderFulfill = makeMutation< + FulfillOrder, + FulfillOrderVariables +>(fulfillOrder); diff --git a/src/orders/queries.ts b/src/orders/queries.ts index 896a2f665..2c49a87b3 100644 --- a/src/orders/queries.ts +++ b/src/orders/queries.ts @@ -13,6 +13,10 @@ import { SearchOrderVariant as SearchOrderVariantType, SearchOrderVariantVariables } from "./types/SearchOrderVariant"; +import { + OrderFulfillData, + OrderFulfillDataVariables +} from "./types/OrderFulfillData"; export const fragmentOrderEvent = gql` fragment OrderEventFragment on OrderEvent { @@ -73,11 +77,32 @@ export const fragmentOrderLine = gql` } } `; +export const fulfillmentFragment = gql` + ${fragmentOrderLine} + fragment FulfillmentFragment on Fulfillment { + id + lines { + id + quantity + orderLine { + ...OrderLineFragment + } + } + fulfillmentOrder + status + trackingNumber + warehouse { + id + name + } + } +`; export const fragmentOrderDetails = gql` ${fragmentAddress} ${fragmentOrderEvent} ${fragmentOrderLine} + ${fulfillmentFragment} fragment OrderDetailsFragment on Order { id billingAddress { @@ -90,17 +115,7 @@ export const fragmentOrderDetails = gql` ...OrderEventFragment } fulfillments { - id - lines { - id - quantity - orderLine { - ...OrderLineFragment - } - } - fulfillmentOrder - status - trackingNumber + ...FulfillmentFragment } lines { ...OrderLineFragment @@ -327,3 +342,45 @@ export const useOrderVariantSearch = makeTopLevelSearch< SearchOrderVariantType, SearchOrderVariantVariables >(searchOrderVariant); + +const orderFulfillData = gql` + query OrderFulfillData($orderId: ID!) { + order(id: $orderId) { + id + lines { + id + isShippingRequired + productName + quantity + quantityFulfilled + variant { + id + name + sku + attributes { + values { + id + name + } + } + stocks { + id + warehouse { + id + } + quantity + quantityAllocated + } + } + thumbnail(size: 64) { + url + } + } + number + } + } +`; +export const useOrderFulfillData = makeQuery< + OrderFulfillData, + OrderFulfillDataVariables +>(orderFulfillData); diff --git a/src/orders/types/FulfillOrder.ts b/src/orders/types/FulfillOrder.ts new file mode 100644 index 000000000..786ffb08c --- /dev/null +++ b/src/orders/types/FulfillOrder.ts @@ -0,0 +1,291 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { OrderFulfillInput, OrderErrorCode, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: FulfillOrder +// ==================================================== + +export interface FulfillOrder_orderFulfill_errors { + __typename: "OrderError"; + code: OrderErrorCode; + field: string | null; + warehouse: string | null; + orderLine: string | null; +} + +export interface FulfillOrder_orderFulfill_order_billingAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface FulfillOrder_orderFulfill_order_billingAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: FulfillOrder_orderFulfill_order_billingAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface FulfillOrder_orderFulfill_order_events_user { + __typename: "User"; + id: string; + email: string; +} + +export interface FulfillOrder_orderFulfill_order_events { + __typename: "OrderEvent"; + id: string; + amount: number | null; + date: any | null; + email: string | null; + emailType: OrderEventsEmailsEnum | null; + message: string | null; + quantity: number | null; + type: OrderEventsEnum | null; + user: FulfillOrder_orderFulfill_order_events_user | null; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_net { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice { + __typename: "TaxedMoney"; + gross: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_gross; + net: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_net; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_thumbnail { + __typename: "Image"; + url: string; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine { + __typename: "OrderLine"; + id: string; + isShippingRequired: boolean; + productName: string; + productSku: string; + quantity: number; + quantityFulfilled: number; + unitPrice: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice | null; + thumbnail: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_thumbnail | null; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_lines { + __typename: "FulfillmentLine"; + id: string; + quantity: number; + orderLine: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine | null; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface FulfillOrder_orderFulfill_order_fulfillments { + __typename: "Fulfillment"; + id: string; + lines: (FulfillOrder_orderFulfill_order_fulfillments_lines | null)[] | null; + fulfillmentOrder: number; + status: FulfillmentStatus; + trackingNumber: string; + warehouse: FulfillOrder_orderFulfill_order_fulfillments_warehouse | null; +} + +export interface FulfillOrder_orderFulfill_order_lines_unitPrice_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_lines_unitPrice_net { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_lines_unitPrice { + __typename: "TaxedMoney"; + gross: FulfillOrder_orderFulfill_order_lines_unitPrice_gross; + net: FulfillOrder_orderFulfill_order_lines_unitPrice_net; +} + +export interface FulfillOrder_orderFulfill_order_lines_thumbnail { + __typename: "Image"; + url: string; +} + +export interface FulfillOrder_orderFulfill_order_lines { + __typename: "OrderLine"; + id: string; + isShippingRequired: boolean; + productName: string; + productSku: string; + quantity: number; + quantityFulfilled: number; + unitPrice: FulfillOrder_orderFulfill_order_lines_unitPrice | null; + thumbnail: FulfillOrder_orderFulfill_order_lines_thumbnail | null; +} + +export interface FulfillOrder_orderFulfill_order_shippingAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface FulfillOrder_orderFulfill_order_shippingAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: FulfillOrder_orderFulfill_order_shippingAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface FulfillOrder_orderFulfill_order_shippingMethod { + __typename: "ShippingMethod"; + id: string; +} + +export interface FulfillOrder_orderFulfill_order_shippingPrice_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_shippingPrice { + __typename: "TaxedMoney"; + gross: FulfillOrder_orderFulfill_order_shippingPrice_gross; +} + +export interface FulfillOrder_orderFulfill_order_subtotal_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_subtotal { + __typename: "TaxedMoney"; + gross: FulfillOrder_orderFulfill_order_subtotal_gross; +} + +export interface FulfillOrder_orderFulfill_order_total_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_total_tax { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_total { + __typename: "TaxedMoney"; + gross: FulfillOrder_orderFulfill_order_total_gross; + tax: FulfillOrder_orderFulfill_order_total_tax; +} + +export interface FulfillOrder_orderFulfill_order_totalAuthorized { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_totalCaptured { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_user { + __typename: "User"; + id: string; + email: string; +} + +export interface FulfillOrder_orderFulfill_order_availableShippingMethods_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillOrder_orderFulfill_order_availableShippingMethods { + __typename: "ShippingMethod"; + id: string; + name: string; + price: FulfillOrder_orderFulfill_order_availableShippingMethods_price | null; +} + +export interface FulfillOrder_orderFulfill_order { + __typename: "Order"; + id: string; + billingAddress: FulfillOrder_orderFulfill_order_billingAddress | null; + canFinalize: boolean; + created: any; + customerNote: string; + events: (FulfillOrder_orderFulfill_order_events | null)[] | null; + fulfillments: (FulfillOrder_orderFulfill_order_fulfillments | null)[]; + lines: (FulfillOrder_orderFulfill_order_lines | null)[]; + number: string | null; + paymentStatus: PaymentChargeStatusEnum | null; + shippingAddress: FulfillOrder_orderFulfill_order_shippingAddress | null; + shippingMethod: FulfillOrder_orderFulfill_order_shippingMethod | null; + shippingMethodName: string | null; + shippingPrice: FulfillOrder_orderFulfill_order_shippingPrice | null; + status: OrderStatus; + subtotal: FulfillOrder_orderFulfill_order_subtotal | null; + total: FulfillOrder_orderFulfill_order_total | null; + actions: (OrderAction | null)[]; + totalAuthorized: FulfillOrder_orderFulfill_order_totalAuthorized | null; + totalCaptured: FulfillOrder_orderFulfill_order_totalCaptured | null; + user: FulfillOrder_orderFulfill_order_user | null; + userEmail: string | null; + availableShippingMethods: (FulfillOrder_orderFulfill_order_availableShippingMethods | null)[] | null; +} + +export interface FulfillOrder_orderFulfill { + __typename: "OrderFulfill"; + errors: FulfillOrder_orderFulfill_errors[]; + order: FulfillOrder_orderFulfill_order | null; +} + +export interface FulfillOrder { + orderFulfill: FulfillOrder_orderFulfill | null; +} + +export interface FulfillOrderVariables { + orderId: string; + input: OrderFulfillInput; +} diff --git a/src/orders/types/FulfillmentFragment.ts b/src/orders/types/FulfillmentFragment.ts new file mode 100644 index 000000000..e8ac634b5 --- /dev/null +++ b/src/orders/types/FulfillmentFragment.ts @@ -0,0 +1,67 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { FulfillmentStatus } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: FulfillmentFragment +// ==================================================== + +export interface FulfillmentFragment_lines_orderLine_unitPrice_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillmentFragment_lines_orderLine_unitPrice_net { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface FulfillmentFragment_lines_orderLine_unitPrice { + __typename: "TaxedMoney"; + gross: FulfillmentFragment_lines_orderLine_unitPrice_gross; + net: FulfillmentFragment_lines_orderLine_unitPrice_net; +} + +export interface FulfillmentFragment_lines_orderLine_thumbnail { + __typename: "Image"; + url: string; +} + +export interface FulfillmentFragment_lines_orderLine { + __typename: "OrderLine"; + id: string; + isShippingRequired: boolean; + productName: string; + productSku: string; + quantity: number; + quantityFulfilled: number; + unitPrice: FulfillmentFragment_lines_orderLine_unitPrice | null; + thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null; +} + +export interface FulfillmentFragment_lines { + __typename: "FulfillmentLine"; + id: string; + quantity: number; + orderLine: FulfillmentFragment_lines_orderLine | null; +} + +export interface FulfillmentFragment_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface FulfillmentFragment { + __typename: "Fulfillment"; + id: string; + lines: (FulfillmentFragment_lines | null)[] | null; + fulfillmentOrder: number; + status: FulfillmentStatus; + trackingNumber: string; + warehouse: FulfillmentFragment_warehouse | null; +} diff --git a/src/orders/types/OrderBulkCancel.ts b/src/orders/types/OrderBulkCancel.ts deleted file mode 100644 index c61e82ac7..000000000 --- a/src/orders/types/OrderBulkCancel.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// This file was automatically generated and should not be edited. - -import { OrderErrorCode } from "./../../types/globalTypes"; - -// ==================================================== -// GraphQL mutation operation: OrderBulkCancel -// ==================================================== - -export interface OrderBulkCancel_orderBulkCancel_errors { - __typename: "OrderError"; - code: OrderErrorCode; - field: string | null; -} - -export interface OrderBulkCancel_orderBulkCancel { - __typename: "OrderBulkCancel"; - errors: OrderBulkCancel_orderBulkCancel_errors[]; -} - -export interface OrderBulkCancel { - orderBulkCancel: OrderBulkCancel_orderBulkCancel | null; -} - -export interface OrderBulkCancelVariables { - ids: (string | null)[]; - restock: boolean; -} diff --git a/src/orders/types/OrderCancel.ts b/src/orders/types/OrderCancel.ts index 19db67e37..7258dbdb0 100644 --- a/src/orders/types/OrderCancel.ts +++ b/src/orders/types/OrderCancel.ts @@ -97,6 +97,12 @@ export interface OrderCancel_orderCancel_order_fulfillments_lines { orderLine: OrderCancel_orderCancel_order_fulfillments_lines_orderLine | null; } +export interface OrderCancel_orderCancel_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderCancel_orderCancel_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderCancel_orderCancel_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderCancel_orderCancel_order_fulfillments_warehouse | null; } export interface OrderCancel_orderCancel_order_lines_unitPrice_gross { @@ -278,5 +285,4 @@ export interface OrderCancel { export interface OrderCancelVariables { id: string; - restock: boolean; } diff --git a/src/orders/types/OrderCapture.ts b/src/orders/types/OrderCapture.ts index 502223d9d..5c3abf2ee 100644 --- a/src/orders/types/OrderCapture.ts +++ b/src/orders/types/OrderCapture.ts @@ -97,6 +97,12 @@ export interface OrderCapture_orderCapture_order_fulfillments_lines { orderLine: OrderCapture_orderCapture_order_fulfillments_lines_orderLine | null; } +export interface OrderCapture_orderCapture_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderCapture_orderCapture_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderCapture_orderCapture_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderCapture_orderCapture_order_fulfillments_warehouse | null; } export interface OrderCapture_orderCapture_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderCreateFulfillment.ts b/src/orders/types/OrderCreateFulfillment.ts deleted file mode 100644 index edc4d54f1..000000000 --- a/src/orders/types/OrderCreateFulfillment.ts +++ /dev/null @@ -1,282 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// This file was automatically generated and should not be edited. - -import { FulfillmentCreateInput, OrderErrorCode, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction } from "./../../types/globalTypes"; - -// ==================================================== -// GraphQL mutation operation: OrderCreateFulfillment -// ==================================================== - -export interface OrderCreateFulfillment_orderFulfillmentCreate_errors { - __typename: "OrderError"; - code: OrderErrorCode; - field: string | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress_country { - __typename: "CountryDisplay"; - code: string; - country: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress { - __typename: "Address"; - city: string; - cityArea: string; - companyName: string; - country: OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress_country; - countryArea: string; - firstName: string; - id: string; - lastName: string; - phone: string | null; - postalCode: string; - streetAddress1: string; - streetAddress2: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_events_user { - __typename: "User"; - id: string; - email: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_events { - __typename: "OrderEvent"; - id: string; - amount: number | null; - date: any | null; - email: string | null; - emailType: OrderEventsEmailsEnum | null; - message: string | null; - quantity: number | null; - type: OrderEventsEnum | null; - user: OrderCreateFulfillment_orderFulfillmentCreate_order_events_user | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_net { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice { - __typename: "TaxedMoney"; - gross: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_gross; - net: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_net; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_thumbnail { - __typename: "Image"; - url: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine { - __typename: "OrderLine"; - id: string; - isShippingRequired: boolean; - productName: string; - productSku: string; - quantity: number; - quantityFulfilled: number; - unitPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice | null; - thumbnail: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_thumbnail | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines { - __typename: "FulfillmentLine"; - id: string; - quantity: number; - orderLine: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments { - __typename: "Fulfillment"; - id: string; - lines: (OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines | null)[] | null; - fulfillmentOrder: number; - status: FulfillmentStatus; - trackingNumber: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_net { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice { - __typename: "TaxedMoney"; - gross: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_gross; - net: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_net; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_thumbnail { - __typename: "Image"; - url: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines { - __typename: "OrderLine"; - id: string; - isShippingRequired: boolean; - productName: string; - productSku: string; - quantity: number; - quantityFulfilled: number; - unitPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice | null; - thumbnail: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_thumbnail | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress_country { - __typename: "CountryDisplay"; - code: string; - country: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress { - __typename: "Address"; - city: string; - cityArea: string; - companyName: string; - country: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress_country; - countryArea: string; - firstName: string; - id: string; - lastName: string; - phone: string | null; - postalCode: string; - streetAddress1: string; - streetAddress2: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingMethod { - __typename: "ShippingMethod"; - id: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice { - __typename: "TaxedMoney"; - gross: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice_gross; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal { - __typename: "TaxedMoney"; - gross: OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal_gross; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total_gross { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total_tax { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total { - __typename: "TaxedMoney"; - gross: OrderCreateFulfillment_orderFulfillmentCreate_order_total_gross; - tax: OrderCreateFulfillment_orderFulfillmentCreate_order_total_tax; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_totalAuthorized { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_totalCaptured { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_user { - __typename: "User"; - id: string; - email: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods_price { - __typename: "Money"; - amount: number; - currency: string; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods { - __typename: "ShippingMethod"; - id: string; - name: string; - price: OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods_price | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate_order { - __typename: "Order"; - id: string; - billingAddress: OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress | null; - canFinalize: boolean; - created: any; - customerNote: string; - events: (OrderCreateFulfillment_orderFulfillmentCreate_order_events | null)[] | null; - fulfillments: (OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments | null)[]; - lines: (OrderCreateFulfillment_orderFulfillmentCreate_order_lines | null)[]; - number: string | null; - paymentStatus: PaymentChargeStatusEnum | null; - shippingAddress: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress | null; - shippingMethod: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingMethod | null; - shippingMethodName: string | null; - shippingPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice | null; - status: OrderStatus; - subtotal: OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal | null; - total: OrderCreateFulfillment_orderFulfillmentCreate_order_total | null; - actions: (OrderAction | null)[]; - totalAuthorized: OrderCreateFulfillment_orderFulfillmentCreate_order_totalAuthorized | null; - totalCaptured: OrderCreateFulfillment_orderFulfillmentCreate_order_totalCaptured | null; - user: OrderCreateFulfillment_orderFulfillmentCreate_order_user | null; - userEmail: string | null; - availableShippingMethods: (OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods | null)[] | null; -} - -export interface OrderCreateFulfillment_orderFulfillmentCreate { - __typename: "FulfillmentCreate"; - errors: OrderCreateFulfillment_orderFulfillmentCreate_errors[]; - order: OrderCreateFulfillment_orderFulfillmentCreate_order | null; -} - -export interface OrderCreateFulfillment { - orderFulfillmentCreate: OrderCreateFulfillment_orderFulfillmentCreate | null; -} - -export interface OrderCreateFulfillmentVariables { - order: string; - input: FulfillmentCreateInput; -} diff --git a/src/orders/types/OrderDetails.ts b/src/orders/types/OrderDetails.ts index a668e4563..4ec01cdca 100644 --- a/src/orders/types/OrderDetails.ts +++ b/src/orders/types/OrderDetails.ts @@ -91,6 +91,12 @@ export interface OrderDetails_order_fulfillments_lines { orderLine: OrderDetails_order_fulfillments_lines_orderLine | null; } +export interface OrderDetails_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderDetails_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -98,6 +104,7 @@ export interface OrderDetails_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderDetails_order_fulfillments_warehouse | null; } export interface OrderDetails_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderDetailsFragment.ts b/src/orders/types/OrderDetailsFragment.ts index fc2f5d3ba..e01d64bbb 100644 --- a/src/orders/types/OrderDetailsFragment.ts +++ b/src/orders/types/OrderDetailsFragment.ts @@ -91,6 +91,12 @@ export interface OrderDetailsFragment_fulfillments_lines { orderLine: OrderDetailsFragment_fulfillments_lines_orderLine | null; } +export interface OrderDetailsFragment_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderDetailsFragment_fulfillments { __typename: "Fulfillment"; id: string; @@ -98,6 +104,7 @@ export interface OrderDetailsFragment_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderDetailsFragment_fulfillments_warehouse | null; } export interface OrderDetailsFragment_lines_unitPrice_gross { diff --git a/src/orders/types/OrderDraftCancel.ts b/src/orders/types/OrderDraftCancel.ts index 75c50fc0d..0d5f737d9 100644 --- a/src/orders/types/OrderDraftCancel.ts +++ b/src/orders/types/OrderDraftCancel.ts @@ -97,6 +97,12 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines { orderLine: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine | null; } +export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderDraftCancel_draftOrderDelete_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse | null; } export interface OrderDraftCancel_draftOrderDelete_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderDraftFinalize.ts b/src/orders/types/OrderDraftFinalize.ts index e4f7c715b..8b1ea1068 100644 --- a/src/orders/types/OrderDraftFinalize.ts +++ b/src/orders/types/OrderDraftFinalize.ts @@ -97,6 +97,12 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines orderLine: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine | null; } +export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse | null; } export interface OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderDraftUpdate.ts b/src/orders/types/OrderDraftUpdate.ts index 6a54380da..77f764948 100644 --- a/src/orders/types/OrderDraftUpdate.ts +++ b/src/orders/types/OrderDraftUpdate.ts @@ -97,6 +97,12 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines { orderLine: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine | null; } +export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse | null; } export interface OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderFulfillData.ts b/src/orders/types/OrderFulfillData.ts new file mode 100644 index 000000000..1bc8412b8 --- /dev/null +++ b/src/orders/types/OrderFulfillData.ts @@ -0,0 +1,71 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: OrderFulfillData +// ==================================================== + +export interface OrderFulfillData_order_lines_variant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; +} + +export interface OrderFulfillData_order_lines_variant_attributes { + __typename: "SelectedAttribute"; + values: (OrderFulfillData_order_lines_variant_attributes_values | null)[]; +} + +export interface OrderFulfillData_order_lines_variant_stocks_warehouse { + __typename: "Warehouse"; + id: string; +} + +export interface OrderFulfillData_order_lines_variant_stocks { + __typename: "Stock"; + id: string; + warehouse: OrderFulfillData_order_lines_variant_stocks_warehouse; + quantity: number; + quantityAllocated: number; +} + +export interface OrderFulfillData_order_lines_variant { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + attributes: OrderFulfillData_order_lines_variant_attributes[]; + stocks: (OrderFulfillData_order_lines_variant_stocks | null)[] | null; +} + +export interface OrderFulfillData_order_lines_thumbnail { + __typename: "Image"; + url: string; +} + +export interface OrderFulfillData_order_lines { + __typename: "OrderLine"; + id: string; + isShippingRequired: boolean; + productName: string; + quantity: number; + quantityFulfilled: number; + variant: OrderFulfillData_order_lines_variant | null; + thumbnail: OrderFulfillData_order_lines_thumbnail | null; +} + +export interface OrderFulfillData_order { + __typename: "Order"; + id: string; + lines: (OrderFulfillData_order_lines | null)[]; + number: string | null; +} + +export interface OrderFulfillData { + order: OrderFulfillData_order | null; +} + +export interface OrderFulfillDataVariables { + orderId: string; +} diff --git a/src/orders/types/OrderFulfillmentCancel.ts b/src/orders/types/OrderFulfillmentCancel.ts index 667f4a9f8..014908b9e 100644 --- a/src/orders/types/OrderFulfillmentCancel.ts +++ b/src/orders/types/OrderFulfillmentCancel.ts @@ -97,6 +97,12 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment orderLine: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine | null; } +export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse | null; } export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderFulfillmentUpdateTracking.ts b/src/orders/types/OrderFulfillmentUpdateTracking.ts index 83b44ed82..0a0e5cca1 100644 --- a/src/orders/types/OrderFulfillmentUpdateTracking.ts +++ b/src/orders/types/OrderFulfillmentUpdateTracking.ts @@ -97,6 +97,12 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o orderLine: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine | null; } +export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse | null; } export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderLineDelete.ts b/src/orders/types/OrderLineDelete.ts index 96c7b2548..8205439ea 100644 --- a/src/orders/types/OrderLineDelete.ts +++ b/src/orders/types/OrderLineDelete.ts @@ -97,6 +97,12 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines { orderLine: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine | null; } +export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderLineDelete_draftOrderLineDelete_order_fulfillments_warehouse | null; } export interface OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderLineUpdate.ts b/src/orders/types/OrderLineUpdate.ts index 3651a5cce..948b0beac 100644 --- a/src/orders/types/OrderLineUpdate.ts +++ b/src/orders/types/OrderLineUpdate.ts @@ -97,6 +97,12 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines { orderLine: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine | null; } +export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_warehouse | null; } export interface OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderLinesAdd.ts b/src/orders/types/OrderLinesAdd.ts index 20ecbd7fa..b2cd429e8 100644 --- a/src/orders/types/OrderLinesAdd.ts +++ b/src/orders/types/OrderLinesAdd.ts @@ -97,6 +97,12 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines { orderLine: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine | null; } +export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_warehouse | null; } export interface OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderMarkAsPaid.ts b/src/orders/types/OrderMarkAsPaid.ts index 4047ef0b9..898cf9a99 100644 --- a/src/orders/types/OrderMarkAsPaid.ts +++ b/src/orders/types/OrderMarkAsPaid.ts @@ -97,6 +97,12 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines { orderLine: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine | null; } +export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse | null; } export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderRefund.ts b/src/orders/types/OrderRefund.ts index 29eee9ef3..6f764f9e6 100644 --- a/src/orders/types/OrderRefund.ts +++ b/src/orders/types/OrderRefund.ts @@ -97,6 +97,12 @@ export interface OrderRefund_orderRefund_order_fulfillments_lines { orderLine: OrderRefund_orderRefund_order_fulfillments_lines_orderLine | null; } +export interface OrderRefund_orderRefund_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderRefund_orderRefund_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderRefund_orderRefund_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderRefund_orderRefund_order_fulfillments_warehouse | null; } export interface OrderRefund_orderRefund_order_lines_unitPrice_gross { diff --git a/src/orders/types/OrderVoid.ts b/src/orders/types/OrderVoid.ts index 3ee9caec1..880598c78 100644 --- a/src/orders/types/OrderVoid.ts +++ b/src/orders/types/OrderVoid.ts @@ -97,6 +97,12 @@ export interface OrderVoid_orderVoid_order_fulfillments_lines { orderLine: OrderVoid_orderVoid_order_fulfillments_lines_orderLine | null; } +export interface OrderVoid_orderVoid_order_fulfillments_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface OrderVoid_orderVoid_order_fulfillments { __typename: "Fulfillment"; id: string; @@ -104,6 +110,7 @@ export interface OrderVoid_orderVoid_order_fulfillments { fulfillmentOrder: number; status: FulfillmentStatus; trackingNumber: string; + warehouse: OrderVoid_orderVoid_order_fulfillments_warehouse | null; } export interface OrderVoid_orderVoid_order_lines_unitPrice_gross { diff --git a/src/orders/urls.ts b/src/orders/urls.ts index c8e7e3af7..d500f7f12 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -97,10 +97,14 @@ export type OrderUrlDialog = | "edit-shipping" | "edit-shipping-address" | "finalize" - | "fulfill" | "mark-paid" | "refund" | "void"; export type OrderUrlQueryParams = Dialog & SingleAction; export const orderUrl = (id: string, params?: OrderUrlQueryParams) => orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params); + +export const orderFulfillPath = (id: string) => + urlJoin(orderPath(id), "fulfill"); +export const orderFulfillUrl = (id: string) => + orderFulfillPath(encodeURIComponent(id)); diff --git a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx index c9b5b9f00..a2162928f 100644 --- a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx +++ b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx @@ -7,7 +7,6 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl import { OrderAddNote } from "../../types/OrderAddNote"; import { OrderCancel } from "../../types/OrderCancel"; import { OrderCapture } from "../../types/OrderCapture"; -import { OrderCreateFulfillment } from "../../types/OrderCreateFulfillment"; import { OrderDraftCancel } from "../../types/OrderDraftCancel"; import { OrderDraftFinalize } from "../../types/OrderDraftFinalize"; import { OrderDraftUpdate } from "../../types/OrderDraftUpdate"; @@ -31,7 +30,6 @@ interface OrderDetailsMessages { handleNoteAdd: (data: OrderAddNote) => void; handleOrderCancel: (data: OrderCancel) => void; handleOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void; - handleOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void; handleOrderFulfillmentUpdate: ( data: OrderFulfillmentUpdateTracking ) => void; @@ -86,17 +84,6 @@ export const OrderDetailsMessages: React.FC = ({ closeModal(); } }; - const handleOrderFulfillmentCreate = (data: OrderCreateFulfillment) => { - const errs = data.orderFulfillmentCreate?.errors; - if (errs.length === 0) { - pushMessage({ - text: intl.formatMessage({ - defaultMessage: "Items successfully fulfilled" - }) - }); - closeModal(); - } - }; const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => { const errs = data.orderMarkAsPaid?.errors; if (errs.length === 0) { @@ -256,7 +243,6 @@ export const OrderDetailsMessages: React.FC = ({ handleNoteAdd, handleOrderCancel, handleOrderFulfillmentCancel, - handleOrderFulfillmentCreate, handleOrderFulfillmentUpdate, handleOrderLineDelete, handleOrderLineUpdate, diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index e722b5ac7..50c369b6c 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -8,6 +8,8 @@ import useUser from "@saleor/hooks/useUser"; import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import NotFoundPage from "@saleor/components/NotFoundPage"; +import { useWarehouseList } from "@saleor/warehouses/queries"; +import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import { customerUrl } from "../../../customers/urls"; import { maybe, @@ -15,7 +17,7 @@ import { getStringOrPlaceholder } from "../../../misc"; import { productUrl } from "../../../products/urls"; -import { OrderStatus } from "../../../types/globalTypes"; +import { OrderStatus, FulfillmentStatus } from "../../../types/globalTypes"; import OrderAddressEditDialog from "../../components/OrderAddressEditDialog"; import OrderCancelDialog from "../../components/OrderCancelDialog"; import OrderDetailsPage from "../../components/OrderDetailsPage"; @@ -25,7 +27,6 @@ import OrderDraftFinalizeDialog, { } from "../../components/OrderDraftFinalizeDialog"; import OrderDraftPage from "../../components/OrderDraftPage"; import OrderFulfillmentCancelDialog from "../../components/OrderFulfillmentCancelDialog"; -import OrderFulfillmentDialog from "../../components/OrderFulfillmentDialog"; import OrderFulfillmentTrackingDialog from "../../components/OrderFulfillmentTrackingDialog"; import OrderMarkAsPaidDialog from "../../components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog"; import OrderPaymentDialog from "../../components/OrderPaymentDialog"; @@ -39,7 +40,8 @@ import { orderListUrl, orderUrl, OrderUrlQueryParams, - OrderUrlDialog + OrderUrlDialog, + orderFulfillUrl } from "../../urls"; import { OrderDetailsMessages } from "./OrderDetailsMessages"; @@ -95,6 +97,12 @@ export const OrderDetails: React.FC = ({ id, params }) => { } = useOrderVariantSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const warehouses = useWarehouseList({ + displayLoader: true, + variables: { + first: 30 + } + }); const intl = useIntl(); const [openModal, closeModal] = createDialogActionHandlers< @@ -118,9 +126,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { {orderMessages => ( = ({ id, params }) => { {({ orderAddNote, orderCancel, - orderCreateFulfillment, orderDraftUpdate, orderLinesAdd, orderLineDelete, @@ -194,7 +198,7 @@ export const OrderDetails: React.FC = ({ id, params }) => { )} userPermissions={user?.userPermissions || []} onOrderCancel={() => openModal("cancel")} - onOrderFulfill={() => openModal("fulfill")} + onOrderFulfill={() => navigate(orderFulfillUrl(id))} onFulfillmentCancel={fulfillmentId => navigate( orderUrl(id, { @@ -226,6 +230,17 @@ export const OrderDetails: React.FC = ({ id, params }) => { navigate(customerUrl(order.user.id)) } /> + + fulfillment.status === + FulfillmentStatus.FULFILLED + ) + } + /> = ({ id, params }) => { number={order?.number} open={params.action === "cancel"} onClose={closeModal} - onSubmit={variables => + onSubmit={() => orderCancel.mutate({ - id, - ...variables + id }) } /> @@ -298,38 +312,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { }) } /> - order.lines, []).filter( - line => line.quantityFulfilled < line.quantity - )} - onClose={closeModal} - onSubmit={variables => - orderCreateFulfillment.mutate({ - input: { - ...variables, - lines: maybe(() => order.lines, []) - .filter( - line => - line.quantityFulfilled < line.quantity - ) - .map((line, lineIndex) => ({ - orderLineId: line.id, - quantity: variables.lines[lineIndex] - })) - .filter(line => line.quantity > 0) - }, - order: order.id - }) - } - /> = ({ id, params }) => { ?.orderFulfillmentCancel.errors || [] } open={params.action === "cancel-fulfillment"} + warehouses={ + warehouses.data?.warehouses.edges.map( + edge => edge.node + ) || [] + } onConfirm={variables => orderFulfillmentCancel.mutate({ id: params.id, diff --git a/src/orders/views/OrderFulfill/OrderFulfill.tsx b/src/orders/views/OrderFulfill/OrderFulfill.tsx new file mode 100644 index 000000000..39dd898d3 --- /dev/null +++ b/src/orders/views/OrderFulfill/OrderFulfill.tsx @@ -0,0 +1,94 @@ +import { useIntl } from "react-intl"; +import React from "react"; + +import { useOrderFulfillData } from "@saleor/orders/queries"; +import OrderFulfillPage from "@saleor/orders/components/OrderFulfillPage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import { orderUrl } from "@saleor/orders/urls"; +import { useWarehouseList } from "@saleor/warehouses/queries"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { useOrderFulfill } from "@saleor/orders/mutations"; +import useNotifier from "@saleor/hooks/useNotifier"; + +export interface OrderFulfillProps { + orderId: string; +} + +const OrderFulfill: React.FC = ({ orderId }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + const { data, loading } = useOrderFulfillData({ + displayLoader: true, + variables: { + orderId + } + }); + const { data: warehouseData, loading: warehousesLoading } = useWarehouseList({ + displayLoader: true, + variables: { + first: 20 + } + }); + const [fulfillOrder, fulfillOrderOpts] = useOrderFulfill({ + onCompleted: data => { + if (data.orderFulfill.errors.length === 0) { + navigate(orderUrl(orderId), true); + notify({ + text: intl.formatMessage({ + defaultMessage: "Fulfilled Items", + description: "order fulfilled success message" + }) + }); + } + } + }); + + return ( + <> + + navigate(orderUrl(orderId))} + onSubmit={formData => + fulfillOrder({ + variables: { + input: { + lines: formData.items.map(line => ({ + orderLineId: line.id, + stocks: line.value + })), + notifyCustomer: formData.sendInfo + }, + orderId + } + }) + } + order={data?.order} + saveButtonBar="default" + warehouses={warehouseData?.warehouses.edges.map(edge => edge.node)} + /> + + ); +}; + +OrderFulfill.displayName = "OrderFulfill"; +export default OrderFulfill; diff --git a/src/orders/views/OrderFulfill/index.ts b/src/orders/views/OrderFulfill/index.ts new file mode 100644 index 000000000..62368b27d --- /dev/null +++ b/src/orders/views/OrderFulfill/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderFulfill"; +export { default } from "./OrderFulfill"; diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index eccc47b8f..d08881d2b 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -1,12 +1,10 @@ -import Button from "@material-ui/core/Button"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { useIntl } from "react-intl"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { SaveFilterTabDialogFormData } from "@saleor/components/SaveFilterTabDialog"; -import useBulkActions from "@saleor/hooks/useBulkActions"; import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; @@ -14,20 +12,15 @@ import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import { maybe } from "@saleor/misc"; +import { maybe, getStringOrPlaceholder } from "@saleor/misc"; import { ListViews } from "@saleor/types"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; import { getSortParams } from "@saleor/utils/sort"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; -import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog"; import OrderListPage from "../../components/OrderListPage/OrderListPage"; -import { - TypedOrderBulkCancelMutation, - useOrderDraftCreateMutation -} from "../../mutations"; +import { useOrderDraftCreateMutation } from "../../mutations"; import { useOrderListQuery } from "../../queries"; -import { OrderBulkCancel } from "../../types/OrderBulkCancel"; import { OrderDraftCreate } from "../../types/OrderDraftCreate"; import { orderListUrl, @@ -56,9 +49,6 @@ export const OrderList: React.FC = ({ params }) => { const notify = useNotifier(); const paginate = usePaginator(); const shop = useShop(); - const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( - params.ids - ); const { updateListSettings, settings } = useListSettings( ListViews.ORDER_LIST ); @@ -91,7 +81,6 @@ export const OrderList: React.FC = ({ params }) => { resetFilters, handleSearchChange ] = createFilterHandlers({ - cleanupFn: reset, createUrl: orderListUrl, getFilterQueryParam, navigate, @@ -103,19 +92,16 @@ export const OrderList: React.FC = ({ params }) => { OrderListUrlQueryParams >(navigate, orderListUrl, params); - const handleTabChange = (tab: number) => { - reset(); + const handleTabChange = (tab: number) => navigate( orderListUrl({ activeTab: tab.toString(), ...getFilterTabs()[tab - 1].data }) ); - }; const handleFilterTabDelete = () => { deleteFilterTab(currentTab); - reset(); navigate(orderListUrl()); }; @@ -135,7 +121,7 @@ export const OrderList: React.FC = ({ params }) => { }), [params, settings.rowNumber] ); - const { data, loading, refetch } = useOrderListQuery({ + const { data, loading } = useOrderListQuery({ displayLoader: true, variables: queryVariables }); @@ -146,101 +132,48 @@ export const OrderList: React.FC = ({ params }) => { params ); - const handleOrderBulkCancel = (data: OrderBulkCancel) => { - if (data.orderBulkCancel.errors.length === 0) { - notify({ - text: intl.formatMessage({ - defaultMessage: "Orders cancelled" - }) - }); - reset(); - refetch(); - closeModal(); - } - }; - const handleSort = createSortHandler(navigate, orderListUrl, params); return ( - - {(orderBulkCancel, orderBulkCancelOpts) => { - const onOrderBulkCancel = (restock: boolean) => - orderBulkCancel({ - variables: { - ids: params.ids, - restock - } - }); - - return ( - <> - data.orders.edges.map(edge => edge.node))} - pageInfo={pageInfo} - sort={getSortParams(params)} - onAdd={createOrder} - onNextPage={loadNextPage} - onPreviousPage={loadPreviousPage} - onUpdateListSettings={updateListSettings} - onRowClick={id => () => navigate(orderUrl(id))} - onSort={handleSort} - isChecked={isSelected} - selected={listElements.length} - toggle={toggle} - toggleAll={toggleAll} - toolbar={ - - } - onSearchChange={handleSearchChange} - onFilterChange={changeFilters} - onTabSave={() => openModal("save-search")} - onTabDelete={() => openModal("delete-search")} - onTabChange={handleTabChange} - initialSearch={params.query || ""} - tabs={getFilterTabs().map(tab => tab.name)} - onAll={resetFilters} - /> - params.ids.length.toString(), "...")} - onClose={closeModal} - onConfirm={onOrderBulkCancel} - open={params.action === "cancel"} - /> - - tabs[currentTab - 1].name, "...")} - /> - - ); - }} - + <> + data.orders.edges.map(edge => edge.node))} + pageInfo={pageInfo} + sort={getSortParams(params)} + onAdd={createOrder} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} + onRowClick={id => () => navigate(orderUrl(id))} + onSort={handleSort} + onSearchChange={handleSearchChange} + onFilterChange={changeFilters} + onTabSave={() => openModal("save-search")} + onTabDelete={() => openModal("delete-search")} + onTabChange={handleTabChange} + initialSearch={params.query || ""} + tabs={getFilterTabs().map(tab => tab.name)} + onAll={resetFilters} + /> + + + ); }; diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 8386cfb8c..ba8174f4f 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -28,6 +28,7 @@ import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/ import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { FetchMoreProps } from "../../../types"; import { createAttributeChangeHandler, @@ -41,7 +42,7 @@ import ProductAttributes, { import ProductDetailsForm from "../ProductDetailsForm"; import ProductOrganization from "../ProductOrganization"; import ProductPricing from "../ProductPricing"; -import ProductStock from "../ProductStock"; +import ProductStocks, { ProductStockInput } from "../ProductStocks"; interface FormData { basePrice: number; @@ -57,9 +58,11 @@ interface FormData { seoTitle: string; sku: string; stockQuantity: number; + trackInventory: boolean; } export interface ProductCreatePageSubmitData extends FormData { attributes: ProductAttributeInput[]; + stocks: ProductStockInput[]; } interface ProductCreatePageProps { @@ -79,9 +82,11 @@ interface ProductCreatePageProps { }>; header: string; saveButtonBarState: ConfirmButtonTransitionState; + warehouses: SearchWarehouses_search_edges_node[]; fetchCategories: (data: string) => void; fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; + onWarehouseEdit: () => void; onBack?(); onSubmit?(data: ProductCreatePageSubmitData); } @@ -100,9 +105,11 @@ export const ProductCreatePage: React.FC = ({ header, productTypes: productTypeChoiceList, saveButtonBarState, + warehouses, onBack, fetchProductTypes, - onSubmit + onSubmit, + onWarehouseEdit }: ProductCreatePageProps) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -112,6 +119,18 @@ export const ProductCreatePage: React.FC = ({ data: attributes, set: setAttributeData } = useFormset([]); + const { change: changeStockData, data: stocks, set: setStocks } = useFormset< + null + >([]); + React.useEffect(() => { + const newStocks = warehouses.map(warehouse => ({ + data: null, + id: warehouse.id, + label: warehouse.name, + value: stocks.find(stock => stock.id === warehouse.id)?.value || 0 + })); + setStocks(newStocks); + }, [JSON.stringify(warehouses)]); // Ensures that it will not change after component rerenders, because it // generates different block keys and it causes editor to lose its content. @@ -131,7 +150,8 @@ export const ProductCreatePage: React.FC = ({ seoDescription: "", seoTitle: "", sku: null, - stockQuantity: null + stockQuantity: null, + trackInventory: false }; // Display values @@ -145,12 +165,7 @@ export const ProductCreatePage: React.FC = ({ MultiAutocompleteChoiceType[] >([]); - const [productType, setProductType] = React.useState({ - hasVariants: false, - id: "", - name: "", - productAttributes: [] - }); + const [productType, setProductType] = React.useState(null); const categories = getChoices(categoryChoiceList); const collections = getChoices(collectionChoiceList); @@ -159,6 +174,7 @@ export const ProductCreatePage: React.FC = ({ const handleSubmit = (data: FormData) => onSubmit({ attributes, + stocks, ...data }); @@ -232,14 +248,16 @@ export const ProductCreatePage: React.FC = ({ onChange={change} /> - {!productType.hasVariants && ( + {!!productType && !productType.hasVariants && ( <> - @@ -273,7 +291,7 @@ export const ProductCreatePage: React.FC = ({ fetchMoreProductTypes={fetchMoreProductTypes} fetchProductTypes={fetchProductTypes} productType={productType} - productTypeInputDisplayValue={productType.name} + productTypeInputDisplayValue={productType?.name || ""} productTypes={productTypes} onCategoryChange={handleCategorySelect} onCollectionChange={handleCollectionSelect} diff --git a/src/products/components/ProductStock/ProductStock.tsx b/src/products/components/ProductStock/ProductStock.tsx deleted file mode 100644 index 6fce4b609..000000000 --- a/src/products/components/ProductStock/ProductStock.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import { makeStyles } from "@material-ui/core/styles"; -import TextField from "@material-ui/core/TextField"; -import React from "react"; -import { useIntl } from "react-intl"; - -import CardTitle from "@saleor/components/CardTitle"; -import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; -import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; -import { ProductDetails_product } from "../../types/ProductDetails"; - -const useStyles = makeStyles( - theme => ({ - root: { - display: "grid", - gridColumnGap: theme.spacing(2), - gridTemplateColumns: "1fr 1fr" - } - }), - { name: "ProductStock" } -); - -interface ProductStockProps { - data: { - sku: string; - stockQuantity: number; - }; - disabled: boolean; - errors: ProductErrorFragment[]; - product: ProductDetails_product; - onChange: (event: React.ChangeEvent) => void; -} - -const ProductStock: React.FC = props => { - const { data, disabled, product, onChange, errors } = props; - - const classes = useStyles(props); - const intl = useIntl(); - - const formErrors = getFormErrors(["sku", "stockQuantity"], errors); - - return ( - - - -
- - -
-
-
- ); -}; -ProductStock.displayName = "ProductStock"; -export default ProductStock; diff --git a/src/products/components/ProductStock/index.ts b/src/products/components/ProductStock/index.ts deleted file mode 100644 index f98d67440..000000000 --- a/src/products/components/ProductStock/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./ProductStock"; -export * from "./ProductStock"; diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx new file mode 100644 index 000000000..e66532cd3 --- /dev/null +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -0,0 +1,218 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Table from "@material-ui/core/Table"; +import TableHead from "@material-ui/core/TableHead"; +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 React from "react"; +import { useIntl, FormattedMessage } from "react-intl"; +import makeStyles from "@material-ui/core/styles/makeStyles"; + +import { FormChange } from "@saleor/hooks/useForm"; +import { FormsetChange, FormsetAtomicData } from "@saleor/hooks/useFormset"; +import CardTitle from "@saleor/components/CardTitle"; +import { getFieldError } from "@saleor/utils/errors"; +import { UserError } from "@saleor/types"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import FormSpacer from "@saleor/components/FormSpacer"; +import Hr from "@saleor/components/Hr"; +import { renderCollection } from "@saleor/misc"; +import Link from "@saleor/components/Link"; + +export type ProductStockInput = FormsetAtomicData; +export interface ProductStockFormData { + sku: string; + trackInventory: boolean; +} + +export interface ProductStocksProps { + data: ProductStockFormData; + disabled: boolean; + errors: UserError[]; + stocks: ProductStockInput[]; + onChange: FormsetChange; + onFormDataChange: FormChange; + onWarehousesEdit: () => void; +} + +const useStyles = makeStyles( + theme => ({ + colName: {}, + colQuantity: { + textAlign: "right", + width: 200 + }, + editWarehouses: { + marginRight: -theme.spacing() + }, + input: { + padding: theme.spacing(1.5), + textAlign: "right" + }, + inputComponent: { + width: 100 + }, + quantityContainer: { + paddingTop: theme.spacing() + }, + quantityHeader: { + alignItems: "center", + display: "flex", + justifyContent: "space-between" + }, + skuInputContainer: { + display: "grid", + gridColumnGap: theme.spacing(3) + "px", + gridTemplateColumns: "repeat(2, 1fr)" + } + }), + { + name: "ProductStocks" + } +); + +const ProductStocks: React.FC = ({ + data, + disabled, + errors, + stocks, + onChange, + onFormDataChange, + onWarehousesEdit +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + return ( + + + +
+ +
+ + + + + + + + } + /> +
+
+ + +
+ + + + +
+
+
+ + + + + + + + + + + + + {renderCollection( + stocks, + stock => ( + + {stock.label} + + onChange(stock.id, event.target.value)} + value={stock.value} + /> + + + ), + () => ( + + + here." + } + values={{ + l: str => {str} + }} + /> + + + ) + )} + +
+
+ ); +}; + +ProductStocks.displayName = "ProductStocks"; +export default ProductStocks; diff --git a/src/products/components/ProductStocks/index.ts b/src/products/components/ProductStocks/index.ts new file mode 100644 index 000000000..ef1830648 --- /dev/null +++ b/src/products/components/ProductStocks/index.ts @@ -0,0 +1,2 @@ +export * from "./ProductStocks"; +export { default } from "./ProductStocks"; diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 0e7362403..d8c23ed2e 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -34,7 +34,8 @@ import { getProductUpdatePageFormData, getSelectedAttributesFromProduct, ProductAttributeValueChoices, - ProductUpdatePageFormData + ProductUpdatePageFormData, + getStockInputFromProduct } from "../../utils/data"; import { createAttributeChangeHandler, @@ -45,8 +46,8 @@ import ProductDetailsForm from "../ProductDetailsForm"; import ProductImages from "../ProductImages"; import ProductOrganization from "../ProductOrganization"; import ProductPricing from "../ProductPricing"; -import ProductStock from "../ProductStock"; import ProductVariants from "../ProductVariants"; +import ProductStocks, { ProductStockInput } from "../ProductStocks"; export interface ProductUpdatePageProps extends ListActions { errors: ProductErrorFragment[]; @@ -63,6 +64,7 @@ export interface ProductUpdatePageProps extends ListActions { saveButtonBarState: ConfirmButtonTransitionState; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; + onWarehousesEdit: () => void; onVariantsAdd: () => void; onVariantShow: (id: string) => () => void; onImageDelete: (id: string) => () => void; @@ -71,7 +73,6 @@ export interface ProductUpdatePageProps extends ListActions { onImageEdit?(id: string); onImageReorder?(event: { oldIndex: number; newIndex: number }); onImageUpload(file: File); - onProductShow?(); onSeoClick?(); onSubmit?(data: ProductUpdatePageSubmitData); onVariantAdd?(); @@ -80,6 +81,7 @@ export interface ProductUpdatePageProps extends ListActions { export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { attributes: ProductAttributeInput[]; collections: string[]; + stocks: ProductStockInput[]; } export const ProductUpdatePage: React.FC = ({ @@ -108,6 +110,7 @@ export const ProductUpdatePage: React.FC = ({ onVariantAdd, onVariantsAdd, onVariantShow, + onWarehousesEdit, isChecked, selected, toggle, @@ -120,9 +123,13 @@ export const ProductUpdatePage: React.FC = ({ () => getAttributeInputFromProduct(product), [product] ); + const stockInput = React.useMemo(() => getStockInputFromProduct(product), [ + product + ]); const { change: changeAttributeData, data: attributes } = useFormset( attributeInput ); + const { change: changeStockData, data: stocks } = useFormset(stockInput); const [selectedAttributes, setSelectedAttributes] = useStateFromProps< ProductAttributeValueChoices[] @@ -149,6 +156,7 @@ export const ProductUpdatePage: React.FC = ({ const handleSubmit = (data: ProductUpdatePageFormData) => onSubmit({ attributes, + stocks, ...data }); @@ -239,12 +247,17 @@ export const ProductUpdatePage: React.FC = ({ toggleAll={toggleAll} /> ) : ( - { + triggerChange(); + changeStockData(id, value); + }} + onFormDataChange={change} + onWarehousesEdit={onWarehousesEdit} /> )} diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreate.stories.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreate.stories.tsx deleted file mode 100644 index b735d3f0f..000000000 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreate.stories.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import { storiesOf } from "@storybook/react"; -import React from "react"; - -import { attributes } from "@saleor/attributes/fixtures"; -import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate"; -import { ProductErrorCode } from "@saleor/types/globalTypes"; -import Decorator from "../../../storybook/Decorator"; -import { createVariants } from "./createVariants"; -import { AllOrAttribute } from "./form"; -import ProductVariantCreateContent, { - ProductVariantCreateContentProps -} from "./ProductVariantCreateContent"; -import ProductVariantCreateDialog from "./ProductVariantCreateDialog"; - -const selectedAttributes = [1, 4, 5].map(index => attributes[index]); - -const price: AllOrAttribute = { - all: false, - attribute: selectedAttributes[1].id, - value: "2.79", - values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({ - slug: attribute.slug, - value: (attributeIndex + 4).toFixed(2) - })) -}; - -const stock: AllOrAttribute = { - all: false, - attribute: selectedAttributes[1].id, - value: "8", - values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({ - slug: attribute.slug, - value: (selectedAttributes.length * 10 - attributeIndex).toString() - })) -}; - -const dataAttributes = selectedAttributes.map(attribute => ({ - id: attribute.id, - values: attribute.values - .map(value => value.slug) - .filter((_, valueIndex) => valueIndex % 2 !== 1) -})); - -const errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[] = [ - { - __typename: "BulkProductError", - code: ProductErrorCode.UNIQUE, - field: "sku", - index: 3 - } -]; - -const props: ProductVariantCreateContentProps = { - attributes, - currencySymbol: "USD", - data: { - attributes: dataAttributes, - price, - stock, - variants: createVariants({ - attributes: dataAttributes, - price, - stock, - variants: [] - }) - }, - dispatchFormDataAction: () => undefined, - errors: [], - onStepClick: () => undefined, - step: "values" -}; - -storiesOf("Views / Products / Create multiple variants", module) - .addDecorator(storyFn => ( - - {storyFn()} - - )) - .addDecorator(Decorator) - .add("choose values", () => ) - .add("prices and SKU", () => ( - - )); - -storiesOf("Views / Products / Create multiple variants / summary", module) - .addDecorator(storyFn => ( - - {storyFn()} - - )) - .addDecorator(Decorator) - .add("default", () => ( - - )) - .add("errors", () => ( - - )); - -storiesOf("Views / Products / Create multiple variants", module) - .addDecorator(Decorator) - .add("interactive", () => ( - undefined} - onSubmit={() => undefined} - /> - )); diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateContent.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreateContent.tsx deleted file mode 100644 index 02bc0ae66..000000000 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateContent.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { makeStyles } from "@material-ui/core/styles"; -import React from "react"; - -import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; -import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate"; -import { isSelected } from "@saleor/utils/lists"; -import { ProductVariantCreateFormData } from "./form"; -import ProductVariantCreatePrices from "./ProductVariantCreatePrices"; -import ProductVariantCreateSummary from "./ProductVariantCreateSummary"; -import ProductVariantCreateTabs from "./ProductVariantCreateTabs"; -import ProductVariantCreateValues from "./ProductVariantCreateValues"; -import { ProductVariantCreateReducerAction } from "./reducer"; -import { ProductVariantCreateStep } from "./types"; - -const useStyles = makeStyles( - theme => ({ - root: { - maxHeight: 400, - overflowX: "hidden", - overflowY: "scroll", - paddingLeft: theme.spacing(3), - paddingRight: theme.spacing(2), - position: "relative", - right: theme.spacing(3), - width: `calc(100% + ${theme.spacing(3)}px)` - } - }), - { name: "ProductVariantCreateContent" } -); - -export interface ProductVariantCreateContentProps { - attributes: ProductDetails_product_productType_variantAttributes[]; - currencySymbol: string; - data: ProductVariantCreateFormData; - dispatchFormDataAction: React.Dispatch; - errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[]; - step: ProductVariantCreateStep; - onStepClick: (step: ProductVariantCreateStep) => void; -} - -const ProductVariantCreateContent: React.FC = props => { - const { - attributes, - currencySymbol, - data, - dispatchFormDataAction, - errors, - step, - onStepClick - } = props; - const classes = useStyles(props); - - const selectedAttributes = attributes.filter(attribute => - isSelected( - attribute.id, - data.attributes.map(dataAttribute => dataAttribute.id), - (a, b) => a === b - ) - ); - - return ( -
- -
- {step === "values" && ( - - dispatchFormDataAction({ - attributeId, - type: "selectValue", - valueId - }) - } - /> - )} - {step === "prices" && ( - - dispatchFormDataAction({ - all, - type: type === "price" ? "applyPriceToAll" : "applyStockToAll" - }) - } - onApplyToAllChange={(value, type) => - dispatchFormDataAction({ - type: - type === "price" - ? "changeApplyPriceToAllValue" - : "changeApplyStockToAllValue", - value - }) - } - onAttributeSelect={(attributeId, type) => - dispatchFormDataAction({ - attributeId, - type: - type === "price" - ? "changeApplyPriceToAttributeId" - : "changeApplyStockToAttributeId" - }) - } - onAttributeValueChange={(valueId, value, type) => - dispatchFormDataAction({ - type: - type === "price" - ? "changeAttributeValuePrice" - : "changeAttributeValueStock", - value, - valueId - }) - } - /> - )} - {step === "summary" && ( - - dispatchFormDataAction({ - field, - type: "changeVariantData", - value, - variantIndex - }) - } - onVariantDelete={variantIndex => - dispatchFormDataAction({ - type: "deleteVariant", - variantIndex - }) - } - /> - )} -
-
- ); -}; - -ProductVariantCreateContent.displayName = "ProductVariantCreateContent"; -export default ProductVariantCreateContent; diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog.tsx deleted file mode 100644 index 17b7f6998..000000000 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog.tsx +++ /dev/null @@ -1,216 +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 React from "react"; -import { FormattedMessage } from "react-intl"; - -import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; -import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen"; -import { ProductVariantBulkCreateInput } from "../../../types/globalTypes"; -import { createInitialForm, ProductVariantCreateFormData } from "./form"; -import ProductVariantCreateContent, { - ProductVariantCreateContentProps -} from "./ProductVariantCreateContent"; -import reduceProductVariantCreateFormData from "./reducer"; -import { ProductVariantCreateStep } from "./types"; - -const useStyles = makeStyles( - theme => ({ - button: { - marginLeft: theme.spacing(2) - }, - content: { - overflowX: "visible", - overflowY: "hidden", - width: 800 - }, - spacer: { - flex: 1 - } - }), - { name: "ProductVariantCreateDialog" } -); - -function canHitNext( - step: ProductVariantCreateStep, - data: ProductVariantCreateFormData -): boolean { - switch (step) { - case "values": - return data.attributes.every(attribute => attribute.values.length > 0); - case "prices": - if (data.price.all) { - if (data.price.value === "") { - return false; - } - } else { - if ( - data.price.attribute === "" || - data.price.values.some(attributeValue => attributeValue.value === "") - ) { - return false; - } - } - - if (data.stock.all) { - if (data.stock.value === "") { - return false; - } - } else { - if ( - data.stock.attribute === "" || - data.stock.values.some(attributeValue => attributeValue.value === "") - ) { - return false; - } - } - - return true; - case "summary": - return data.variants.every(variant => variant.sku !== ""); - - default: - return false; - } -} - -export interface ProductVariantCreateDialogProps - extends Omit< - ProductVariantCreateContentProps, - "data" | "dispatchFormDataAction" | "step" | "onStepClick" - > { - defaultPrice: string; - open: boolean; - onClose: () => void; - onSubmit: (data: ProductVariantBulkCreateInput[]) => void; -} - -const ProductVariantCreateDialog: React.FC< - ProductVariantCreateDialogProps -> = props => { - const { - attributes, - defaultPrice, - errors: apiErrors, - open, - onClose, - onSubmit, - ...contentProps - } = props; - const classes = useStyles(props); - const [step, setStep] = React.useState("values"); - - function handleNextStep() { - switch (step) { - case "values": - setStep("prices"); - break; - case "prices": - setStep("summary"); - break; - } - } - - function handlePrevStep() { - switch (step) { - case "prices": - setStep("values"); - break; - case "summary": - setStep("prices"); - break; - } - } - - const [data, dispatchFormDataAction] = React.useReducer( - reduceProductVariantCreateFormData, - createInitialForm(attributes, defaultPrice) - ); - - const reloadForm = () => - dispatchFormDataAction({ - data: createInitialForm(attributes, defaultPrice), - type: "reload" - }); - - React.useEffect(reloadForm, [attributes.length]); - - useModalDialogOpen(open, { - onClose: () => { - reloadForm(); - setStep("values"); - } - }); - - const errors = useModalDialogErrors(apiErrors, open); - - return ( - - - - - - setStep(step)} - /> - - - -
- {step !== "values" && ( - - )} - {step !== "summary" ? ( - - ) : ( - - )} - -
- ); -}; - -ProductVariantCreateDialog.displayName = "ProductVariantCreateDialog"; -export default ProductVariantCreateDialog; diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreatePrices.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreatePrices.tsx deleted file mode 100644 index d4ef692f9..000000000 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreatePrices.tsx +++ /dev/null @@ -1,315 +0,0 @@ -import FormControlLabel from "@material-ui/core/FormControlLabel"; -import Radio from "@material-ui/core/Radio"; -import RadioGroup from "@material-ui/core/RadioGroup"; -import { makeStyles } from "@material-ui/core/styles"; -import TextField from "@material-ui/core/TextField"; -import Typography from "@material-ui/core/Typography"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import FormSpacer from "@saleor/components/FormSpacer"; -import Grid from "@saleor/components/Grid"; -import Hr from "@saleor/components/Hr"; -import SingleSelectField from "@saleor/components/SingleSelectField"; -import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; -import { ProductVariantCreateFormData } from "./form"; - -const useStyles = makeStyles( - theme => ({ - hr: { - marginBottom: theme.spacing(), - marginTop: theme.spacing(0.5) - }, - hrAttribute: { - marginTop: theme.spacing(2) - }, - label: { - alignSelf: "center" - }, - shortInput: { - width: "50%" - } - }), - { name: "ProductVariantCreatePrices" } -); - -export type PriceOrStock = "price" | "stock"; -export interface ProductVariantCreatePricesProps { - attributes: ProductDetails_product_productType_variantAttributes[]; - currencySymbol: string; - data: ProductVariantCreateFormData; - onApplyPriceOrStockChange: (applyToAll: boolean, type: PriceOrStock) => void; - onApplyToAllChange: (value: string, type: PriceOrStock) => void; - onAttributeSelect: (id: string, type: PriceOrStock) => void; - onAttributeValueChange: ( - id: string, - value: string, - type: PriceOrStock - ) => void; -} - -const ProductVariantCreatePrices: React.FC< - ProductVariantCreatePricesProps -> = props => { - const { - attributes, - currencySymbol, - data, - onApplyPriceOrStockChange, - onApplyToAllChange, - onAttributeSelect, - onAttributeValueChange - } = props; - const classes = useStyles(props); - const intl = useIntl(); - - const attributeChoices = attributes.map(attribute => ({ - label: attribute.name, - value: attribute.id - })); - const priceAttributeValues = data.price.all - ? null - : data.price.attribute - ? attributes - .find(attribute => attribute.id === data.price.attribute) - .values.filter(value => - data.attributes - .find(attribute => attribute.id === data.price.attribute) - .values.includes(value.slug) - ) - : []; - const stockAttributeValues = data.stock.all - ? null - : data.stock.attribute - ? attributes - .find(attribute => attribute.id === data.stock.attribute) - .values.filter(value => - data.attributes - .find(attribute => attribute.id === data.stock.attribute) - .values.includes(value.slug) - ) - : []; - - return ( - <> - - - -
- - } - label={intl.formatMessage({ - defaultMessage: "Apply single price to all SKUs" - })} - onChange={() => onApplyPriceOrStockChange(true, "price")} - /> - - onApplyToAllChange(event.target.value, "price")} - /> - - } - label={intl.formatMessage({ - defaultMessage: "Apply unique prices by attribute to each SKU" - })} - onChange={() => onApplyPriceOrStockChange(false, "price")} - /> - - {!data.price.all && ( - <> - - -
- - - -
-
- - onAttributeSelect(event.target.value, "price") - } - /> -
-
-
- {priceAttributeValues && - priceAttributeValues.map(attributeValue => ( - - - -
- {attributeValue.name} -
-
- value.slug === attributeValue.slug - ).value - } - onChange={event => - onAttributeValueChange( - attributeValue.slug, - event.target.value, - "price" - ) - } - /> -
-
-
- ))} - - )} - - - - -
- - } - label={intl.formatMessage({ - defaultMessage: "Apply single stock to all SKUs" - })} - onChange={() => onApplyPriceOrStockChange(true, "stock")} - /> - - onApplyToAllChange(event.target.value, "stock")} - /> - - } - label={intl.formatMessage({ - defaultMessage: "Apply unique stock by attribute to each SKU" - })} - onChange={() => onApplyPriceOrStockChange(false, "stock")} - /> - - {!data.stock.all && ( - <> - - -
- - - -
-
- - onAttributeSelect(event.target.value, "stock") - } - /> -
-
-
- {stockAttributeValues && - stockAttributeValues.map(attributeValue => ( - - - -
- {attributeValue.name} -
-
- value.slug === attributeValue.slug - ).value - } - onChange={event => - onAttributeValueChange( - attributeValue.slug, - event.target.value, - "stock" - ) - } - /> -
-
-
- ))} - - )} - - ); -}; - -ProductVariantCreatePrices.displayName = "ProductVariantCreatePrices"; -export default ProductVariantCreatePrices; diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateValues.tsx b/src/products/components/ProductVariantCreateDialog/ProductVariantCreateValues.tsx deleted file mode 100644 index 212f7dcbd..000000000 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateValues.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import makeStyles from "@material-ui/core/styles/makeStyles"; -import Typography from "@material-ui/core/Typography"; -import React from "react"; - -import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; -import Debounce from "@saleor/components/Debounce"; -import Hr from "@saleor/components/Hr"; -import Skeleton from "@saleor/components/Skeleton"; -import { maybe } from "@saleor/misc"; -import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; -import { isSelected } from "@saleor/utils/lists"; -import { ProductVariantCreateFormData } from "./form"; - -export interface ProductVariantCreateValuesProps { - attributes: ProductDetails_product_productType_variantAttributes[]; - data: ProductVariantCreateFormData; - onValueClick: (attributeId: string, valueId: string) => void; -} - -const useStyles = makeStyles( - theme => ({ - hr: { - marginBottom: theme.spacing(), - marginTop: theme.spacing(0.5) - }, - valueContainer: { - display: "grid", - gridColumnGap: theme.spacing(3), - gridTemplateColumns: "repeat(3, 1fr)", - marginBottom: theme.spacing(3) - } - }), - { name: "ProductVariantCreateValues" } -); - -const ProductVariantCreateValues: React.FC< - ProductVariantCreateValuesProps -> = props => { - const { attributes, data, onValueClick } = props; - const classes = useStyles(props); - - return ( - <> - {attributes.map(attribute => ( - - - {maybe(() => attribute.name, )} - -
-
- {attribute.values.map(value => ( - onValueClick(attribute.id, value.slug)} - time={100} - key={value.slug} - > - {change => ( - attribute.id === dataAttribute.id - ).values, - (a, b) => a === b - )} - name={`value:${value.slug}`} - label={value.name} - onChange={change} - /> - )} - - ))} -
-
- ))} - - ); -}; - -ProductVariantCreateValues.displayName = "ProductVariantCreateValues"; -export default ProductVariantCreateValues; diff --git a/src/products/components/ProductVariantCreateDialog/__snapshots__/reducer.test.ts.snap b/src/products/components/ProductVariantCreateDialog/__snapshots__/reducer.test.ts.snap deleted file mode 100644 index 22a84acfc..000000000 --- a/src/products/components/ProductVariantCreateDialog/__snapshots__/reducer.test.ts.snap +++ /dev/null @@ -1,1028 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Reducer is able to select attribute values 1`] = ` -Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - "val-4-5", - ], - }, - ], - "price": Object { - "all": true, - "attribute": undefined, - "value": "10.99", - "values": Array [], - }, - "stock": Object { - "all": true, - "attribute": undefined, - "value": "", - "values": Array [], - }, - "variants": Array [], -} -`; - -exports[`Reducer is able to select price for all variants 1`] = ` -Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - "val-4-5", - ], - }, - ], - "price": Object { - "all": true, - "attribute": undefined, - "value": "45.99", - "values": Array [], - }, - "stock": Object { - "all": true, - "attribute": undefined, - "value": "", - "values": Array [], - }, - "variants": Array [ - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - ], -} -`; - -exports[`Reducer is able to select price to each attribute value 1`] = ` -Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - "val-4-5", - ], - }, - ], - "price": Object { - "all": false, - "attribute": "attr-1", - "value": "10.99", - "values": Array [ - Object { - "slug": "val-1-1", - "value": "45.99", - }, - Object { - "slug": "val-1-7", - "value": "51.99", - }, - ], - }, - "stock": Object { - "all": true, - "attribute": undefined, - "value": "", - "values": Array [], - }, - "variants": Array [ - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "45.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "51.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "51.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "51.99", - "quantity": NaN, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "51.99", - "quantity": NaN, - "sku": "", - }, - ], -} -`; - -exports[`Reducer is able to select stock for all variants 1`] = ` -Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - "val-4-5", - ], - }, - ], - "price": Object { - "all": true, - "attribute": undefined, - "value": "10.99", - "values": Array [], - }, - "stock": Object { - "all": true, - "attribute": undefined, - "value": "45.99", - "values": Array [], - }, - "variants": Array [ - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 45, - "sku": "", - }, - ], -} -`; - -exports[`Reducer is able to select stock to each attribute value 1`] = ` -Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - "val-4-5", - ], - }, - ], - "price": Object { - "all": true, - "attribute": undefined, - "value": "10.99", - "values": Array [], - }, - "stock": Object { - "all": false, - "attribute": "attr-1", - "value": "", - "values": Array [ - Object { - "slug": "val-1-1", - "value": "13", - }, - Object { - "slug": "val-1-7", - "value": "19", - }, - ], - }, - "variants": Array [ - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 13, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 13, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 13, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-1", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 13, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 19, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-2", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 19, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-1", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 19, - "sku": "", - }, - Object { - "attributes": Array [ - Object { - "id": "attr-1", - "values": Array [ - "val-1-7", - ], - }, - Object { - "id": "attr-2", - "values": Array [ - "val-2-4", - ], - }, - Object { - "id": "attr-4", - "values": Array [ - "val-4-5", - ], - }, - ], - "priceOverride": "10.99", - "quantity": 19, - "sku": "", - }, - ], -} -`; diff --git a/src/products/components/ProductVariantCreateDialog/index.ts b/src/products/components/ProductVariantCreateDialog/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/products/components/ProductVariantCreateDialog/reducer.test.ts b/src/products/components/ProductVariantCreateDialog/reducer.test.ts deleted file mode 100644 index 9a224af0f..000000000 --- a/src/products/components/ProductVariantCreateDialog/reducer.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { attributes, fourthStep, secondStep, thirdStep } from "./fixtures"; -import reducer, { VariantField } from "./reducer"; - -function execActions( - initialState: TState, - reducer: (state: TState, action: TAction) => TState, - actions: TAction[] -): TState { - return actions.reduce((acc, action) => reducer(acc, action), initialState); -} - -describe("Reducer is able to", () => { - it("select attribute values", () => { - const state = execActions(secondStep, reducer, [ - { - attributeId: attributes[0].id, - type: "selectValue", - valueId: attributes[0].values[0] - }, - { - attributeId: attributes[0].id, - type: "selectValue", - valueId: attributes[0].values[6] - }, - { - attributeId: attributes[1].id, - type: "selectValue", - valueId: attributes[1].values[1] - }, - { - attributeId: attributes[1].id, - type: "selectValue", - valueId: attributes[1].values[3] - }, - { - attributeId: attributes[3].id, - type: "selectValue", - valueId: attributes[3].values[0] - }, - { - attributeId: attributes[3].id, - type: "selectValue", - valueId: attributes[3].values[4] - } - ]); - - expect(state.attributes[0].values).toHaveLength(2); - expect(state.attributes[1].values).toHaveLength(2); - expect(state.attributes[2].values).toHaveLength(2); - expect(state).toMatchSnapshot(); - }); - - it("select price for all variants", () => { - const value = "45.99"; - const state = execActions(thirdStep, reducer, [ - { - all: true, - type: "applyPriceToAll" - }, - { - type: "changeApplyPriceToAllValue", - value - } - ]); - - expect(state.price.all).toBeTruthy(); - expect(state.price.value).toBe(value); - expect(state).toMatchSnapshot(); - }); - - it("select stock for all variants", () => { - const value = 45.99; - const state = execActions(thirdStep, reducer, [ - { - all: true, - type: "applyStockToAll" - }, - { - type: "changeApplyStockToAllValue", - value: value.toString() - } - ]); - - expect(state.stock.all).toBeTruthy(); - expect(state.stock.value).toBe(value.toString()); - expect(state).toMatchSnapshot(); - }); - - it("select price to each attribute value", () => { - const attribute = thirdStep.attributes[0]; - const value = 45.99; - const state = execActions(thirdStep, reducer, [ - { - all: false, - type: "applyPriceToAll" - }, - { - attributeId: attribute.id, - type: "changeApplyPriceToAttributeId" - }, - { - type: "changeAttributeValuePrice", - value: value.toString(), - valueId: attribute.values[0] - }, - { - type: "changeAttributeValuePrice", - value: (value + 6).toString(), - valueId: attribute.values[1] - } - ]); - - expect(state.price.all).toBeFalsy(); - expect(state.price.values).toHaveLength( - state.attributes.find(attribute => state.price.attribute === attribute.id) - .values.length - ); - expect(state).toMatchSnapshot(); - }); - - it("select stock to each attribute value", () => { - const attribute = thirdStep.attributes[0]; - const value = 13; - const state = execActions(thirdStep, reducer, [ - { - all: false, - type: "applyStockToAll" - }, - { - attributeId: attribute.id, - type: "changeApplyStockToAttributeId" - }, - { - type: "changeAttributeValueStock", - value: value.toString(), - valueId: attribute.values[0] - }, - { - type: "changeAttributeValueStock", - value: (value + 6).toString(), - valueId: attribute.values[1] - } - ]); - - expect(state.stock.all).toBeFalsy(); - expect(state.stock.values).toHaveLength( - state.attributes.find(attribute => state.stock.attribute === attribute.id) - .values.length - ); - expect(state).toMatchSnapshot(); - }); - - it("modify individual variant price", () => { - const field: VariantField = "price"; - const value = "49.99"; - const variantIndex = 3; - - const state = execActions(fourthStep, reducer, [ - { - field, - type: "changeVariantData", - value, - variantIndex - } - ]); - - expect(state.variants[variantIndex].priceOverride).toBe(value); - expect(state.variants[variantIndex - 1].priceOverride).toBe( - fourthStep.variants[variantIndex - 1].priceOverride - ); - }); - - it("delete variant", () => { - const variantIndex = 3; - - const state = execActions(fourthStep, reducer, [ - { - type: "deleteVariant", - variantIndex - } - ]); - - expect(state.variants.length).toBe(fourthStep.variants.length - 1); - }); -}); diff --git a/src/products/components/ProductVariantCreateDialog/reducer.ts b/src/products/components/ProductVariantCreateDialog/reducer.ts deleted file mode 100644 index 6280a3e24..000000000 --- a/src/products/components/ProductVariantCreateDialog/reducer.ts +++ /dev/null @@ -1,362 +0,0 @@ -import { - add, - remove, - removeAtIndex, - toggle, - updateAtIndex -} from "@saleor/utils/lists"; -import { createVariants } from "./createVariants"; -import { ProductVariantCreateFormData } from "./form"; - -export type ProductVariantCreateReducerActionType = - | "applyPriceToAll" - | "applyPriceToAttribute" - | "applyStockToAll" - | "applyStockToAttribute" - | "changeApplyPriceToAllValue" - | "changeApplyPriceToAttributeId" - | "changeApplyStockToAllValue" - | "changeApplyStockToAttributeId" - | "changeAttributeValuePrice" - | "changeAttributeValueStock" - | "changeVariantData" - | "deleteVariant" - | "reload" - | "selectValue"; - -export type VariantField = "stock" | "price" | "sku"; -export interface ProductVariantCreateReducerAction { - all?: boolean; - attributeId?: string; - data?: ProductVariantCreateFormData; - field?: VariantField; - type: ProductVariantCreateReducerActionType; - value?: string; - valueId?: string; - variantIndex?: number; -} - -function selectValue( - prevState: ProductVariantCreateFormData, - attributeId: string, - valueSlug: string -): ProductVariantCreateFormData { - const attribute = prevState.attributes.find( - attribute => attribute.id === attributeId - ); - const values = toggle(valueSlug, attribute.values, (a, b) => a === b); - const updatedAttributes = add( - { - id: attributeId, - values - }, - remove(attribute, prevState.attributes, (a, b) => a.id === b.id) - ); - - const priceValues = - prevState.price.attribute === attributeId - ? toggle( - { - slug: valueSlug, - value: "" - }, - prevState.price.values, - (a, b) => a.slug === b.slug - ) - : prevState.price.values; - - const stockValues = - prevState.stock.attribute === attributeId - ? toggle( - { - slug: valueSlug, - value: "" - }, - prevState.stock.values, - (a, b) => a.slug === b.slug - ) - : prevState.stock.values; - - return { - ...prevState, - attributes: updatedAttributes, - price: { - ...prevState.price, - values: priceValues - }, - stock: { - ...prevState.stock, - values: stockValues - } - }; -} - -function applyPriceToAll( - state: ProductVariantCreateFormData, - value: boolean -): ProductVariantCreateFormData { - const data = { - ...state, - price: { - ...state.price, - all: value - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function applyStockToAll( - state: ProductVariantCreateFormData, - value: boolean -): ProductVariantCreateFormData { - const data = { - ...state, - stock: { - ...state.stock, - all: value - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeAttributeValuePrice( - state: ProductVariantCreateFormData, - attributeValueSlug: string, - price: string -): ProductVariantCreateFormData { - const index = state.price.values.findIndex( - value => value.slug === attributeValueSlug - ); - - if (index === -1) { - throw new Error(`Value with id ${attributeValueSlug} not found`); - } - - const values = updateAtIndex( - { - slug: attributeValueSlug, - value: price - }, - state.price.values, - index - ); - - const data = { - ...state, - price: { - ...state.price, - values - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeAttributeValueStock( - state: ProductVariantCreateFormData, - attributeValueSlug: string, - stock: string -): ProductVariantCreateFormData { - const index = state.stock.values.findIndex( - value => value.slug === attributeValueSlug - ); - - if (index === -1) { - throw new Error(`Value with id ${attributeValueSlug} not found`); - } - - const values = updateAtIndex( - { - slug: attributeValueSlug, - value: stock - }, - state.stock.values, - index - ); - - const data = { - ...state, - stock: { - ...state.stock, - values - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeApplyPriceToAttributeId( - state: ProductVariantCreateFormData, - attributeId: string -): ProductVariantCreateFormData { - const attribute = state.attributes.find( - attribute => attribute.id === attributeId - ); - const values = attribute.values.map(slug => ({ - slug, - value: "" - })); - const data = { - ...state, - price: { - ...state.price, - attribute: attributeId, - values - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeApplyStockToAttributeId( - state: ProductVariantCreateFormData, - attributeId: string -): ProductVariantCreateFormData { - const attribute = state.attributes.find( - attribute => attribute.id === attributeId - ); - const values = attribute.values.map(slug => ({ - slug, - value: "" - })); - - const data = { - ...state, - stock: { - ...state.stock, - attribute: attributeId, - values - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeApplyPriceToAllValue( - state: ProductVariantCreateFormData, - value: string -): ProductVariantCreateFormData { - const data = { - ...state, - price: { - ...state.price, - value - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeApplyStockToAllValue( - state: ProductVariantCreateFormData, - value: string -): ProductVariantCreateFormData { - const data = { - ...state, - stock: { - ...state.stock, - value - } - }; - - return { - ...data, - variants: createVariants(data) - }; -} - -function changeVariantData( - state: ProductVariantCreateFormData, - field: VariantField, - value: string, - variantIndex: number -): ProductVariantCreateFormData { - const variant = state.variants[variantIndex]; - if (field === "price") { - variant.priceOverride = value; - } else if (field === "sku") { - variant.sku = value; - } else { - variant.quantity = parseInt(value, 10); - } - - return { - ...state, - variants: updateAtIndex(variant, state.variants, variantIndex) - }; -} - -function deleteVariant( - state: ProductVariantCreateFormData, - variantIndex: number -): ProductVariantCreateFormData { - return { - ...state, - variants: removeAtIndex(state.variants, variantIndex) - }; -} - -function reduceProductVariantCreateFormData( - prevState: ProductVariantCreateFormData, - action: ProductVariantCreateReducerAction -) { - switch (action.type) { - case "selectValue": - return selectValue(prevState, action.attributeId, action.valueId); - - case "applyPriceToAll": - return applyPriceToAll(prevState, action.all); - case "applyStockToAll": - return applyStockToAll(prevState, action.all); - case "changeAttributeValuePrice": - return changeAttributeValuePrice(prevState, action.valueId, action.value); - case "changeAttributeValueStock": - return changeAttributeValueStock(prevState, action.valueId, action.value); - case "changeApplyPriceToAttributeId": - return changeApplyPriceToAttributeId(prevState, action.attributeId); - case "changeApplyStockToAttributeId": - return changeApplyStockToAttributeId(prevState, action.attributeId); - case "changeApplyPriceToAllValue": - return changeApplyPriceToAllValue(prevState, action.value); - case "changeApplyStockToAllValue": - return changeApplyStockToAllValue(prevState, action.value); - case "changeVariantData": - return changeVariantData( - prevState, - action.field, - action.value, - action.variantIndex - ); - case "deleteVariant": - return deleteVariant(prevState, action.variantIndex); - case "reload": - return action.data; - default: - return prevState; - } -} - -export default reduceProductVariantCreateFormData; diff --git a/src/products/components/ProductVariantCreateDialog/types.ts b/src/products/components/ProductVariantCreateDialog/types.ts deleted file mode 100644 index 576a569aa..000000000 --- a/src/products/components/ProductVariantCreateDialog/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type ProductVariantCreateStep = "values" | "prices" | "summary"; diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 47b3c4d13..5204732bd 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -15,6 +15,7 @@ import useFormset, { } from "@saleor/hooks/useFormset"; import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { maybe } from "../../../misc"; import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData"; import ProductVariantAttributes, { @@ -22,7 +23,7 @@ import ProductVariantAttributes, { } from "../ProductVariantAttributes"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; -import ProductVariantStock from "../ProductVariantStock"; +import ProductStocks, { ProductStockInput } from "../ProductStocks"; interface ProductVariantCreatePageFormData { costPrice: string; @@ -30,35 +31,41 @@ interface ProductVariantCreatePageFormData { priceOverride: string; quantity: string; sku: string; + trackInventory: boolean; } export interface ProductVariantCreatePageSubmitData extends ProductVariantCreatePageFormData { attributes: FormsetData; + stocks: ProductStockInput[]; } interface ProductVariantCreatePageProps { currencySymbol: string; + disabled: boolean; errors: ProductErrorFragment[]; header: string; - loading: boolean; product: ProductVariantCreateData_product; saveButtonBarState: ConfirmButtonTransitionState; + warehouses: SearchWarehouses_search_edges_node[]; onBack: () => void; onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; + onWarehouseEdit: () => void; } const ProductVariantCreatePage: React.FC = ({ currencySymbol, + disabled, errors, - loading, header, product, saveButtonBarState, + warehouses, onBack, onSubmit, - onVariantClick + onVariantClick, + onWarehouseEdit }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -68,28 +75,33 @@ const ProductVariantCreatePage: React.FC = ({ const { change: changeAttributeData, data: attributes } = useFormset( attributeInput ); + const { change: changeStockData, data: stocks, set: setStocks } = useFormset< + null + >([]); + React.useEffect(() => { + const newStocks = warehouses.map(warehouse => ({ + data: null, + id: warehouse.id, + label: warehouse.name, + value: stocks.find(stock => stock.id === warehouse.id)?.value || 0 + })); + setStocks(newStocks); + }, [JSON.stringify(warehouses)]); - const initialForm = { - attributes: maybe( - () => - product.productType.variantAttributes.map(attribute => ({ - name: attribute.name, - slug: attribute.slug, - values: [""] - })), - [] - ), + const initialForm: ProductVariantCreatePageFormData = { costPrice: "", images: maybe(() => product.images.map(image => image.id)), priceOverride: "", quantity: "0", - sku: "" + sku: "", + trackInventory: true }; const handleSubmit = (data: ProductVariantCreatePageFormData) => onSubmit({ ...data, - attributes + attributes, + stocks }); return ( @@ -119,7 +131,7 @@ const ProductVariantCreatePage: React.FC = ({
@@ -129,21 +141,23 @@ const ProductVariantCreatePage: React.FC = ({ priceOverride={data.priceOverride} currencySymbol={currencySymbol} costPrice={data.costPrice} - loading={loading} + loading={disabled} onChange={change} /> -
attributes[index]); +const selectedWarehouses = [0, 1, 3].map(index => warehouseList[index]); + +const price: AllOrAttribute = { + attribute: selectedAttributes[0].id, + mode: "attribute", + value: "2.79", + values: selectedAttributes[0].values.map((attribute, attributeIndex) => ({ + slug: attribute.slug, + value: (attributeIndex + 4).toFixed(2) + })) +}; + +const stock: AllOrAttribute = { + attribute: selectedAttributes[0].id, + mode: "attribute", + value: selectedWarehouses.map( + (_, warehouseIndex) => (warehouseIndex + 2) * 3 + ), + values: selectedAttributes[0].values.map((attribute, attributeIndex) => ({ + slug: attribute.slug, + value: selectedWarehouses.map( + (_, warehouseIndex) => + selectedAttributes.length * 10 - attributeIndex - warehouseIndex * 3 + ) + })) +}; + +const dataAttributes = selectedAttributes.map(attribute => ({ + id: attribute.id, + values: attribute.values + .map(value => value.slug) + .filter((_, valueIndex) => valueIndex % 2 !== 1) +})); + +const errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[] = [ + { + __typename: "BulkProductError", + code: ProductErrorCode.UNIQUE, + field: "sku", + index: 3 + } +]; + +const data: ProductVariantCreateFormData = { + attributes: dataAttributes, + price, + stock, + variants: createVariants({ + attributes: dataAttributes, + price, + stock, + variants: [], + warehouses: selectedWarehouses.map(warehouse => warehouse.id) + }), + warehouses: selectedWarehouses.map(warehouse => warehouse.id) +}; +const props: ProductVariantCreatorContentProps = { + attributes: [0, 1, 4, 6].map(index => attributes[index]), + currencySymbol: "USD", + data: { + ...data, + variants: createVariants(data) + }, + dispatchFormDataAction: () => undefined, + errors: [], + step: ProductVariantCreatorStep.values, + warehouses: warehouseList +}; + +storiesOf("Views / Products / Create multiple variants", module) + .addDecorator(storyFn => {storyFn()}) + .addDecorator(Decorator) + .add("choose values", () => ); + +storiesOf( + "Views / Products / Create multiple variants / prices and SKUs", + module +) + .addDecorator(storyFn => {storyFn()}) + .addDecorator(Decorator) + .add("apply to all", () => ( + + )) + .add("apply to all when one warehouse", () => ( + + )) + .add("apply to attribute", () => ( + + )) + .add("apply to attribute when one warehouse", () => ( + + )); + +storiesOf("Views / Products / Create multiple variants / summary", module) + .addDecorator(storyFn => {storyFn()}) + .addDecorator(Decorator) + .add("default", () => ( + + )) + .add("errors", () => ( + + )); + +storiesOf("Views / Products / Create multiple variants", module) + .addDecorator(Decorator) + .add("interactive", () => ( + undefined} + /> + )); diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorContent.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorContent.tsx new file mode 100644 index 000000000..0653a0b0b --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorContent.tsx @@ -0,0 +1,183 @@ +import React from "react"; + +import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import { isSelected } from "@saleor/utils/lists"; +import { ProductVariantCreateFormData } from "./form"; +import ProductVariantCreatePriceAndSku from "./ProductVariantCreatorPriceAndSku"; +import ProductVariantCreateSummary from "./ProductVariantCreatorSummary"; +import ProductVariantCreateValues from "./ProductVariantCreatorValues"; +import { + ProductVariantCreateReducerAction, + ProductVariantCreateReducerActionType +} from "./reducer"; +import { ProductVariantCreatorStep } from "./types"; + +export interface ProductVariantCreatorContentProps { + attributes: ProductDetails_product_productType_variantAttributes[]; + currencySymbol: string; + data: ProductVariantCreateFormData; + dispatchFormDataAction: React.Dispatch; + errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[]; + step: ProductVariantCreatorStep; + warehouses: WarehouseFragment[]; +} + +const ProductVariantCreatorContent: React.FC = ({ + attributes, + currencySymbol, + data, + dispatchFormDataAction, + errors, + step, + warehouses +}) => { + const selectedAttributes = attributes.filter(attribute => + isSelected( + attribute.id, + data.attributes.map(dataAttribute => dataAttribute.id), + (a, b) => a === b + ) + ); + + return ( + <> + {step === ProductVariantCreatorStep.values && ( + + dispatchFormDataAction({ + selectValue: { + attributeId, + valueId + }, + type: ProductVariantCreateReducerActionType.selectValue + }) + } + /> + )} + {step === ProductVariantCreatorStep.prices && ( + + dispatchFormDataAction({ + applyPriceOrStockToAll: { + mode + }, + type: + type === "price" + ? ProductVariantCreateReducerActionType.applyPriceToAll + : ProductVariantCreateReducerActionType.applyStockToAll + }) + } + onApplyToAllPriceChange={price => + dispatchFormDataAction({ + changeApplyPriceToAllValue: { + price + }, + type: + ProductVariantCreateReducerActionType.changeApplyPriceToAllValue + }) + } + onApplyToAllStockChange={(quantity, warehouseIndex) => + dispatchFormDataAction({ + changeApplyStockToAllValue: { + quantity, + warehouseIndex + }, + type: + ProductVariantCreateReducerActionType.changeApplyStockToAllValue + }) + } + onAttributeSelect={(attributeId, type) => + dispatchFormDataAction({ + changeApplyPriceOrStockToAttributeId: { + attributeId + }, + type: + type === "price" + ? ProductVariantCreateReducerActionType.changeApplyPriceToAttributeId + : ProductVariantCreateReducerActionType.changeApplyStockToAttributeId + }) + } + onAttributePriceChange={(valueId, price) => + dispatchFormDataAction({ + changeAttributeValuePrice: { + price, + valueId + }, + type: + ProductVariantCreateReducerActionType.changeAttributeValuePrice + }) + } + onAttributeStockChange={(valueId, quantity, warehouseIndex) => + dispatchFormDataAction({ + changeAttributeValueStock: { + quantity, + valueId, + warehouseIndex + }, + type: + ProductVariantCreateReducerActionType.changeAttributeValueStock + }) + } + onWarehouseToggle={warehouseId => + dispatchFormDataAction({ + changeWarehouses: { + warehouseId + }, + type: ProductVariantCreateReducerActionType.changeWarehouses + }) + } + /> + )} + {step === ProductVariantCreatorStep.summary && ( + + dispatchFormDataAction({ + changeVariantData: { + field, + value, + variantIndex + }, + type: ProductVariantCreateReducerActionType.changeVariantData + }) + } + onVariantStockDataChange={(variantIndex, warehouse, value) => + dispatchFormDataAction({ + changeVariantStockData: { + stock: { + quantity: parseInt(value, 10), + warehouse + }, + variantIndex + }, + type: ProductVariantCreateReducerActionType.changeVariantStockData + }) + } + onVariantDelete={variantIndex => + dispatchFormDataAction({ + deleteVariant: { + variantIndex + }, + type: ProductVariantCreateReducerActionType.deleteVariant + }) + } + warehouses={warehouses} + /> + )} + + ); +}; + +ProductVariantCreatorContent.displayName = "ProductVariantCreatorContent"; +export default ProductVariantCreatorContent; diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPage.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPage.tsx new file mode 100644 index 000000000..ec1879404 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPage.tsx @@ -0,0 +1,233 @@ +import Button from "@material-ui/core/Button"; +import Typography from "@material-ui/core/Typography"; +import { makeStyles } from "@material-ui/core/styles"; +import React from "react"; +import { FormattedMessage, useIntl, IntlShape } from "react-intl"; + +import useWizard from "@saleor/hooks/useWizard"; +import PageHeader from "@saleor/components/PageHeader"; +import Container from "@saleor/components/Container"; +import Hr from "@saleor/components/Hr"; +import { ProductVariantBulkCreateInput } from "../../../types/globalTypes"; +import { createInitialForm, ProductVariantCreateFormData } from "./form"; +import ProductVariantCreatorContent, { + ProductVariantCreatorContentProps +} from "./ProductVariantCreatorContent"; +import reduceProductVariantCreateFormData, { + ProductVariantCreateReducerActionType +} from "./reducer"; +import { ProductVariantCreatorStep } from "./types"; +import ProductVariantCreateTabs from "./ProductVariantCreatorTabs"; + +const useStyles = makeStyles( + theme => ({ + button: { + marginLeft: theme.spacing(2) + }, + content: { + overflowX: "visible", + overflowY: "hidden", + width: 800 + }, + description: { + marginTop: theme.spacing() + }, + hr: { + margin: theme.spacing(3, 0) + } + }), + { name: "ProductVariantCreatePage" } +); + +function canHitNext( + step: ProductVariantCreatorStep, + data: ProductVariantCreateFormData +): boolean { + switch (step) { + case ProductVariantCreatorStep.values: + return data.attributes.every(attribute => attribute.values.length > 0); + case ProductVariantCreatorStep.prices: + if (data.price.mode === "all") { + if (data.price.value === "") { + return false; + } + } else if (data.price.mode === "attribute") { + if ( + data.price.attribute === "" || + data.price.values.some(attributeValue => attributeValue.value === "") + ) { + return false; + } + } + + if (data.stock.mode === "attribute" && data.stock.attribute === "") { + return false; + } + + return true; + case ProductVariantCreatorStep.summary: + return data.variants.every(variant => variant.sku !== ""); + + default: + return false; + } +} + +export interface ProductVariantCreatePageProps + extends Omit< + ProductVariantCreatorContentProps, + "data" | "dispatchFormDataAction" | "step" | "onStepClick" + > { + defaultPrice: string; + onSubmit: (data: ProductVariantBulkCreateInput[]) => void; +} + +function getTitle(step: ProductVariantCreatorStep, intl: IntlShape): string { + switch (step) { + case ProductVariantCreatorStep.values: + return intl.formatMessage({ + defaultMessage: "Choose Values", + description: "product attribute values, page title" + }); + case ProductVariantCreatorStep.prices: + return intl.formatMessage({ + defaultMessage: "Price and SKUs", + description: "page title" + }); + case ProductVariantCreatorStep.summary: + return intl.formatMessage({ + defaultMessage: "Summary", + description: "page title" + }); + } +} + +function getDescription( + step: ProductVariantCreatorStep, + intl: IntlShape +): string { + switch (step) { + case ProductVariantCreatorStep.values: + return intl.formatMessage({ + defaultMessage: + "Selected values will be used to create variants for the configurable product." + }); + case ProductVariantCreatorStep.prices: + return intl.formatMessage({ + defaultMessage: + "Based on your selections we will create 8 products. Use this step to customize price and stocks for your new products." + }); + case ProductVariantCreatorStep.summary: + return intl.formatMessage({ + defaultMessage: + "Here is the summary of variants that will be created. You can change prices, stocks an SKU for each one created." + }); + } +} + +const ProductVariantCreatePage: React.FC = props => { + const { + attributes, + defaultPrice, + errors, + onSubmit, + warehouses, + ...contentProps + } = props; + const classes = useStyles(props); + const intl = useIntl(); + const [wizardData, dispatchFormDataAction] = React.useReducer( + reduceProductVariantCreateFormData, + createInitialForm(attributes, defaultPrice, warehouses) + ); + const [step, { next: nextStep, prev: prevStep, set: setStep }] = useWizard< + ProductVariantCreatorStep + >( + ProductVariantCreatorStep.values, + [ + ProductVariantCreatorStep.values, + ProductVariantCreatorStep.prices, + ProductVariantCreatorStep.summary + ], + { + onTransition: (_, nextStep) => { + if (nextStep === ProductVariantCreatorStep.summary) { + dispatchFormDataAction({ + type: ProductVariantCreateReducerActionType.reload + }); + } + } + } + ); + const reloadForm = () => + dispatchFormDataAction({ + reload: { + data: createInitialForm(attributes, defaultPrice, warehouses) + }, + type: ProductVariantCreateReducerActionType.reload + }); + + React.useEffect(reloadForm, [attributes.length, warehouses.length]); + + return ( + + + + {getTitle(step, intl)} + + {getDescription(step, intl)} + + + } + > + {step !== ProductVariantCreatorStep.values && ( + + )} + {step !== ProductVariantCreatorStep.summary ? ( + + ) : ( + + )} + +
+ +
+ ); +}; + +ProductVariantCreatePage.displayName = "ProductVariantCreatePage"; +export default ProductVariantCreatePage; diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPriceAndSku.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPriceAndSku.tsx new file mode 100644 index 000000000..52ccf460c --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPriceAndSku.tsx @@ -0,0 +1,74 @@ +import React from "react"; + +import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import CardSpacer from "@saleor/components/CardSpacer"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import { + ProductVariantCreateFormData, + VariantCreatorPricesAndSkuMode +} from "./form"; +import ProductVariantCreatorPrices from "./ProductVariantCreatorPrices"; +import ProductVariantCreatorStock from "./ProductVariantCreatorStock"; + +export type PriceOrStock = "price" | "stock"; +export interface ProductVariantCreatorPriceAndSkuProps { + attributes: ProductDetails_product_productType_variantAttributes[]; + currencySymbol: string; + data: ProductVariantCreateFormData; + warehouses: WarehouseFragment[]; + onApplyToAllChange: ( + value: VariantCreatorPricesAndSkuMode, + type: PriceOrStock + ) => void; + onApplyToAllPriceChange: (value: string) => void; + onApplyToAllStockChange: (quantity: number, warehouseIndex: number) => void; + onAttributeSelect: (id: string, type: PriceOrStock) => void; + onAttributePriceChange: (id: string, value: string) => void; + onAttributeStockChange: ( + id: string, + quantity: number, + warehouseIndex: number + ) => void; + onWarehouseToggle: (id: string) => void; +} + +const ProductVariantCreatorPriceAndSku: React.FC = ({ + attributes, + currencySymbol, + data, + warehouses, + onApplyToAllChange, + onApplyToAllPriceChange, + onApplyToAllStockChange, + onAttributeSelect, + onAttributePriceChange, + onAttributeStockChange, + onWarehouseToggle +}) => ( + <> + onApplyToAllChange(value, "price")} + onApplyToAllPriceChange={onApplyToAllPriceChange} + onAttributeSelect={id => onAttributeSelect(id, "price")} + onAttributeValueChange={onAttributePriceChange} + /> + + onApplyToAllChange(value, "stock")} + onApplyToAllStockChange={onApplyToAllStockChange} + onAttributeSelect={id => onAttributeSelect(id, "stock")} + onAttributeValueChange={onAttributeStockChange} + onWarehouseToggle={onWarehouseToggle} + /> + +); + +ProductVariantCreatorPriceAndSku.displayName = + "ProductVariantCreatorPriceAndSku"; +export default ProductVariantCreatorPriceAndSku; diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPrices.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPrices.tsx new file mode 100644 index 000000000..21ddd1938 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorPrices.tsx @@ -0,0 +1,189 @@ +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import { makeStyles } from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import FormSpacer from "@saleor/components/FormSpacer"; +import Grid from "@saleor/components/Grid"; +import Hr from "@saleor/components/Hr"; +import SingleSelectField from "@saleor/components/SingleSelectField"; +import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import CardTitle from "@saleor/components/CardTitle"; +import { + ProductVariantCreateFormData, + VariantCreatorPricesAndSkuMode +} from "./form"; +import { getPriceAttributeValues } from "./utils"; + +const useStyles = makeStyles( + theme => ({ + hr: { + marginBottom: theme.spacing(), + marginTop: theme.spacing(0.5) + }, + hrAttribute: { + marginTop: theme.spacing(2) + }, + label: { + alignSelf: "center" + }, + shortInput: { + width: "33%" + } + }), + { name: "ProductVariantCreatorPrices" } +); + +export interface ProductVariantCreatorPricesProps { + attributes: ProductDetails_product_productType_variantAttributes[]; + currencySymbol: string; + data: ProductVariantCreateFormData; + onApplyToAllChange: (applyToAll: VariantCreatorPricesAndSkuMode) => void; + onApplyToAllPriceChange: (value: string) => void; + onAttributeSelect: (id: string) => void; + onAttributeValueChange: (id: string, value: string) => void; +} + +const ProductVariantCreatorPrices: React.FC = props => { + const { + attributes, + currencySymbol, + data, + onApplyToAllChange, + onApplyToAllPriceChange, + onAttributeSelect, + onAttributeValueChange + } = props; + const classes = useStyles(props); + const intl = useIntl(); + + const attributeChoices = attributes.map(attribute => ({ + label: attribute.name, + value: attribute.id + })); + const priceAttributeValues = getPriceAttributeValues(data, attributes); + + return ( + + + + + } + label={intl.formatMessage({ + defaultMessage: "Apply single price to all SKUs" + })} + onChange={() => onApplyToAllChange("all")} + /> + + onApplyToAllPriceChange(event.target.value)} + /> + + } + label={intl.formatMessage({ + defaultMessage: "Apply unique prices by attribute to each SKU" + })} + onChange={() => onApplyToAllChange("attribute")} + /> + + {data.price.mode === "attribute" && ( + <> + + +
+ + + +
+
+ onAttributeSelect(event.target.value)} + /> +
+
+ {priceAttributeValues && + priceAttributeValues.map(attributeValue => ( + +
+ + +
+ {attributeValue.name} +
+
+ value.slug === attributeValue.slug + ).value + } + onChange={event => + onAttributeValueChange( + attributeValue.slug, + event.target.value + ) + } + /> +
+
+
+ ))} + + )} +
+
+ ); +}; + +ProductVariantCreatorPrices.displayName = "ProductVariantCreatorPrices"; +export default ProductVariantCreatorPrices; diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx new file mode 100644 index 000000000..b087073a9 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx @@ -0,0 +1,318 @@ +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import { makeStyles } from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import FormSpacer from "@saleor/components/FormSpacer"; +import Hr from "@saleor/components/Hr"; +import SingleSelectField from "@saleor/components/SingleSelectField"; +import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import CardTitle from "@saleor/components/CardTitle"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import CardSpacer from "@saleor/components/CardSpacer"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import { isSelected } from "@saleor/utils/lists"; +import { + ProductVariantCreateFormData, + VariantCreatorPricesAndSkuMode +} from "./form"; +import { getStockAttributeValues } from "./utils"; + +const useStyles = makeStyles( + theme => ({ + attributeStockContainer: { + columnGap: theme.spacing(3) + "px", + display: "grid", + gridTemplateColumns: ({ data }: ProductVariantCreatorStockProps) => + `150px repeat(${data.warehouses.length}, 288px)`, + rowGap: theme.spacing(2) + "px" + }, + attributeStockScroll: { + overflowX: "scroll", + width: "100%" + }, + hr: { + marginBottom: theme.spacing(), + marginTop: theme.spacing(0.5) + }, + hrAttribute: { + marginTop: theme.spacing(2) + }, + label: { + alignSelf: "center" + }, + shortInput: { + width: "33%" + }, + stockContainer: { + columnGap: theme.spacing(3) + "px", + display: "grid", + gridTemplateColumns: "repeat(3, 288px)", + marginTop: theme.spacing(2), + rowGap: theme.spacing(2) + "px" + }, + stockHeader: { + marginBottom: theme.spacing() + }, + warehouseContainer: { + columnGap: theme.spacing(3) + "px", + display: "grid", + gridTemplateColumns: "repeat(4, 1fr)", + rowGap: theme.spacing(2) + "px" + }, + warehouseHeader: { + marginBottom: theme.spacing() + }, + warehouseName: { + marginBottom: theme.spacing() + }, + warehouseSubheader: { + marginBottom: theme.spacing(2) + } + }), + { name: "ProductVariantCreatorStock" } +); + +export interface ProductVariantCreatorStockProps { + attributes: ProductDetails_product_productType_variantAttributes[]; + data: ProductVariantCreateFormData; + warehouses: WarehouseFragment[]; + onApplyToAllChange: (mode: VariantCreatorPricesAndSkuMode) => void; + onApplyToAllStockChange: (quantity: number, warehouseIndex: number) => void; + onAttributeSelect: (id: string) => void; + onAttributeValueChange: ( + id: string, + quantity: number, + warehouseIndex: number + ) => void; + onWarehouseToggle: (id: string) => void; +} + +const ProductVariantCreatorStock: React.FC = props => { + const { + attributes, + data, + warehouses, + onApplyToAllChange, + onApplyToAllStockChange, + onAttributeSelect, + onAttributeValueChange, + onWarehouseToggle + } = props; + const classes = useStyles(props); + const intl = useIntl(); + + const attributeChoices = attributes.map(attribute => ({ + label: attribute.name, + value: attribute.id + })); + const stockAttributeValues = getStockAttributeValues(data, attributes); + + return ( + + + + {warehouses.length > 1 && ( + <> + + + + + acc + attr.values.length, + 0 + ) + }} + /> + +
+ {warehouses.map(warehouse => ( + a === b + )} + name={`warehouse:${warehouse.id}`} + label={warehouse.name} + onChange={() => onWarehouseToggle(warehouse.id)} + key={warehouse.id} + /> + ))} +
+ +
+ + + )} + + + + + } + label={intl.formatMessage({ + defaultMessage: "Apply single stock to all SKUs" + })} + onChange={() => onApplyToAllChange("all")} + /> + {data.stock.mode === "all" && ( +
+ {data.warehouses.map((warehouseId, warehouseIndex) => ( +
+ + { + warehouses.find(warehouse => warehouse.id === warehouseId) + .name + } + + + onApplyToAllStockChange( + parseInt(event.target.value, 10), + warehouseIndex + ) + } + /> +
+ ))} +
+ )} + + } + label={intl.formatMessage({ + defaultMessage: "Apply unique stock by attribute to each SKU" + })} + onChange={() => onApplyToAllChange("attribute")} + /> + {data.stock.mode === "attribute" && ( + <> + + onAttributeSelect(event.target.value)} + /> + {stockAttributeValues && ( + <> +
+ +
+
+
+ {data.stock.attribute && + data.warehouses.map(warehouseId => ( + + { + warehouses.find( + warehouse => warehouse.id === warehouseId + ).name + } + + ))} + {stockAttributeValues.map(attributeValue => ( + + {attributeValue.name} + {data.warehouses.map( + (warehouseId, warehouseIndex) => ( + value.slug === attributeValue.slug + ).value[warehouseIndex] + } + onChange={event => + onAttributeValueChange( + attributeValue.slug, + parseInt(event.target.value, 10), + warehouseIndex + ) + } + key={warehouseId} + /> + ) + )} + + ))} +
+
+ + )} + + )} + {data.stock.mode === "attribute" && !!data.stock.attribute && ( + <> + +
+ + )} + + } + label={intl.formatMessage({ + defaultMessage: "Skip stock for now" + })} + onChange={() => onApplyToAllChange("skip")} + /> + + + + ); +}; + +ProductVariantCreatorStock.displayName = "ProductVariantCreatorStock"; +export default ProductVariantCreatorStock; diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx similarity index 59% rename from src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx rename to src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx index 01e370e9e..620c7ea31 100644 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorSummary.tsx @@ -3,62 +3,87 @@ import cyan from "@material-ui/core/colors/cyan"; import green from "@material-ui/core/colors/green"; import purple from "@material-ui/core/colors/purple"; import yellow from "@material-ui/core/colors/yellow"; +import Card from "@material-ui/core/Card"; import IconButton from "@material-ui/core/IconButton"; -import { makeStyles } from "@material-ui/core/styles"; +import { makeStyles, Theme } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import Typography from "@material-ui/core/Typography"; import DeleteIcon from "@material-ui/icons/Delete"; import classNames from "classnames"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import Hr from "@saleor/components/Hr"; import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate"; import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes"; import { getFormErrors } from "@saleor/utils/errors"; import { getBulkProductErrorMessage } from "@saleor/utils/errors/product"; +import CardTitle from "@saleor/components/CardTitle"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; +import Hr from "@saleor/components/Hr"; import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails"; import { ProductVariantCreateFormData } from "./form"; import { VariantField } from "./reducer"; -export interface ProductVariantCreateSummaryProps { +export interface ProductVariantCreatorSummaryProps { attributes: ProductDetails_product_productType_variantAttributes[]; currencySymbol: string; data: ProductVariantCreateFormData; errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[]; + warehouses: WarehouseFragment[]; onVariantDataChange: ( variantIndex: number, field: VariantField, value: string ) => void; + onVariantStockDataChange: ( + variantIndex: number, + warehouseId: string, + value: string + ) => void; onVariantDelete: (variantIndex: number) => void; } +type ClassKey = + | "attributeValue" + | "card" + | "col" + | "colHeader" + | "colName" + | "colPrice" + | "colSku" + | "colStock" + | "delete" + | "hr" + | "input" + | "summary"; const colors = [blue, cyan, green, purple, yellow].map(color => color[800]); -const useStyles = makeStyles( +const useStyles = makeStyles< + Theme, + ProductVariantCreatorSummaryProps, + ClassKey +>( theme => ({ attributeValue: { display: "inline-block", marginRight: theme.spacing(1) }, - col: { - ...theme.typography.body1, - fontSize: 14, - paddingLeft: theme.spacing(), - paddingRight: theme.spacing(1) + card: { + paddingBottom: theme.spacing() }, - colHeader: { + col: { ...theme.typography.body1, fontSize: 14 }, + colHeader: { + ...theme.typography.body1, + fontSize: 14, + paddingTop: theme.spacing(3) + }, colName: { - "&&": { - paddingLeft: "0 !important" - }, "&:not($colHeader)": { paddingTop: theme.spacing(2) - } + }, + paddingLeft: theme.spacing(3) }, colPrice: {}, colSku: {}, @@ -66,26 +91,25 @@ const useStyles = makeStyles( delete: { marginTop: theme.spacing(0.5) }, - errorRow: {}, hr: { - marginBottom: theme.spacing(), - marginTop: theme.spacing(0.5) + gridColumn: props => `span ${4 + props.data.variants[0].stocks.length}` }, input: { "& input": { padding: "16px 12px 17px" - }, - marginTop: theme.spacing(0.5) + } }, - row: { - borderBottom: `1px solid ${theme.palette.divider}`, + summary: { + columnGap: theme.spacing(3), display: "grid", - gridTemplateColumns: "1fr 180px 120px 180px 64px", - padding: theme.spacing(1, 0) + gridTemplateColumns: props => + `minmax(240px, auto) 170px repeat(${props.data.variants[0].stocks.length}, 140px) 140px 64px`, + overflowX: "scroll", + rowGap: theme.spacing() + "px" } }), { - name: "ProductVariantCreateSummary" + name: "ProductVariantCreatorSummary" } ); @@ -108,75 +132,72 @@ function getVariantName( ); } -const ProductVariantCreateSummary: React.FC = props => { +const ProductVariantCreatorSummary: React.FC = props => { const { attributes, currencySymbol, data, errors, + warehouses, onVariantDataChange, - onVariantDelete + onVariantDelete, + onVariantStockDataChange } = props; const classes = useStyles(props); const intl = useIntl(); return ( - <> - - - -
-
-
-
- -
-
- -
+ + +
+
+ +
+
+ +
+ {data.warehouses.map(warehouseId => (
- -
-
- + {warehouses.find(warehouse => warehouse.id === warehouseId).name}
+ ))} +
+
+
+
{data.variants.map((variant, variantIndex) => { const variantErrors = errors.filter( error => error.index === variantIndex @@ -187,10 +208,7 @@ const ProductVariantCreateSummary: React.FC = ); return ( -
0 - })} + attribute.values[0]) .join(":")} @@ -203,7 +221,7 @@ const ProductVariantCreateSummary: React.FC = style={{ color: colors[valueIndex % colors.length] }} - key={value} + key={`${value}:${valueIndex}`} > {value} @@ -236,29 +254,34 @@ const ProductVariantCreateSummary: React.FC = } />
-
- - onVariantDataChange( - variantIndex, - "stock", - event.target.value - ) - } - /> -
+ {variant.stocks.map(stock => ( +
+ + onVariantStockDataChange( + variantIndex, + stock.warehouse, + event.target.value + ) + } + /> +
+ ))}
=
-
+ {variantIndex !== data.variants.length - 1 && ( +
+ )} + ); })}
- +
); }; -ProductVariantCreateSummary.displayName = "ProductVariantCreateSummary"; -export default ProductVariantCreateSummary; +ProductVariantCreatorSummary.displayName = "ProductVariantCreatorSummary"; +export default ProductVariantCreatorSummary; diff --git a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateTabs.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorTabs.tsx similarity index 78% rename from src/products/components/ProductVariantCreateDialog/ProductVariantCreateTabs.tsx rename to src/products/components/ProductVariantCreatorPage/ProductVariantCreatorTabs.tsx index 40f03e03f..49c4ab52a 100644 --- a/src/products/components/ProductVariantCreateDialog/ProductVariantCreateTabs.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorTabs.tsx @@ -4,11 +4,11 @@ import classNames from "classnames"; import React from "react"; import { IntlShape, useIntl } from "react-intl"; -import { ProductVariantCreateStep } from "./types"; +import { ProductVariantCreatorStep } from "./types"; interface Step { label: string; - value: ProductVariantCreateStep; + value: ProductVariantCreatorStep; } function getSteps(intl: IntlShape): Step[] { return [ @@ -17,21 +17,21 @@ function getSteps(intl: IntlShape): Step[] { defaultMessage: "Select Values", description: "attribute values, variant creation step" }), - value: "values" + value: ProductVariantCreatorStep.values }, { label: intl.formatMessage({ defaultMessage: "Prices and SKU", description: "variant creation step" }), - value: "prices" + value: ProductVariantCreatorStep.prices }, { label: intl.formatMessage({ defaultMessage: "Summary", description: "variant creation step" }), - value: "summary" + value: ProductVariantCreatorStep.summary } ]; } @@ -62,18 +62,16 @@ const useStyles = makeStyles( } }), { - name: "ProductVariantCreateTabs" + name: "ProductVariantCreatorTabs" } ); -export interface ProductVariantCreateTabsProps { - step: ProductVariantCreateStep; - onStepClick: (step: ProductVariantCreateStep) => void; +export interface ProductVariantCreatorTabsProps { + step: ProductVariantCreatorStep; + onStepClick: (step: ProductVariantCreatorStep) => void; } -const ProductVariantCreateTabs: React.FC< - ProductVariantCreateTabsProps -> = props => { +const ProductVariantCreatorTabs: React.FC = props => { const { step: currentStep, onStepClick } = props; const classes = useStyles(props); const intl = useIntl(); @@ -104,5 +102,5 @@ const ProductVariantCreateTabs: React.FC< ); }; -ProductVariantCreateTabs.displayName = "ProductVariantCreateTabs"; -export default ProductVariantCreateTabs; +ProductVariantCreatorTabs.displayName = "ProductVariantCreatorTabs"; +export default ProductVariantCreatorTabs; diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx new file mode 100644 index 000000000..b482bbd30 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorValues.tsx @@ -0,0 +1,75 @@ +import makeStyles from "@material-ui/core/styles/makeStyles"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import React from "react"; + +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import Debounce from "@saleor/components/Debounce"; +import Skeleton from "@saleor/components/Skeleton"; +import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import { isSelected } from "@saleor/utils/lists"; +import CardTitle from "@saleor/components/CardTitle"; +import CardSpacer from "@saleor/components/CardSpacer"; +import { ProductVariantCreateFormData } from "./form"; + +export interface ProductVariantCreatorValuesProps { + attributes: ProductDetails_product_productType_variantAttributes[]; + data: ProductVariantCreateFormData; + onValueClick: (attributeId: string, valueId: string) => void; +} + +const useStyles = makeStyles( + theme => ({ + valueContainer: { + display: "grid", + gridColumnGap: theme.spacing(3), + gridTemplateColumns: "repeat(5, 1fr)" + } + }), + { name: "ProductVariantCreatorValues" } +); + +const ProductVariantCreatorValues: React.FC = props => { + const { attributes, data, onValueClick } = props; + const classes = useStyles(props); + + return ( + <> + {attributes.map(attribute => ( + + + } /> + + {attribute.values.map(value => ( + onValueClick(attribute.id, value.slug)} + time={100} + key={value.slug} + > + {change => ( + attribute.id === dataAttribute.id + )?.values || [], + (a, b) => a === b + )} + name={`value:${value.slug}`} + label={value.name} + onChange={change} + /> + )} + + ))} + + + + + ))} + + ); +}; + +ProductVariantCreatorValues.displayName = "ProductVariantCreatorValues"; +export default ProductVariantCreatorValues; diff --git a/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap new file mode 100644 index 000000000..32a195361 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/__snapshots__/reducer.test.ts.snap @@ -0,0 +1,2397 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Reducer is able to modify individual variant price 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": "attr-2", + "mode": "attribute", + "value": "", + "values": Array [ + Object { + "slug": "val-2-2", + "value": "24.99", + }, + Object { + "slug": "val-2-4", + "value": "26.99", + }, + ], + }, + "stock": Object { + "attribute": "attr-4", + "mode": "attribute", + "value": Array [], + "values": Array [ + Object { + "slug": "val-4-1", + "value": Array [ + 50, + 20, + 45, + 75, + ], + }, + Object { + "slug": "val-4-5", + "value": Array [ + 80, + 50, + 85, + 105, + ], + }, + ], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "49.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to modify individual variant stock 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": "attr-2", + "mode": "attribute", + "value": "", + "values": Array [ + Object { + "slug": "val-2-2", + "value": "24.99", + }, + Object { + "slug": "val-2-4", + "value": "26.99", + }, + ], + }, + "stock": Object { + "attribute": "attr-4", + "mode": "attribute", + "value": Array [], + "values": Array [ + Object { + "slug": "val-4-1", + "value": Array [ + 50, + 20, + 45, + 75, + ], + }, + Object { + "slug": "val-4-5", + "value": Array [ + 80, + 50, + 85, + 105, + ], + }, + ], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 5, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "24.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 50, + "warehouse": "wh-1", + }, + Object { + "quantity": 20, + "warehouse": "wh-2", + }, + Object { + "quantity": 45, + "warehouse": "wh-3", + }, + Object { + "quantity": 75, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "26.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 80, + "warehouse": "wh-1", + }, + Object { + "quantity": 50, + "warehouse": "wh-2", + }, + Object { + "quantity": 85, + "warehouse": "wh-3", + }, + Object { + "quantity": 105, + "warehouse": "wh-4", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to select attribute values 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": undefined, + "mode": "all", + "value": "10.99", + "values": Array [], + }, + "stock": Object { + "attribute": undefined, + "mode": "all", + "value": Array [], + "values": Array [], + }, + "variants": Array [], + "warehouses": Array [], +} +`; + +exports[`Reducer is able to select price for all variants 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": undefined, + "mode": "all", + "value": "45.99", + "values": Array [], + }, + "stock": Object { + "attribute": undefined, + "mode": "all", + "value": Array [ + 0, + 0, + 0, + 0, + ], + "values": Array [], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to select price to each attribute value 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": "attr-1", + "mode": "attribute", + "value": "10.99", + "values": Array [ + Object { + "slug": "val-1-1", + "value": "45.99", + }, + Object { + "slug": "val-1-7", + "value": "51.99", + }, + ], + }, + "stock": Object { + "attribute": undefined, + "mode": "all", + "value": Array [ + 0, + 0, + 0, + 0, + ], + "values": Array [], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "45.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "51.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "51.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "51.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "51.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 0, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to select stock for all variants 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": undefined, + "mode": "all", + "value": "10.99", + "values": Array [], + }, + "stock": Object { + "attribute": undefined, + "mode": "all", + "value": Array [ + 0, + 45, + 0, + 0, + ], + "values": Array [], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 0, + "warehouse": "wh-1", + }, + Object { + "quantity": 45, + "warehouse": "wh-2", + }, + Object { + "quantity": 0, + "warehouse": "wh-3", + }, + Object { + "quantity": 0, + "warehouse": "wh-4", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to select stock to each attribute value 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": undefined, + "mode": "all", + "value": "10.99", + "values": Array [], + }, + "stock": Object { + "attribute": "attr-1", + "mode": "attribute", + "value": Array [ + 0, + 0, + 0, + 0, + ], + "values": Array [ + Object { + "slug": "val-1-1", + "value": Array [ + 13, + ], + }, + Object { + "slug": "val-1-7", + "value": Array [ + 19, + ], + }, + ], + }, + "variants": Array [ + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 13, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 13, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 13, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 13, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 19, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 19, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 19, + "warehouse": "wh-1", + }, + ], + }, + Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-5", + ], + }, + ], + "priceOverride": "10.99", + "sku": "", + "stocks": Array [ + Object { + "quantity": 19, + "warehouse": "wh-1", + }, + ], + }, + ], + "warehouses": Array [ + "wh-1", + "wh-2", + "wh-3", + "wh-4", + ], +} +`; + +exports[`Reducer is able to select warehouses in which stock will be created 1`] = ` +Object { + "attributes": Array [ + Object { + "id": "attr-1", + "values": Array [ + "val-1-1", + "val-1-7", + ], + }, + Object { + "id": "attr-2", + "values": Array [ + "val-2-2", + "val-2-4", + ], + }, + Object { + "id": "attr-4", + "values": Array [ + "val-4-1", + "val-4-5", + ], + }, + ], + "price": Object { + "attribute": undefined, + "mode": "all", + "value": "10.99", + "values": Array [], + }, + "stock": Object { + "attribute": undefined, + "mode": "all", + "value": Array [ + 0, + 0, + ], + "values": Array [], + }, + "variants": Array [], + "warehouses": Array [ + "wh-2", + "wh-4", + ], +} +`; diff --git a/src/products/components/ProductVariantCreateDialog/createVariants.test.ts b/src/products/components/ProductVariantCreatorPage/createVariants.test.ts similarity index 80% rename from src/products/components/ProductVariantCreateDialog/createVariants.test.ts rename to src/products/components/ProductVariantCreatorPage/createVariants.test.ts index e41c65229..9fbcd609d 100644 --- a/src/products/components/ProductVariantCreateDialog/createVariants.test.ts +++ b/src/products/components/ProductVariantCreatorPage/createVariants.test.ts @@ -18,19 +18,19 @@ describe("Creates variant matrix", () => { it("with constant price and stock", () => { const price = "49.99"; - const stock = 80; + const stock = [80, 40, 30]; const data: ProductVariantCreateFormData = { ...thirdStep, price: { ...thirdStep.price, - all: true, + mode: "all", value: price }, stock: { ...thirdStep.stock, - all: true, - value: stock.toString() + mode: "all", + value: stock } }; @@ -44,13 +44,15 @@ describe("Creates variant matrix", () => { variants.forEach(variant => { expect(variant.priceOverride).toBe(price); - expect(variant.quantity).toBe(stock); + variant.stocks.forEach((_, stockIndex) => { + expect(variant.stocks[stockIndex].quantity).toBe(stock[stockIndex]); + }); }); }); it("with constant stock and attribute dependent price", () => { const price = 49.99; - const stock = 80; + const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id ); @@ -59,8 +61,8 @@ describe("Creates variant matrix", () => { ...thirdStep, price: { ...thirdStep.price, - all: false, attribute: attribute.id, + mode: "attribute", values: attribute.values.map((attributeValue, attributeValueIndex) => ({ slug: attributeValue, value: (price * (attributeValueIndex + 1)).toString() @@ -68,8 +70,8 @@ describe("Creates variant matrix", () => { }, stock: { ...thirdStep.stock, - all: true, - value: stock.toString() + mode: "all", + value: stock } }; @@ -82,7 +84,9 @@ describe("Creates variant matrix", () => { ); variants.forEach(variant => { - expect(variant.quantity).toBe(stock); + variant.stocks.forEach((_, stockIndex) => { + expect(variant.stocks[stockIndex].quantity).toBe(stock[stockIndex]); + }); }); attribute.values.forEach((attributeValue, attributeValueIndex) => { @@ -103,7 +107,7 @@ describe("Creates variant matrix", () => { it("with constant price and attribute dependent stock", () => { const price = "49.99"; - const stock = 80; + const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id ); @@ -112,16 +116,18 @@ describe("Creates variant matrix", () => { ...thirdStep, price: { ...thirdStep.price, - all: true, + mode: "all", value: price }, stock: { ...thirdStep.stock, - all: false, attribute: attribute.id, + mode: "attribute", values: attribute.values.map((attributeValue, attributeValueIndex) => ({ slug: attributeValue, - value: (stock * (attributeValueIndex + 1)).toString() + value: stock.map( + (_, stockIndex) => stock[stockIndex] * (attributeValueIndex + 1) + ) })) } }; @@ -147,14 +153,18 @@ describe("Creates variant matrix", () => { ).values[0] === attributeValue ) .forEach(variant => { - expect(variant.quantity).toBe(stock * (attributeValueIndex + 1)); + variant.stocks.forEach((_, stockIndex) => { + expect(variant.stocks[stockIndex].quantity).toBe( + stock[stockIndex] * (attributeValueIndex + 1) + ); + }); }); }); }); it("with attribute dependent price and stock", () => { const price = 49.99; - const stock = 80; + const stock = [80, 40, 30]; const attribute = attributes.find( attribute => attribute.id === thirdStep.attributes[0].id ); @@ -163,8 +173,8 @@ describe("Creates variant matrix", () => { ...thirdStep, price: { ...thirdStep.price, - all: false, attribute: attribute.id, + mode: "attribute", values: attribute.values.map((attributeValue, attributeValueIndex) => ({ slug: attributeValue, value: (price * (attributeValueIndex + 1)).toString() @@ -172,11 +182,13 @@ describe("Creates variant matrix", () => { }, stock: { ...thirdStep.stock, - all: false, attribute: attribute.id, + mode: "attribute", values: attribute.values.map((attributeValue, attributeValueIndex) => ({ slug: attributeValue, - value: (stock * (attributeValueIndex + 1)).toString() + value: stock.map( + (_, stockIndex) => stock[stockIndex] * (attributeValueIndex + 1) + ) })) } }; @@ -213,7 +225,11 @@ describe("Creates variant matrix", () => { ).values[0] === attributeValue ) .forEach(variant => { - expect(variant.quantity).toBe(stock * (attributeValueIndex + 1)); + variant.stocks.forEach((_, stockIndex) => { + expect(variant.stocks[stockIndex].quantity).toBe( + stock[stockIndex] * (attributeValueIndex + 1) + ); + }); }); }); }); diff --git a/src/products/components/ProductVariantCreateDialog/createVariants.ts b/src/products/components/ProductVariantCreatorPage/createVariants.ts similarity index 66% rename from src/products/components/ProductVariantCreateDialog/createVariants.ts rename to src/products/components/ProductVariantCreatorPage/createVariants.ts index 6239832e2..231ad44a8 100644 --- a/src/products/components/ProductVariantCreateDialog/createVariants.ts +++ b/src/products/components/ProductVariantCreatorPage/createVariants.ts @@ -11,10 +11,10 @@ interface CreateVariantAttributeValueInput { } type CreateVariantInput = CreateVariantAttributeValueInput[]; -function getAttributeValuePriceOrStock( +function getAttributeValuePriceOrStock( attributes: CreateVariantInput, - priceOrStock: AllOrAttribute -): string { + priceOrStock: AllOrAttribute +): T { const attribute = attributes.find( attribute => attribute.attributeId === priceOrStock.attribute ); @@ -26,18 +26,30 @@ function getAttributeValuePriceOrStock( return attributeValue.value; } +function getValueFromMode( + attributes: CreateVariantInput, + priceOrStock: AllOrAttribute, + skipValue: T +): T { + switch (priceOrStock.mode) { + case "all": + return priceOrStock.value; + case "attribute": + return getAttributeValuePriceOrStock(attributes, priceOrStock); + case "skip": + return skipValue; + } +} + function createVariant( data: ProductVariantCreateFormData, attributes: CreateVariantInput ): ProductVariantBulkCreateInput { - const priceOverride = data.price.all - ? data.price.value - : getAttributeValuePriceOrStock(attributes, data.price); - const quantity = parseInt( - data.stock.all - ? data.stock.value - : getAttributeValuePriceOrStock(attributes, data.stock), - 10 + const priceOverride = getValueFromMode(attributes, data.price, "0"); + const stocks = getValueFromMode( + attributes, + data.stock, + data.warehouses.map(() => 0) ); return { @@ -46,8 +58,11 @@ function createVariant( values: [attribute.attributeValueSlug] })), priceOverride, - quantity, - sku: "" + sku: "", + stocks: stocks.map((quantity, stockIndex) => ({ + quantity, + warehouse: data.warehouses[stockIndex] + })) }; } @@ -92,14 +107,15 @@ export function createVariants( data: ProductVariantCreateFormData ): ProductVariantBulkCreateInput[] { if ( - (!data.price.all && !data.price.attribute) || - (!data.stock.all && !data.stock.attribute) + (data.price.mode === "attribute" && !data.price.attribute) || + (data.stock.mode === "attribute" && !data.stock.attribute) ) { return []; } - const variants = createVariantFlatMatrixDimension([[]], data.attributes).map( - variant => createVariant(data, variant) - ); + const variants = createVariantFlatMatrixDimension( + [[]], + data.attributes + ).map(variant => createVariant(data, variant)); return variants; } diff --git a/src/products/components/ProductVariantCreateDialog/fixtures.ts b/src/products/components/ProductVariantCreatorPage/fixtures.ts similarity index 69% rename from src/products/components/ProductVariantCreateDialog/fixtures.ts rename to src/products/components/ProductVariantCreatorPage/fixtures.ts index 5cc1b3ce2..41aacbd6b 100644 --- a/src/products/components/ProductVariantCreateDialog/fixtures.ts +++ b/src/products/components/ProductVariantCreatorPage/fixtures.ts @@ -1,3 +1,4 @@ +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; import { createVariants } from "./createVariants"; import { AllOrAttribute, @@ -32,8 +33,31 @@ export const attributes = [ } ]; +export const warehouses: WarehouseFragment[] = [ + { + __typename: "Warehouse", + id: "wh-1", + name: "Warehouse 1" + }, + { + __typename: "Warehouse", + id: "wh-2", + name: "Warehouse 2" + }, + { + __typename: "Warehouse", + id: "wh-3", + name: "Warehouse 3" + }, + { + __typename: "Warehouse", + id: "wh-4", + name: "Warehouse 4" + } +]; + export const secondStep: ProductVariantCreateFormData = { - ...createInitialForm([], "10.99"), + ...createInitialForm([], "10.99", warehouses), attributes: [ { id: attributes[0].id, @@ -65,12 +89,17 @@ export const thirdStep: ProductVariantCreateFormData = { id: attributes[3].id, values: [0, 4].map(index => attributes[3].values[index]) } - ] + ], + stock: { + ...secondStep.stock, + value: warehouses.map(() => 0) + }, + warehouses: warehouses.map(warehouse => warehouse.id) }; -const price: AllOrAttribute = { - all: false, +const price: AllOrAttribute = { attribute: thirdStep.attributes[1].id, + mode: "attribute", value: "", values: [ { @@ -83,18 +112,18 @@ const price: AllOrAttribute = { } ] }; -const stock: AllOrAttribute = { - all: false, +const stock: AllOrAttribute = { attribute: thirdStep.attributes[2].id, - value: "", + mode: "attribute", + value: [], values: [ { slug: thirdStep.attributes[2].values[0], - value: "50" + value: [50, 20, 45, 75] }, { slug: thirdStep.attributes[2].values[1], - value: "35" + value: [80, 50, 85, 105] } ] }; diff --git a/src/products/components/ProductVariantCreateDialog/form.ts b/src/products/components/ProductVariantCreatorPage/form.ts similarity index 55% rename from src/products/components/ProductVariantCreateDialog/form.ts rename to src/products/components/ProductVariantCreatorPage/form.ts index d51f13b5d..db3f2c46d 100644 --- a/src/products/components/ProductVariantCreateDialog/form.ts +++ b/src/products/components/ProductVariantCreatorPage/form.ts @@ -1,15 +1,17 @@ import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails"; +import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment"; import { ProductVariantBulkCreateInput } from "../../../types/globalTypes"; -export interface AttributeValue { +export interface AttributeValue { slug: string; - value: string; + value: T; } -export interface AllOrAttribute { - all: boolean; +export type VariantCreatorPricesAndSkuMode = "all" | "attribute" | "skip"; +export interface AllOrAttribute { + mode: VariantCreatorPricesAndSkuMode; attribute: string; - value: string; - values: AttributeValue[]; + value: T; + values: Array>; } export interface Attribute { id: string; @@ -17,30 +19,33 @@ export interface Attribute { } export interface ProductVariantCreateFormData { attributes: Attribute[]; - price: AllOrAttribute; - stock: AllOrAttribute; + price: AllOrAttribute; + stock: AllOrAttribute; variants: ProductVariantBulkCreateInput[]; + warehouses: string[]; } export const createInitialForm = ( attributes: ProductDetails_product_productType_variantAttributes[], - price: string + price: string, + warehouses: WarehouseFragment[] ): ProductVariantCreateFormData => ({ attributes: attributes.map(attribute => ({ id: attribute.id, values: [] })), price: { - all: true, attribute: undefined, + mode: "all", value: price || "", values: [] }, stock: { - all: true, attribute: undefined, - value: "", + mode: "all", + value: warehouses.length === 1 ? [0] : [], values: [] }, - variants: [] + variants: [], + warehouses: warehouses.length === 1 ? [warehouses[0].id] : [] }); diff --git a/src/products/components/ProductVariantCreatorPage/index.ts b/src/products/components/ProductVariantCreatorPage/index.ts new file mode 100644 index 000000000..5e4e6b074 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/index.ts @@ -0,0 +1,2 @@ +export * from "./ProductVariantCreatorPage"; +export { default } from "./ProductVariantCreatorPage"; diff --git a/src/products/components/ProductVariantCreatorPage/reducer.test.ts b/src/products/components/ProductVariantCreatorPage/reducer.test.ts new file mode 100644 index 000000000..7cab43af8 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/reducer.test.ts @@ -0,0 +1,298 @@ +import { + attributes, + fourthStep, + secondStep, + thirdStep, + warehouses +} from "./fixtures"; +import reducer, { + VariantField, + ProductVariantCreateReducerActionType +} from "./reducer"; + +function execActions( + initialState: TState, + reducer: (state: TState, action: TAction) => TState, + actions: TAction[] +): TState { + return actions.reduce((acc, action) => reducer(acc, action), initialState); +} + +describe("Reducer is able to", () => { + it("select attribute values", () => { + const state = execActions(secondStep, reducer, [ + { + selectValue: { + attributeId: attributes[0].id, + valueId: attributes[0].values[0] + }, + type: ProductVariantCreateReducerActionType.selectValue + }, + { + selectValue: { + attributeId: attributes[0].id, + valueId: attributes[0].values[6] + }, + + type: ProductVariantCreateReducerActionType.selectValue + }, + { + selectValue: { + attributeId: attributes[1].id, + valueId: attributes[1].values[1] + }, + type: ProductVariantCreateReducerActionType.selectValue + }, + { + selectValue: { + attributeId: attributes[1].id, + valueId: attributes[1].values[3] + }, + type: ProductVariantCreateReducerActionType.selectValue + }, + { + selectValue: { + attributeId: attributes[3].id, + valueId: attributes[3].values[0] + }, + type: ProductVariantCreateReducerActionType.selectValue + }, + { + selectValue: { + attributeId: attributes[3].id, + valueId: attributes[3].values[4] + }, + type: ProductVariantCreateReducerActionType.selectValue + } + ]); + + expect(state.attributes[0].values).toHaveLength(2); + expect(state.attributes[1].values).toHaveLength(2); + expect(state.attributes[2].values).toHaveLength(2); + expect(state).toMatchSnapshot(); + }); + + it("select price for all variants", () => { + const price = "45.99"; + const state = execActions(thirdStep, reducer, [ + { + applyPriceOrStockToAll: { + mode: "all" + }, + type: ProductVariantCreateReducerActionType.applyPriceToAll + }, + { + changeApplyPriceToAllValue: { + price + }, + type: ProductVariantCreateReducerActionType.changeApplyPriceToAllValue + }, + { + type: ProductVariantCreateReducerActionType.reload + } + ]); + + expect(state.price.mode).toBe("all"); + expect(state.price.value).toBe(price); + expect(state).toMatchSnapshot(); + }); + + it("select warehouses in which stock will be created", () => { + const state = execActions(thirdStep, reducer, [ + { + changeWarehouses: { + warehouseId: warehouses[0].id + }, + type: ProductVariantCreateReducerActionType.changeWarehouses + }, + { + changeWarehouses: { + warehouseId: warehouses[2].id + }, + type: ProductVariantCreateReducerActionType.changeWarehouses + } + ]); + + expect(state.warehouses).toHaveLength(2); + expect(state).toMatchSnapshot(); + }); + + it("select stock for all variants", () => { + const quantity = 45; + const warehouseIndex = 1; + const state = execActions(thirdStep, reducer, [ + { + applyPriceOrStockToAll: { + mode: "all" + }, + type: ProductVariantCreateReducerActionType.applyStockToAll + }, + { + changeApplyStockToAllValue: { + quantity, + warehouseIndex + }, + type: ProductVariantCreateReducerActionType.changeApplyStockToAllValue + }, + { + type: ProductVariantCreateReducerActionType.reload + } + ]); + + expect(state.stock.mode).toBe("all"); + expect(state.stock.value[warehouseIndex]).toBe(quantity); + expect(state).toMatchSnapshot(); + }); + + it("select price to each attribute value", () => { + const attribute = thirdStep.attributes[0]; + const value = 45.99; + const state = execActions(thirdStep, reducer, [ + { + applyPriceOrStockToAll: { + mode: "attribute" + }, + type: ProductVariantCreateReducerActionType.applyPriceToAll + }, + { + changeApplyPriceOrStockToAttributeId: { + attributeId: attribute.id + }, + type: + ProductVariantCreateReducerActionType.changeApplyPriceToAttributeId + }, + { + changeAttributeValuePrice: { + price: value.toString(), + valueId: attribute.values[0] + }, + type: ProductVariantCreateReducerActionType.changeAttributeValuePrice + }, + { + changeAttributeValuePrice: { + price: (value + 6).toString(), + valueId: attribute.values[1] + }, + type: ProductVariantCreateReducerActionType.changeAttributeValuePrice + }, + { + type: ProductVariantCreateReducerActionType.reload + } + ]); + + expect(state.price.mode).toBe("attribute"); + expect(state.price.values).toHaveLength( + state.attributes.find(attribute => state.price.attribute === attribute.id) + .values.length + ); + expect(state).toMatchSnapshot(); + }); + + it("select stock to each attribute value", () => { + const attribute = thirdStep.attributes[0]; + const quantity = 13; + const state = execActions(thirdStep, reducer, [ + { + applyPriceOrStockToAll: { + mode: "attribute" + }, + type: ProductVariantCreateReducerActionType.applyStockToAll + }, + { + changeApplyPriceOrStockToAttributeId: { + attributeId: attribute.id + }, + type: + ProductVariantCreateReducerActionType.changeApplyStockToAttributeId + }, + { + changeAttributeValueStock: { + quantity, + valueId: attribute.values[0], + warehouseIndex: 0 + }, + type: ProductVariantCreateReducerActionType.changeAttributeValueStock + }, + { + changeAttributeValueStock: { + quantity: quantity + 6, + valueId: attribute.values[1], + warehouseIndex: 0 + }, + type: ProductVariantCreateReducerActionType.changeAttributeValueStock + }, + { + type: ProductVariantCreateReducerActionType.reload + } + ]); + + expect(state.stock.mode).toBe("attribute"); + expect(state.stock.values).toHaveLength( + state.attributes.find(attribute => state.stock.attribute === attribute.id) + .values.length + ); + expect(state).toMatchSnapshot(); + }); + + it("modify individual variant price", () => { + const field: VariantField = "price"; + const value = "49.99"; + const variantIndex = 3; + + const state = execActions(fourthStep, reducer, [ + { + changeVariantData: { + field, + value, + variantIndex + }, + type: ProductVariantCreateReducerActionType.changeVariantData + } + ]); + + expect(state.variants[variantIndex].priceOverride).toBe(value); + expect(state.variants[variantIndex - 1].priceOverride).toBe( + fourthStep.variants[variantIndex - 1].priceOverride + ); + expect(state).toMatchSnapshot(); + }); + + it("modify individual variant stock", () => { + const quantity = 5; + const variantIndex = 3; + + const state = execActions(fourthStep, reducer, [ + { + changeVariantStockData: { + stock: { + quantity, + warehouse: warehouses[0].id + }, + variantIndex + }, + type: ProductVariantCreateReducerActionType.changeVariantStockData + } + ]); + + expect(state.variants[variantIndex].stocks[0].quantity).toBe(quantity); + expect(state.variants[variantIndex - 1].stocks[0].quantity).toBe( + fourthStep.variants[variantIndex - 1].stocks[0].quantity + ); + expect(state).toMatchSnapshot(); + }); + + it("delete variant", () => { + const variantIndex = 3; + + const state = execActions(fourthStep, reducer, [ + { + deleteVariant: { + variantIndex + }, + type: ProductVariantCreateReducerActionType.deleteVariant + } + ]); + + expect(state.variants.length).toBe(fourthStep.variants.length - 1); + }); +}); diff --git a/src/products/components/ProductVariantCreatorPage/reducer.ts b/src/products/components/ProductVariantCreatorPage/reducer.ts new file mode 100644 index 000000000..23b387af7 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/reducer.ts @@ -0,0 +1,464 @@ +import { + add, + remove, + removeAtIndex, + toggle, + updateAtIndex, + update +} from "@saleor/utils/lists"; +import { StockInput } from "@saleor/types/globalTypes"; +import { createVariants } from "./createVariants"; +import { + ProductVariantCreateFormData, + VariantCreatorPricesAndSkuMode +} from "./form"; + +export enum ProductVariantCreateReducerActionType { + applyPriceToAll, + applyPriceToAttribute, + applyStockToAll, + applyStockToAttribute, + changeApplyPriceToAllValue, + changeApplyPriceToAttributeId, + changeApplyStockToAllValue, + changeApplyStockToAttributeId, + changeAttributeValuePrice, + changeAttributeValueStock, + changeVariantData, + changeVariantStockData, + changeWarehouses, + deleteVariant, + reload, + selectValue +} +export type VariantField = "price" | "sku"; +export interface ProductVariantCreateReducerAction { + applyPriceOrStockToAll?: { + mode: VariantCreatorPricesAndSkuMode; + }; + changeApplyPriceToAllValue?: { + price: string; + }; + changeApplyPriceOrStockToAttributeId?: { + attributeId: string; + }; + changeApplyStockToAllValue?: Record<"quantity" | "warehouseIndex", number>; + changeAttributeValuePrice?: Record<"valueId" | "price", string>; + changeAttributeValueStock?: { + valueId: string; + quantity: number; + warehouseIndex: number; + }; + changeVariantData?: { + field: VariantField; + value: string; + variantIndex: number; + }; + changeVariantStockData?: { + stock: StockInput; + variantIndex: number; + }; + changeWarehouses?: { + warehouseId: string; + }; + deleteVariant?: { + variantIndex: number; + }; + reload?: { + data?: ProductVariantCreateFormData; + }; + selectValue?: Record<"attributeId" | "valueId", string>; + type: ProductVariantCreateReducerActionType; +} + +function selectValue( + prevState: ProductVariantCreateFormData, + attributeId: string, + valueSlug: string +): ProductVariantCreateFormData { + const attribute = prevState.attributes.find( + attribute => attribute.id === attributeId + ); + const values = toggle(valueSlug, attribute.values, (a, b) => a === b); + const updatedAttributes = add( + { + id: attributeId, + values + }, + remove(attribute, prevState.attributes, (a, b) => a.id === b.id) + ); + + const priceValues = + prevState.price.attribute === attributeId + ? toggle( + { + slug: valueSlug, + value: "" + }, + prevState.price.values, + (a, b) => a.slug === b.slug + ) + : prevState.price.values; + + const stockValues = + prevState.stock.attribute === attributeId + ? toggle( + { + slug: valueSlug, + value: [] + }, + prevState.stock.values, + (a, b) => a.slug === b.slug + ) + : prevState.stock.values; + + return { + ...prevState, + attributes: updatedAttributes, + price: { + ...prevState.price, + values: priceValues + }, + stock: { + ...prevState.stock, + values: stockValues + } + }; +} + +function applyPriceToAll( + state: ProductVariantCreateFormData, + mode: VariantCreatorPricesAndSkuMode +): ProductVariantCreateFormData { + return { + ...state, + price: { + ...state.price, + mode + } + }; +} + +function applyStockToAll( + state: ProductVariantCreateFormData, + mode: VariantCreatorPricesAndSkuMode +): ProductVariantCreateFormData { + return { + ...state, + stock: { + ...state.stock, + mode + } + }; +} + +function changeAttributeValuePrice( + state: ProductVariantCreateFormData, + attributeValueSlug: string, + price: string +): ProductVariantCreateFormData { + const index = state.price.values.findIndex( + value => value.slug === attributeValueSlug + ); + + if (index === -1) { + throw new Error(`Value with id ${attributeValueSlug} not found`); + } + + const values = updateAtIndex( + { + slug: attributeValueSlug, + value: price + }, + state.price.values, + index + ); + + return { + ...state, + price: { + ...state.price, + values + } + }; +} + +function changeAttributeValueStock( + state: ProductVariantCreateFormData, + attributeValueSlug: string, + quantity: number, + warehouseIndex: number +): ProductVariantCreateFormData { + const index = state.stock.values.findIndex( + value => value.slug === attributeValueSlug + ); + + if (index === -1) { + throw new Error(`Value with id ${attributeValueSlug} not found`); + } + + const values = updateAtIndex( + { + ...state.stock.values[index], + value: updateAtIndex( + quantity, + state.stock.values[index].value, + warehouseIndex + ) + }, + state.stock.values, + index + ); + + return { + ...state, + stock: { + ...state.stock, + values + } + }; +} + +function changeApplyPriceToAttributeId( + state: ProductVariantCreateFormData, + attributeId: string +): ProductVariantCreateFormData { + const attribute = state.attributes.find( + attribute => attribute.id === attributeId + ); + const values = attribute.values.map(slug => ({ + slug, + value: "" + })); + + return { + ...state, + price: { + ...state.price, + attribute: attributeId, + values + } + }; +} + +function changeApplyStockToAttributeId( + state: ProductVariantCreateFormData, + attributeId: string +): ProductVariantCreateFormData { + const attribute = state.attributes.find( + attribute => attribute.id === attributeId + ); + const values = attribute.values.map(slug => ({ + slug, + value: [] + })); + + return { + ...state, + stock: { + ...state.stock, + attribute: attributeId, + values + } + }; +} + +function changeApplyPriceToAllValue( + state: ProductVariantCreateFormData, + value: string +): ProductVariantCreateFormData { + return { + ...state, + price: { + ...state.price, + value + } + }; +} + +function changeApplyStockToAllValue( + state: ProductVariantCreateFormData, + warehouseIndex: number, + quantity: number +): ProductVariantCreateFormData { + return { + ...state, + stock: { + ...state.stock, + value: updateAtIndex(quantity, state.stock.value, warehouseIndex) + } + }; +} + +function changeVariantData( + state: ProductVariantCreateFormData, + field: VariantField, + value: string, + variantIndex: number +): ProductVariantCreateFormData { + const variant = { + ...state.variants[variantIndex] + }; + if (field === "price") { + variant.priceOverride = value; + } else if (field === "sku") { + variant.sku = value; + } + + return { + ...state, + variants: updateAtIndex(variant, state.variants, variantIndex) + }; +} + +function changeVariantStockData( + state: ProductVariantCreateFormData, + stock: StockInput, + variantIndex: number +): ProductVariantCreateFormData { + const variant = { + ...state.variants[variantIndex] + }; + variant.stocks = update( + stock, + variant.stocks, + (a, b) => a.warehouse === b.warehouse + ); + + return { + ...state, + variants: updateAtIndex(variant, state.variants, variantIndex) + }; +} + +function changeWarehouses( + state: ProductVariantCreateFormData, + warehouseId: string +): ProductVariantCreateFormData { + const warehouses = toggle(warehouseId, state.warehouses, (a, b) => a === b); + const added = warehouses.length > state.warehouses.length; + + if (added) { + return { + ...state, + stock: { + ...state.stock, + value: [...state.stock.value, 0], + values: state.stock.values.map(stockValue => ({ + ...stockValue, + value: [...stockValue.value, 0] + })) + }, + warehouses + }; + } + + const warehouseIndex = state.warehouses.indexOf(warehouseId); + + return { + ...state, + stock: { + ...state.stock, + value: removeAtIndex(state.stock.value, warehouseIndex), + values: state.stock.values.map(stockValue => ({ + ...stockValue, + value: removeAtIndex(stockValue.value, warehouseIndex) + })) + }, + warehouses + }; +} + +function deleteVariant( + state: ProductVariantCreateFormData, + variantIndex: number +): ProductVariantCreateFormData { + return { + ...state, + variants: removeAtIndex(state.variants, variantIndex) + }; +} + +function createVariantMatrix( + state: ProductVariantCreateFormData +): ProductVariantCreateFormData { + return { + ...state, + variants: createVariants(state) + }; +} + +function reduceProductVariantCreateFormData( + prevState: ProductVariantCreateFormData, + action: ProductVariantCreateReducerAction +) { + switch (action.type) { + case ProductVariantCreateReducerActionType.selectValue: + return selectValue( + prevState, + action.selectValue.attributeId, + action.selectValue.valueId + ); + case ProductVariantCreateReducerActionType.applyPriceToAll: + return applyPriceToAll(prevState, action.applyPriceOrStockToAll.mode); + case ProductVariantCreateReducerActionType.applyStockToAll: + return applyStockToAll(prevState, action.applyPriceOrStockToAll.mode); + case ProductVariantCreateReducerActionType.changeAttributeValuePrice: + return changeAttributeValuePrice( + prevState, + action.changeAttributeValuePrice.valueId, + action.changeAttributeValuePrice.price + ); + case ProductVariantCreateReducerActionType.changeAttributeValueStock: + return changeAttributeValueStock( + prevState, + action.changeAttributeValueStock.valueId, + action.changeAttributeValueStock.quantity, + action.changeAttributeValueStock.warehouseIndex + ); + case ProductVariantCreateReducerActionType.changeApplyPriceToAttributeId: + return changeApplyPriceToAttributeId( + prevState, + action.changeApplyPriceOrStockToAttributeId.attributeId + ); + case ProductVariantCreateReducerActionType.changeApplyStockToAttributeId: + return changeApplyStockToAttributeId( + prevState, + action.changeApplyPriceOrStockToAttributeId.attributeId + ); + case ProductVariantCreateReducerActionType.changeApplyPriceToAllValue: + return changeApplyPriceToAllValue( + prevState, + action.changeApplyPriceToAllValue.price + ); + case ProductVariantCreateReducerActionType.changeApplyStockToAllValue: + return changeApplyStockToAllValue( + prevState, + action.changeApplyStockToAllValue.warehouseIndex, + action.changeApplyStockToAllValue.quantity + ); + case ProductVariantCreateReducerActionType.changeVariantData: + return changeVariantData( + prevState, + action.changeVariantData.field, + action.changeVariantData.value, + action.changeVariantData.variantIndex + ); + case ProductVariantCreateReducerActionType.changeVariantStockData: + return changeVariantStockData( + prevState, + action.changeVariantStockData.stock, + action.changeVariantStockData.variantIndex + ); + case ProductVariantCreateReducerActionType.changeWarehouses: + return changeWarehouses(prevState, action.changeWarehouses.warehouseId); + case ProductVariantCreateReducerActionType.deleteVariant: + return deleteVariant(prevState, action.deleteVariant.variantIndex); + case ProductVariantCreateReducerActionType.reload: + return action.reload?.data || createVariantMatrix(prevState); + default: + return prevState; + } +} + +export default reduceProductVariantCreateFormData; diff --git a/src/products/components/ProductVariantCreatorPage/types.ts b/src/products/components/ProductVariantCreatorPage/types.ts new file mode 100644 index 000000000..baab8f3e7 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/types.ts @@ -0,0 +1,5 @@ +export enum ProductVariantCreatorStep { + values, + prices, + summary +} diff --git a/src/products/components/ProductVariantCreatorPage/utils.ts b/src/products/components/ProductVariantCreatorPage/utils.ts new file mode 100644 index 000000000..eae1a5714 --- /dev/null +++ b/src/products/components/ProductVariantCreatorPage/utils.ts @@ -0,0 +1,39 @@ +import { + ProductDetails_product_productType_variantAttributes_values, + ProductDetails_product_productType_variantAttributes +} from "@saleor/products/types/ProductDetails"; +import { ProductVariantCreateFormData } from "./form"; + +export function getPriceAttributeValues( + data: ProductVariantCreateFormData, + attributes: ProductDetails_product_productType_variantAttributes[] +): ProductDetails_product_productType_variantAttributes_values[] { + return data.price.mode === "all" + ? null + : data.price.attribute + ? attributes + .find(attribute => attribute.id === data.price.attribute) + .values.filter(value => + data.attributes + .find(attribute => attribute.id === data.price.attribute) + .values.includes(value.slug) + ) + : []; +} + +export function getStockAttributeValues( + data: ProductVariantCreateFormData, + attributes: ProductDetails_product_productType_variantAttributes[] +): ProductDetails_product_productType_variantAttributes_values[] { + return data.stock.mode === "all" + ? null + : data.stock.attribute + ? attributes + .find(attribute => attribute.id === data.stock.attribute) + .values.filter(value => + data.attributes + .find(attribute => attribute.id === data.stock.attribute) + .values.includes(value.slug) + ) + : []; +} diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 25b91284b..fb12161d2 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -13,7 +13,10 @@ import useFormset, { FormsetData } from "@saleor/hooks/useFormset"; import { VariantUpdate_productVariantUpdate_errors } from "@saleor/products/types/VariantUpdate"; -import { getAttributeInputFromVariant } from "@saleor/products/utils/data"; +import { + getAttributeInputFromVariant, + getStockInputFromVariant +} from "@saleor/products/utils/data"; import { maybe } from "../../../misc"; import { ProductVariant } from "../../types/ProductVariant"; import ProductVariantAttributes, { @@ -23,18 +26,19 @@ import ProductVariantImages from "../ProductVariantImages"; import ProductVariantImageSelectDialog from "../ProductVariantImageSelectDialog"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; -import ProductVariantStock from "../ProductVariantStock"; +import ProductStocks, { ProductStockInput } from "../ProductStocks"; export interface ProductVariantPageFormData { costPrice: string; priceOverride: string; - quantity: string; sku: string; + trackInventory: boolean; } export interface ProductVariantPageSubmitData extends ProductVariantPageFormData { attributes: FormsetData; + stocks: ProductStockInput[]; } interface ProductVariantPageProps { @@ -44,6 +48,7 @@ interface ProductVariantPageProps { loading?: boolean; placeholderImage?: string; header: string; + onWarehousesEdit: () => void; onAdd(); onBack(); onDelete(); @@ -64,15 +69,20 @@ const ProductVariantPage: React.FC = ({ onDelete, onImageSelect, onSubmit, + onWarehousesEdit, onVariantClick }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), [variant] ); + const stockInput = React.useMemo(() => getStockInputFromVariant(variant), [ + variant + ]); const { change: changeAttributeData, data: attributes } = useFormset( attributeInput ); + const { change: changeStockData, data: stocks } = useFormset(stockInput); const [isModalOpened, setModalStatus] = React.useState(false); const toggleModal = () => setModalStatus(!isModalOpened); @@ -92,14 +102,15 @@ const ProductVariantPage: React.FC = ({ const initialForm: ProductVariantPageFormData = { costPrice: maybe(() => variant.costPrice.amount.toString(), ""), priceOverride: maybe(() => variant.priceOverride.amount.toString(), ""), - quantity: maybe(() => variant.quantity.toString(), "0"), - sku: maybe(() => variant.sku, "") + sku: maybe(() => variant.sku, ""), + trackInventory: variant?.trackInventory }; const handleSubmit = (data: ProductVariantPageFormData) => onSubmit({ ...data, - attributes + attributes, + stocks }); return ( @@ -164,15 +175,17 @@ const ProductVariantPage: React.FC = ({ onChange={change} /> - { + triggerChange(); + changeStockData(id, value); + }} + onFormDataChange={change} + onWarehousesEdit={onWarehousesEdit} />
diff --git a/src/products/components/ProductVariantStock/ProductVariantStock.tsx b/src/products/components/ProductVariantStock/ProductVariantStock.tsx deleted file mode 100644 index fe8b52776..000000000 --- a/src/products/components/ProductVariantStock/ProductVariantStock.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import { makeStyles } from "@material-ui/core/styles"; -import TextField from "@material-ui/core/TextField"; -import React from "react"; -import { useIntl } from "react-intl"; - -import CardTitle from "@saleor/components/CardTitle"; -import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; -import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; - -const useStyles = makeStyles( - theme => ({ - grid: { - display: "grid", - gridColumnGap: theme.spacing(2), - gridTemplateColumns: "1fr 1fr" - } - }), - { name: "ProductVariantStock" } -); - -interface ProductVariantStockProps { - errors: ProductErrorFragment[]; - sku: string; - quantity: string; - stockAllocated?: number; - loading?: boolean; - onChange(event: any); -} - -const ProductVariantStock: React.FC = props => { - const { errors, sku, quantity, stockAllocated, loading, onChange } = props; - - const classes = useStyles(props); - const intl = useIntl(); - - const formErrors = getFormErrors(["quantity", "sku"], errors); - - return ( - - - -
-
- -
-
- -
-
-
-
- ); -}; -ProductVariantStock.displayName = "ProductVariantStock"; -export default ProductVariantStock; diff --git a/src/products/components/ProductVariantStock/index.ts b/src/products/components/ProductVariantStock/index.ts deleted file mode 100644 index 0cb07fabd..000000000 --- a/src/products/components/ProductVariantStock/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./ProductVariantStock"; -export * from "./ProductVariantStock"; diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 6e46489a9..da53ce059 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -8,34 +8,76 @@ import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedMessage, useIntl, IntlShape } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; 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 TableHead from "@saleor/components/TableHead"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import LinkChoice from "@saleor/components/LinkChoice"; import { maybe, renderCollection } from "../../../misc"; import { ListActions } from "../../../types"; -import { ProductDetails_product_variants } from "../../types/ProductDetails"; +import { + ProductDetails_product_variants, + ProductDetails_product_variants_stocks_warehouse +} from "../../types/ProductDetails"; import { ProductVariant_costPrice } from "../../types/ProductVariant"; +function getWarehouseChoices( + variants: ProductDetails_product_variants[], + intl: IntlShape +): SingleAutocompleteChoiceType[] { + return [ + { + label: intl.formatMessage({ + defaultMessage: "All Warehouses", + description: "filtering option" + }), + value: null + }, + ...variants + .reduce( + (warehouses, variant) => [ + ...warehouses, + ...variant.stocks.reduce< + ProductDetails_product_variants_stocks_warehouse[] + >((variantStocks, stock) => { + if (!!warehouses.find(w => w.id === stock.warehouse.id)) { + return variantStocks; + } + + return [...variantStocks, stock.warehouse]; + }, []) + ], + [] + ) + .map(w => ({ + label: w.name, + value: w.id + })) + ]; +} + const useStyles = makeStyles( theme => ({ [theme.breakpoints.up("lg")]: { + colInventory: { + width: 300 + }, colName: {}, colPrice: { - width: 200 + width: 150 }, colSku: { - width: 250 - }, - colStatus: { width: 200 } }, + colInventory: { + textAlign: "right" + }, colName: {}, colPrice: { textAlign: "right" @@ -50,16 +92,81 @@ const useStyles = makeStyles( link: { cursor: "pointer" }, + select: { + display: "inline-block" + }, textLeft: { textAlign: "left" as "left" }, textRight: { textAlign: "right" as "right" + }, + warehouseLabel: { + display: "inline-block", + marginRight: theme.spacing() + }, + warehouseSelectContainer: { + paddingTop: theme.spacing(2) } }), { name: "ProductVariants" } ); +function getAvailabilityLabel( + intl: IntlShape, + warehouse: string, + variant: ProductDetails_product_variants, + numAvailable: number +): string { + const variantStock = variant.stocks.find(s => s.warehouse.id === warehouse); + + if (!!warehouse) { + if (!!variantStock) { + if (variantStock.quantity > 0) { + return intl.formatMessage( + { + defaultMessage: + "{stockQuantity,plural,other{{stockQuantity} available}}", + description: "product variant inventory" + }, + { + stockQuantity: variantStock.quantity + } + ); + } else { + return intl.formatMessage({ + defaultMessage: "Unavailable", + description: "product variant inventory" + }); + } + } else { + return intl.formatMessage({ + defaultMessage: "Not stocked", + description: "product variant inventory" + }); + } + } else { + if (numAvailable > 0) { + return intl.formatMessage( + { + defaultMessage: + "{numLocations,plural,one{{numAvailable} available at {numLocations} location} other{{numAvailable} available at {numLocations} locations}}", + description: "product variant inventory" + }, + { + numAvailable, + numLocations: variant.stocks.length + } + ); + } else { + return intl.formatMessage({ + defaultMessage: "Unavailable in all locations", + description: "product variant inventory" + }); + } + } +} + interface ProductVariantsProps extends ListActions { disabled: boolean; variants: ProductDetails_product_variants[]; @@ -88,6 +195,7 @@ export const ProductVariants: React.FC = props => { const classes = useStyles(props); const intl = useIntl(); + const [warehouse, setWarehouse] = React.useState(null); const hasVariants = maybe(() => variants.length > 0, true); return ( @@ -125,7 +233,24 @@ export const ProductVariants: React.FC = props => { ) } /> - {!variants.length && ( + + {variants.length > 0 ? ( + + + + + setWarehouse(event.target.value)} + /> + + ) : ( @@ -144,16 +269,10 @@ export const ProductVariants: React.FC = props => { > - - - @@ -165,10 +284,20 @@ export const ProductVariants: React.FC = props => { /> + + + {renderCollection(variants, variant => { const isSelected = variant ? isChecked(variant.id) : false; + const numAvailable = + variant && variant.stocks + ? variant.stocks.reduce((acc, s) => acc + s.quantity, 0) + : null; return ( = props => { {variant ? variant.name || variant.sku : } - variant.stockQuantity > 0 - )} - > - {variant ? ( - 0 ? "success" : "error"} - label={ - variant.stockQuantity > 0 - ? intl.formatMessage({ - defaultMessage: "Available", - description: "product variant status" - }) - : intl.formatMessage({ - defaultMessage: "Unavailable", - description: "product variant status" - }) - } - /> - ) : ( - - )} - {variant ? variant.sku : } @@ -233,6 +336,21 @@ export const ProductVariants: React.FC = props => { )} + + {numAvailable === null ? ( + + ) : ( + getAvailabilityLabel( + intl, + warehouse, + variant, + numAvailable + ) + )} + ); })} diff --git a/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.stories.tsx b/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.stories.tsx new file mode 100644 index 000000000..28f8734d6 --- /dev/null +++ b/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.stories.tsx @@ -0,0 +1,46 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import Decorator from "@saleor/storybook//Decorator"; +import { warehouseList } from "@saleor/warehouses/fixtures"; +import { StockErrorCode } from "@saleor/types/globalTypes"; +import ProductWarehousesDialog, { + ProductWarehousesDialogProps +} from "./ProductWarehousesDialog"; + +const props: ProductWarehousesDialogProps = { + confirmButtonState: "default", + disabled: false, + errors: [], + onClose: () => undefined, + onConfirm: () => undefined, + open: true, + warehouses: warehouseList, + warehousesWithStocks: [warehouseList[0].id, warehouseList[2].id] +}; + +storiesOf("Views / Products / Edit warehouses", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading warehouses", () => ( + + )) + .add("loading confirmation", () => ( + + )) + .add("with error", () => ( + + )); diff --git a/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.tsx b/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.tsx new file mode 100644 index 000000000..0b9192c1d --- /dev/null +++ b/src/products/components/ProductWarehousesDialog/ProductWarehousesDialog.tsx @@ -0,0 +1,152 @@ +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 Typography from "@material-ui/core/Typography"; +import React from "react"; +import { FormattedMessage, useIntl, IntlShape } from "react-intl"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import { diff, DiffData } from "fast-array-diff"; + +import ConfirmButton, { + ConfirmButtonTransitionState +} from "@saleor/components/ConfirmButton"; +import { buttonMessages } from "@saleor/intl"; +import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import Skeleton from "@saleor/components/Skeleton"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import { isSelected, toggle } from "@saleor/utils/lists"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { BulkStockErrorFragment } from "@saleor/products/types/BulkStockErrorFragment"; +import { StockErrorFragment } from "@saleor/products/types/StockErrorFragment"; +import getStockErrorMessage, { + getBulkStockErrorMessage +} from "@saleor/utils/errors/stock"; + +const useStyles = makeStyles( + theme => ({ + dropShadow: { + boxShadow: `0px -5px 10px 0px ${theme.palette.divider}` + }, + errorParagraph: { + paddingTop: 0 + }, + helperText: { + marginBottom: theme.spacing(1) + } + }), + { + name: "ProductWarehousesDialog" + } +); + +export interface ProductWarehousesDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + disabled: boolean; + errors: Array; + open: boolean; + warehouses: SearchWarehouses_search_edges_node[]; + warehousesWithStocks: string[]; + onClose: () => void; + onConfirm: (data: DiffData) => void; +} + +function getErrorMessage( + err: BulkStockErrorFragment | StockErrorFragment, + intl: IntlShape +): string { + switch (err?.__typename) { + case "BulkStockError": + return getBulkStockErrorMessage(err, intl); + default: + return getStockErrorMessage(err, intl); + } +} + +const ProductWarehousesDialog: React.FC = ({ + confirmButtonState, + disabled, + errors, + onClose, + onConfirm, + open, + warehousesWithStocks, + warehouses +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + const [selectedWarehouses, setSelectedWarehouses] = useStateFromProps( + warehousesWithStocks || [] + ); + + const handleConfirm = () => + onConfirm(diff(warehousesWithStocks, selectedWarehouses)); + + return ( + + + + +
+ + + + + {warehouses === undefined ? ( + + ) : ( + warehouses.map(warehouse => ( +
+ a === b + )} + name={`warehouse:${warehouse.id}`} + onChange={() => + setSelectedWarehouses( + toggle( + warehouse.id, + selectedWarehouses, + (a, b) => a === b + ) + ) + } + disabled={disabled} + label={warehouse.name} + /> +
+ )) + )} +
+ {errors.length > 0 && ( + + + {getErrorMessage(errors[0], intl)} + + + )} + + + + + + +
+
+ ); +}; + +ProductWarehousesDialog.displayName = "ProductWarehousesDialog"; +export default ProductWarehousesDialog; diff --git a/src/products/components/ProductWarehousesDialog/index.ts b/src/products/components/ProductWarehousesDialog/index.ts new file mode 100644 index 000000000..233e2f720 --- /dev/null +++ b/src/products/components/ProductWarehousesDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ProductWarehousesDialog"; +export * from "./ProductWarehousesDialog"; diff --git a/src/products/containers/ProductUpdateOperations.tsx b/src/products/containers/ProductUpdateOperations.tsx index d07c6561b..a2e79a952 100644 --- a/src/products/containers/ProductUpdateOperations.tsx +++ b/src/products/containers/ProductUpdateOperations.tsx @@ -7,7 +7,6 @@ import { TypedProductImageCreateMutation, TypedProductImageDeleteMutation, TypedProductUpdateMutation, - TypedProductVariantBulkCreateMutation, TypedProductVariantBulkDeleteMutation, TypedSimpleProductUpdateMutation } from "../mutations"; @@ -26,10 +25,6 @@ import { ProductImageReorderVariables } from "../types/ProductImageReorder"; import { ProductUpdate, ProductUpdateVariables } from "../types/ProductUpdate"; -import { - ProductVariantBulkCreate, - ProductVariantBulkCreateVariables -} from "../types/ProductVariantBulkCreate"; import { ProductVariantBulkDelete, ProductVariantBulkDeleteVariables @@ -43,10 +38,6 @@ import ProductImagesReorderProvider from "./ProductImagesReorder"; interface ProductUpdateOperationsProps { product: ProductDetails_product; children: (props: { - bulkProductVariantCreate: PartialMutationProviderOutput< - ProductVariantBulkCreate, - ProductVariantBulkCreateVariables - >; bulkProductVariantDelete: PartialMutationProviderOutput< ProductVariantBulkDelete, ProductVariantBulkDeleteVariables @@ -76,7 +67,6 @@ interface ProductUpdateOperationsProps { SimpleProductUpdateVariables >; }) => React.ReactNode; - onBulkProductVariantCreate?: (data: ProductVariantBulkCreate) => void; onBulkProductVariantDelete?: (data: ProductVariantBulkDelete) => void; onDelete?: (data: ProductDelete) => void; onImageCreate?: (data: ProductImageCreate) => void; @@ -88,7 +78,6 @@ interface ProductUpdateOperationsProps { const ProductUpdateOperations: React.FC = ({ product, children, - onBulkProductVariantCreate, onBulkProductVariantDelete, onDelete, onImageDelete, @@ -121,40 +110,31 @@ const ProductUpdateOperations: React.FC = ({ - {(...bulkProductVariantDelete) => ( - - {(...bulkProductVariantCreate) => - children({ - bulkProductVariantCreate: getMutationProviderData( - ...bulkProductVariantCreate - ), - bulkProductVariantDelete: getMutationProviderData( - ...bulkProductVariantDelete - ), - createProductImage: getMutationProviderData( - ...createProductImage - ), - deleteProduct: getMutationProviderData( - ...deleteProduct - ), - deleteProductImage: getMutationProviderData( - ...deleteProductImage - ), - reorderProductImages: getMutationProviderData( - ...reorderProductImages - ), - updateProduct: getMutationProviderData( - ...updateProduct - ), - updateSimpleProduct: getMutationProviderData( - ...updateSimpleProduct - ) - }) - } - - )} + {(...bulkProductVariantDelete) => + children({ + bulkProductVariantDelete: getMutationProviderData( + ...bulkProductVariantDelete + ), + createProductImage: getMutationProviderData( + ...createProductImage + ), + deleteProduct: getMutationProviderData( + ...deleteProduct + ), + deleteProductImage: getMutationProviderData( + ...deleteProductImage + ), + reorderProductImages: getMutationProviderData( + ...reorderProductImages + ), + updateProduct: getMutationProviderData( + ...updateProduct + ), + updateSimpleProduct: getMutationProviderData( + ...updateSimpleProduct + ) + }) + } )} diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 5f019f4dc..04eca4b56 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -1,4 +1,5 @@ import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { warehouseList } from "@saleor/warehouses/fixtures"; import { content } from "../storybook/stories/components/RichTextEditor"; import { ProductDetails_product } from "./types/ProductDetails"; import { ProductList_products_edges_node } from "./types/ProductList"; @@ -259,10 +260,22 @@ export const product: ( amount: 678.78, currency: "USD" }, - quantity: 12, - quantityAllocated: 1, sku: "87192-94370", - stockQuantity: 48 + stocks: [ + { + __typename: "Stock", + id: "1", + quantity: 1, + warehouse: warehouseList[0] + }, + { + __typename: "Stock", + id: "2", + quantity: 4, + warehouse: warehouseList[1] + } + ], + trackInventory: true }, { __typename: "ProductVariant", @@ -282,10 +295,16 @@ export const product: ( margin: 7, name: "silver", priceOverride: null, - quantity: 12, - quantityAllocated: 1, sku: "69055-15190", - stockQuantity: 14 + stocks: [ + { + __typename: "Stock", + id: "1", + quantity: 13, + warehouse: warehouseList[0] + } + ], + trackInventory: false } ] }); @@ -1228,9 +1247,30 @@ export const variant = (placeholderImage: string): ProductVariant => ({ } ] }, - quantity: 19, - quantityAllocated: 12, - sku: "1230959124123" + sku: "1230959124123", + stocks: [ + { + __typename: "Stock", + id: "1", + quantity: 1, + warehouse: { + __typename: "Warehouse", + id: "123", + name: "Warehouse 1" + } + }, + { + __typename: "Stock", + id: "2", + quantity: 4, + warehouse: { + __typename: "Warehouse", + id: "1234", + name: "Warehouse 2" + } + } + ], + trackInventory: true }); export const variantImages = (placeholderImage: string) => variant(placeholderImage).images; diff --git a/src/products/index.tsx b/src/products/index.tsx index 9db230308..624a50346 100644 --- a/src/products/index.tsx +++ b/src/products/index.tsx @@ -18,14 +18,18 @@ import { ProductUrlQueryParams, productVariantAddPath, productVariantEditPath, - ProductVariantEditUrlQueryParams + ProductVariantEditUrlQueryParams, + ProductAddUrlQueryParams, + ProductVariantAddUrlQueryParams, + productVariantCreatorPath } from "./urls"; -import ProductCreate from "./views/ProductCreate"; +import ProductCreateComponent from "./views/ProductCreate"; import ProductImageComponent from "./views/ProductImage"; import ProductListComponent from "./views/ProductList"; import ProductUpdateComponent from "./views/ProductUpdate"; import ProductVariantComponent from "./views/ProductVariant"; import ProductVariantCreateComponent from "./views/ProductVariantCreate"; +import ProductVariantCreatorComponent from "./views/ProductVariantCreator"; const ProductList: React.FC> = ({ location }) => { const qs = parseQs(location.search.substr(1)); @@ -86,12 +90,31 @@ const ProductImage: React.FC> = ({ const ProductVariantCreate: React.FC> = ({ match -}) => ( - +}) => { + const qs = parseQs(location.search.substr(1)); + const params: ProductVariantAddUrlQueryParams = qs; + + return ( + + ); +}; + +const ProductVariantCreator: React.FC> = ({ match }) => ( + ); +const ProductCreate: React.FC = ({ location }) => { + const qs = parseQs(location.search.substr(1)); + const params: ProductAddUrlQueryParams = qs; + + return ; +}; + const Component = () => { const intl = useIntl(); @@ -101,6 +124,10 @@ const Component = () => { + (productUpdateMutation); export const simpleProductUpdateMutation = gql` + ${bulkStockErrorFragment} ${productErrorFragment} ${productFragmentDetails} + ${stockErrorFragment} ${fragmentVariant} mutation SimpleProductUpdate( $id: ID! @@ -187,6 +211,9 @@ export const simpleProductUpdateMutation = gql` $productVariantId: ID! $productVariantInput: ProductVariantInput! $seo: SeoInput + $addStocks: [StockInput!]! + $deleteStocks: [ID!]! + $updateStocks: [StockInput!]! ) { productUpdate( id: $id @@ -218,6 +245,39 @@ export const simpleProductUpdateMutation = gql` ...ProductVariant } } + productVariantStocksCreate( + stocks: $addStocks + variantId: $productVariantId + ) { + errors: bulkStockErrors { + ...BulkStockErrorFragment + } + productVariant { + ...ProductVariant + } + } + productVariantStocksDelete( + warehouseIds: $deleteStocks + variantId: $productVariantId + ) { + errors: stockErrors { + ...StockErrorFragment + } + productVariant { + ...ProductVariant + } + } + productVariantStocksUpdate( + stocks: $updateStocks + variantId: $productVariantId + ) { + errors: bulkStockErrors { + ...BulkStockErrorFragment + } + productVariant { + ...ProductVariant + } + } } `; export const TypedSimpleProductUpdateMutation = TypedMutation< @@ -240,8 +300,9 @@ export const productCreateMutation = gql` $basePrice: Decimal $productType: ID! $sku: String - $stockQuantity: Int $seo: SeoInput + $stocks: [StockInput!]! + $trackInventory: Boolean! ) { productCreate( input: { @@ -256,8 +317,9 @@ export const productCreateMutation = gql` basePrice: $basePrice productType: $productType sku: $sku - quantity: $stockQuantity seo: $seo + stocks: $stocks + trackInventory: $trackInventory } ) { errors: productErrors { @@ -293,6 +355,7 @@ export const TypedVariantDeleteMutation = TypedMutation< >(variantDeleteMutation); export const variantUpdateMutation = gql` + ${bulkStockErrorFragment} ${fragmentVariant} ${productErrorFragment} mutation VariantUpdate( @@ -301,8 +364,8 @@ export const variantUpdateMutation = gql` $costPrice: Decimal $priceOverride: Decimal $sku: String - $quantity: Int $trackInventory: Boolean! + $stocks: [StockInput!]! ) { productVariantUpdate( id: $id @@ -311,7 +374,6 @@ export const variantUpdateMutation = gql` costPrice: $costPrice priceOverride: $priceOverride sku: $sku - quantity: $quantity trackInventory: $trackInventory } ) { @@ -322,6 +384,14 @@ export const variantUpdateMutation = gql` ...ProductVariant } } + productVariantStocksUpdate(stocks: $stocks, variantId: $id) { + errors: bulkStockErrors { + ...BulkStockErrorFragment + } + productVariant { + ...ProductVariant + } + } } `; export const TypedVariantUpdateMutation = TypedMutation< @@ -469,7 +539,7 @@ export const ProductVariantBulkCreateMutation = gql` } } `; -export const TypedProductVariantBulkCreateMutation = TypedMutation< +export const useProductVariantBulkCreateMutation = makeMutation< ProductVariantBulkCreate, ProductVariantBulkCreateVariables >(ProductVariantBulkCreateMutation); @@ -488,3 +558,41 @@ export const TypedProductVariantBulkDeleteMutation = TypedMutation< ProductVariantBulkDelete, ProductVariantBulkDeleteVariables >(ProductVariantBulkDeleteMutation); + +const addOrRemoveStocks = gql` + ${bulkStockErrorFragment} + ${stockFragment} + mutation AddOrRemoveStocks( + $variantId: ID! + $add: [StockInput!]! + $remove: [ID!]! + ) { + productVariantStocksCreate(stocks: $add, variantId: $variantId) { + errors: bulkStockErrors { + ...BulkStockErrorFragment + } + productVariant { + id + stocks { + ...StockFragment + } + } + } + productVariantStocksDelete(warehouseIds: $remove, variantId: $variantId) { + errors: stockErrors { + code + field + } + productVariant { + id + stocks { + ...StockFragment + } + } + } + } +`; +export const useAddOrRemoveStocks = makeMutation< + AddOrRemoveStocks, + AddOrRemoveStocksVariables +>(addOrRemoveStocks); diff --git a/src/products/queries.ts b/src/products/queries.ts index dbed486ce..0b43ce676 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -1,6 +1,7 @@ import gql from "graphql-tag"; import makeQuery from "@saleor/hooks/makeQuery"; +import { warehouseFragment } from "@saleor/warehouses/queries"; import { pageInfoFragment, TypedQuery } from "../queries"; import { AvailableInGridAttributes, @@ -27,6 +28,21 @@ import { InitialProductFilterData, InitialProductFilterDataVariables } from "./types/InitialProductFilterData"; +import { + CreateMultipleVariantsData, + CreateMultipleVariantsDataVariables +} from "./types/CreateMultipleVariantsData"; + +export const stockFragment = gql` + fragment StockFragment on Stock { + id + quantity + warehouse { + id + name + } + } +`; export const fragmentMoney = gql` fragment Money on Money { @@ -63,11 +79,51 @@ export const productFragment = gql` } } `; + +const productVariantAttributesFragment = gql` + fragment ProductVariantAttributesFragment on Product { + id + attributes { + attribute { + id + slug + name + inputType + valueRequired + values { + id + name + slug + } + } + values { + id + name + slug + } + } + productType { + id + variantAttributes { + id + name + values { + id + name + slug + } + } + } + } +`; + export const productFragmentDetails = gql` ${fragmentProductImage} ${fragmentMoney} + ${productVariantAttributesFragment} + ${stockFragment} fragment Product on Product { - id + ...ProductVariantAttributesFragment name descriptionJson seoTitle @@ -99,25 +155,6 @@ export const productFragmentDetails = gql` isPublished chargeTaxes publicationDate - attributes { - attribute { - id - slug - name - inputType - valueRequired - values { - id - name - slug - } - } - values { - id - name - slug - } - } pricing { priceRange { start { @@ -143,22 +180,23 @@ export const productFragmentDetails = gql` ...Money } margin - quantity - quantityAllocated - stockQuantity + stocks { + ...StockFragment + } + trackInventory } productType { id name hasVariants } - url } `; export const fragmentVariant = gql` ${fragmentMoney} ${fragmentProductImage} + ${stockFragment} fragment ProductVariant on ProductVariant { id attributes { @@ -210,8 +248,10 @@ export const fragmentVariant = gql` } } sku - quantity - quantityAllocated + stocks { + ...StockFragment + } + trackInventory } `; @@ -317,17 +357,6 @@ const productDetailsQuery = gql` query ProductDetails($id: ID!) { product(id: $id) { ...Product - productType { - variantAttributes { - id - name - values { - id - name - slug - } - } - } } } `; @@ -449,3 +478,28 @@ export const AvailableInGridAttributesQuery = TypedQuery< AvailableInGridAttributes, AvailableInGridAttributesVariables >(availableInGridAttributes); + +const createMultipleVariantsData = gql` + ${fragmentMoney} + ${productVariantAttributesFragment} + ${warehouseFragment} + query CreateMultipleVariantsData($id: ID!) { + product(id: $id) { + ...ProductVariantAttributesFragment + basePrice { + ...Money + } + } + warehouses(first: 20) { + edges { + node { + ...WarehouseFragment + } + } + } + } +`; +export const useCreateMultipleVariantsData = makeQuery< + CreateMultipleVariantsData, + CreateMultipleVariantsDataVariables +>(createMultipleVariantsData); diff --git a/src/products/types/AddOrRemoveStocks.ts b/src/products/types/AddOrRemoveStocks.ts new file mode 100644 index 000000000..723501871 --- /dev/null +++ b/src/products/types/AddOrRemoveStocks.ts @@ -0,0 +1,83 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { StockInput, ProductErrorCode, StockErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AddOrRemoveStocks +// ==================================================== + +export interface AddOrRemoveStocks_productVariantStocksCreate_errors { + __typename: "BulkStockError"; + code: ProductErrorCode; + field: string | null; + index: number | null; +} + +export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks_warehouse; +} + +export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant { + __typename: "ProductVariant"; + id: string; + stocks: (AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks | null)[] | null; +} + +export interface AddOrRemoveStocks_productVariantStocksCreate { + __typename: "ProductVariantStocksCreate"; + errors: AddOrRemoveStocks_productVariantStocksCreate_errors[]; + productVariant: AddOrRemoveStocks_productVariantStocksCreate_productVariant | null; +} + +export interface AddOrRemoveStocks_productVariantStocksDelete_errors { + __typename: "StockError"; + code: StockErrorCode; + field: string | null; +} + +export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks_warehouse; +} + +export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant { + __typename: "ProductVariant"; + id: string; + stocks: (AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks | null)[] | null; +} + +export interface AddOrRemoveStocks_productVariantStocksDelete { + __typename: "ProductVariantStocksDelete"; + errors: AddOrRemoveStocks_productVariantStocksDelete_errors[]; + productVariant: AddOrRemoveStocks_productVariantStocksDelete_productVariant | null; +} + +export interface AddOrRemoveStocks { + productVariantStocksCreate: AddOrRemoveStocks_productVariantStocksCreate | null; + productVariantStocksDelete: AddOrRemoveStocks_productVariantStocksDelete | null; +} + +export interface AddOrRemoveStocksVariables { + variantId: string; + add: StockInput[]; + remove: string[]; +} diff --git a/src/products/types/BulkStockErrorFragment.ts b/src/products/types/BulkStockErrorFragment.ts new file mode 100644 index 000000000..9333796c8 --- /dev/null +++ b/src/products/types/BulkStockErrorFragment.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: BulkStockErrorFragment +// ==================================================== + +export interface BulkStockErrorFragment { + __typename: "BulkStockError"; + code: ProductErrorCode; + field: string | null; + index: number | null; +} diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts new file mode 100644 index 000000000..086d657b7 --- /dev/null +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -0,0 +1,98 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL query operation: CreateMultipleVariantsData +// ==================================================== + +export interface CreateMultipleVariantsData_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface CreateMultipleVariantsData_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (CreateMultipleVariantsData_product_attributes_attribute_values | null)[] | null; +} + +export interface CreateMultipleVariantsData_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface CreateMultipleVariantsData_product_attributes { + __typename: "SelectedAttribute"; + attribute: CreateMultipleVariantsData_product_attributes_attribute; + values: (CreateMultipleVariantsData_product_attributes_values | null)[]; +} + +export interface CreateMultipleVariantsData_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface CreateMultipleVariantsData_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (CreateMultipleVariantsData_product_productType_variantAttributes_values | null)[] | null; +} + +export interface CreateMultipleVariantsData_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (CreateMultipleVariantsData_product_productType_variantAttributes | null)[] | null; +} + +export interface CreateMultipleVariantsData_product_basePrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface CreateMultipleVariantsData_product { + __typename: "Product"; + id: string; + attributes: CreateMultipleVariantsData_product_attributes[]; + productType: CreateMultipleVariantsData_product_productType; + basePrice: CreateMultipleVariantsData_product_basePrice | null; +} + +export interface CreateMultipleVariantsData_warehouses_edges_node { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface CreateMultipleVariantsData_warehouses_edges { + __typename: "WarehouseCountableEdge"; + node: CreateMultipleVariantsData_warehouses_edges_node; +} + +export interface CreateMultipleVariantsData_warehouses { + __typename: "WarehouseCountableConnection"; + edges: CreateMultipleVariantsData_warehouses_edges[]; +} + +export interface CreateMultipleVariantsData { + product: CreateMultipleVariantsData_product | null; + warehouses: CreateMultipleVariantsData_warehouses | null; +} + +export interface CreateMultipleVariantsDataVariables { + id: string; +} diff --git a/src/products/types/Product.ts b/src/products/types/Product.ts index 1b7426fc4..a0420a20a 100644 --- a/src/products/types/Product.ts +++ b/src/products/types/Product.ts @@ -8,6 +8,58 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL fragment: Product // ==================================================== +export interface Product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface Product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (Product_attributes_attribute_values | null)[] | null; +} + +export interface Product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface Product_attributes { + __typename: "SelectedAttribute"; + attribute: Product_attributes_attribute; + values: (Product_attributes_values | null)[]; +} + +export interface Product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface Product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (Product_productType_variantAttributes_values | null)[] | null; +} + +export interface Product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (Product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface Product_category { __typename: "Category"; id: string; @@ -50,36 +102,6 @@ export interface Product_purchaseCost { stop: Product_purchaseCost_stop | null; } -export interface Product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface Product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (Product_attributes_attribute_values | null)[] | null; -} - -export interface Product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface Product_attributes { - __typename: "SelectedAttribute"; - attribute: Product_attributes_attribute; - values: (Product_attributes_values | null)[]; -} - export interface Product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -127,6 +149,19 @@ export interface Product_variants_priceOverride { currency: string; } +export interface Product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface Product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: Product_variants_stocks_warehouse; +} + export interface Product_variants { __typename: "ProductVariant"; id: string; @@ -134,21 +169,15 @@ export interface Product_variants { name: string; priceOverride: Product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface Product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (Product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface Product { __typename: "Product"; id: string; + attributes: Product_attributes[]; + productType: Product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -162,10 +191,7 @@ export interface Product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: Product_attributes[]; pricing: Product_pricing | null; images: (Product_images | null)[] | null; variants: (Product_variants | null)[] | null; - productType: Product_productType; - url: string; } diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index c265fd69f..f85cef9c8 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeValueInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductCreate @@ -14,6 +14,58 @@ export interface ProductCreate_productCreate_errors { field: string | null; } +export interface ProductCreate_productCreate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductCreate_productCreate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductCreate_productCreate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductCreate_productCreate_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductCreate_productCreate_product_attributes_attribute; + values: (ProductCreate_productCreate_product_attributes_values | null)[]; +} + +export interface ProductCreate_productCreate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductCreate_productCreate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductCreate_productCreate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductCreate_productCreate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductCreate_productCreate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface ProductCreate_productCreate_product_category { __typename: "Category"; id: string; @@ -56,36 +108,6 @@ export interface ProductCreate_productCreate_product_purchaseCost { stop: ProductCreate_productCreate_product_purchaseCost_stop | null; } -export interface ProductCreate_productCreate_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductCreate_productCreate_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; -} - -export interface ProductCreate_productCreate_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductCreate_productCreate_product_attributes { - __typename: "SelectedAttribute"; - attribute: ProductCreate_productCreate_product_attributes_attribute; - values: (ProductCreate_productCreate_product_attributes_values | null)[]; -} - export interface ProductCreate_productCreate_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -133,6 +155,19 @@ export interface ProductCreate_productCreate_product_variants_priceOverride { currency: string; } +export interface ProductCreate_productCreate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductCreate_productCreate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductCreate_productCreate_product_variants_stocks_warehouse; +} + export interface ProductCreate_productCreate_product_variants { __typename: "ProductVariant"; id: string; @@ -140,21 +175,15 @@ export interface ProductCreate_productCreate_product_variants { name: string; priceOverride: ProductCreate_productCreate_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface ProductCreate_productCreate_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (ProductCreate_productCreate_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductCreate_productCreate_product { __typename: "Product"; id: string; + attributes: ProductCreate_productCreate_product_attributes[]; + productType: ProductCreate_productCreate_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -168,12 +197,9 @@ export interface ProductCreate_productCreate_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: ProductCreate_productCreate_product_attributes[]; pricing: ProductCreate_productCreate_product_pricing | null; images: (ProductCreate_productCreate_product_images | null)[] | null; variants: (ProductCreate_productCreate_product_variants | null)[] | null; - productType: ProductCreate_productCreate_product_productType; - url: string; } export interface ProductCreate_productCreate { @@ -198,6 +224,7 @@ export interface ProductCreateVariables { basePrice?: any | null; productType: string; sku?: string | null; - stockQuantity?: number | null; seo?: SeoInput | null; + stocks: StockInput[]; + trackInventory: boolean; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 2ebf1cfcc..ed22c7264 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -8,6 +8,58 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes"; // GraphQL query operation: ProductDetails // ==================================================== +export interface ProductDetails_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductDetails_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductDetails_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductDetails_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductDetails_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductDetails_product_attributes_attribute; + values: (ProductDetails_product_attributes_values | null)[]; +} + +export interface ProductDetails_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductDetails_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductDetails_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductDetails_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface ProductDetails_product_category { __typename: "Category"; id: string; @@ -50,36 +102,6 @@ export interface ProductDetails_product_purchaseCost { stop: ProductDetails_product_purchaseCost_stop | null; } -export interface ProductDetails_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductDetails_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (ProductDetails_product_attributes_attribute_values | null)[] | null; -} - -export interface ProductDetails_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductDetails_product_attributes { - __typename: "SelectedAttribute"; - attribute: ProductDetails_product_attributes_attribute; - values: (ProductDetails_product_attributes_values | null)[]; -} - export interface ProductDetails_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -127,6 +149,19 @@ export interface ProductDetails_product_variants_priceOverride { currency: string; } +export interface ProductDetails_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductDetails_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductDetails_product_variants_stocks_warehouse; +} + export interface ProductDetails_product_variants { __typename: "ProductVariant"; id: string; @@ -134,36 +169,15 @@ export interface ProductDetails_product_variants { name: string; priceOverride: ProductDetails_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface ProductDetails_product_productType_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductDetails_product_productType_variantAttributes { - __typename: "Attribute"; - id: string; - name: string | null; - values: (ProductDetails_product_productType_variantAttributes_values | null)[] | null; -} - -export interface ProductDetails_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; - variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null; + stocks: (ProductDetails_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductDetails_product { __typename: "Product"; id: string; + attributes: ProductDetails_product_attributes[]; + productType: ProductDetails_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -177,12 +191,9 @@ export interface ProductDetails_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: ProductDetails_product_attributes[]; pricing: ProductDetails_product_pricing | null; images: (ProductDetails_product_images | null)[] | null; variants: (ProductDetails_product_variants | null)[] | null; - productType: ProductDetails_product_productType; - url: string; } export interface ProductDetails { diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index c481ea341..3c1ad318d 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -14,6 +14,58 @@ export interface ProductImageCreate_productImageCreate_errors { field: string | null; } +export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageCreate_productImageCreate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductImageCreate_productImageCreate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageCreate_productImageCreate_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductImageCreate_productImageCreate_product_attributes_attribute; + values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[]; +} + +export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductImageCreate_productImageCreate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductImageCreate_productImageCreate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductImageCreate_productImageCreate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface ProductImageCreate_productImageCreate_product_category { __typename: "Category"; id: string; @@ -56,36 +108,6 @@ export interface ProductImageCreate_productImageCreate_product_purchaseCost { stop: ProductImageCreate_productImageCreate_product_purchaseCost_stop | null; } -export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductImageCreate_productImageCreate_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null; -} - -export interface ProductImageCreate_productImageCreate_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductImageCreate_productImageCreate_product_attributes { - __typename: "SelectedAttribute"; - attribute: ProductImageCreate_productImageCreate_product_attributes_attribute; - values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[]; -} - export interface ProductImageCreate_productImageCreate_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -133,6 +155,19 @@ export interface ProductImageCreate_productImageCreate_product_variants_priceOve currency: string; } +export interface ProductImageCreate_productImageCreate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductImageCreate_productImageCreate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductImageCreate_productImageCreate_product_variants_stocks_warehouse; +} + export interface ProductImageCreate_productImageCreate_product_variants { __typename: "ProductVariant"; id: string; @@ -140,21 +175,15 @@ export interface ProductImageCreate_productImageCreate_product_variants { name: string; priceOverride: ProductImageCreate_productImageCreate_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface ProductImageCreate_productImageCreate_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (ProductImageCreate_productImageCreate_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductImageCreate_productImageCreate_product { __typename: "Product"; id: string; + attributes: ProductImageCreate_productImageCreate_product_attributes[]; + productType: ProductImageCreate_productImageCreate_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -168,12 +197,9 @@ export interface ProductImageCreate_productImageCreate_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: ProductImageCreate_productImageCreate_product_attributes[]; pricing: ProductImageCreate_productImageCreate_product_pricing | null; images: (ProductImageCreate_productImageCreate_product_images | null)[] | null; variants: (ProductImageCreate_productImageCreate_product_variants | null)[] | null; - productType: ProductImageCreate_productImageCreate_product_productType; - url: string; } export interface ProductImageCreate_productImageCreate { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index 23687fe07..becd44e2c 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -14,6 +14,58 @@ export interface ProductImageUpdate_productImageUpdate_errors { field: string | null; } +export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductImageUpdate_productImageUpdate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageUpdate_productImageUpdate_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductImageUpdate_productImageUpdate_product_attributes_attribute; + values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[]; +} + +export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductImageUpdate_productImageUpdate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface ProductImageUpdate_productImageUpdate_product_category { __typename: "Category"; id: string; @@ -56,36 +108,6 @@ export interface ProductImageUpdate_productImageUpdate_product_purchaseCost { stop: ProductImageUpdate_productImageUpdate_product_purchaseCost_stop | null; } -export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null; -} - -export interface ProductImageUpdate_productImageUpdate_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductImageUpdate_productImageUpdate_product_attributes { - __typename: "SelectedAttribute"; - attribute: ProductImageUpdate_productImageUpdate_product_attributes_attribute; - values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[]; -} - export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -133,6 +155,19 @@ export interface ProductImageUpdate_productImageUpdate_product_variants_priceOve currency: string; } +export interface ProductImageUpdate_productImageUpdate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductImageUpdate_productImageUpdate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductImageUpdate_productImageUpdate_product_variants_stocks_warehouse; +} + export interface ProductImageUpdate_productImageUpdate_product_variants { __typename: "ProductVariant"; id: string; @@ -140,21 +175,15 @@ export interface ProductImageUpdate_productImageUpdate_product_variants { name: string; priceOverride: ProductImageUpdate_productImageUpdate_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface ProductImageUpdate_productImageUpdate_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (ProductImageUpdate_productImageUpdate_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductImageUpdate_productImageUpdate_product { __typename: "Product"; id: string; + attributes: ProductImageUpdate_productImageUpdate_product_attributes[]; + productType: ProductImageUpdate_productImageUpdate_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -168,12 +197,9 @@ export interface ProductImageUpdate_productImageUpdate_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: ProductImageUpdate_productImageUpdate_product_attributes[]; pricing: ProductImageUpdate_productImageUpdate_product_pricing | null; images: (ProductImageUpdate_productImageUpdate_product_images | null)[] | null; variants: (ProductImageUpdate_productImageUpdate_product_variants | null)[] | null; - productType: ProductImageUpdate_productImageUpdate_product_productType; - url: string; } export interface ProductImageUpdate_productImageUpdate { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 20c745df8..f9c8c7a7e 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -14,6 +14,58 @@ export interface ProductUpdate_productUpdate_errors { field: string | null; } +export interface ProductUpdate_productUpdate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductUpdate_productUpdate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductUpdate_productUpdate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductUpdate_productUpdate_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductUpdate_productUpdate_product_attributes_attribute; + values: (ProductUpdate_productUpdate_product_attributes_values | null)[]; +} + +export interface ProductUpdate_productUpdate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductUpdate_productUpdate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductUpdate_productUpdate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductUpdate_productUpdate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface ProductUpdate_productUpdate_product_category { __typename: "Category"; id: string; @@ -56,36 +108,6 @@ export interface ProductUpdate_productUpdate_product_purchaseCost { stop: ProductUpdate_productUpdate_product_purchaseCost_stop | null; } -export interface ProductUpdate_productUpdate_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductUpdate_productUpdate_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; -} - -export interface ProductUpdate_productUpdate_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface ProductUpdate_productUpdate_product_attributes { - __typename: "SelectedAttribute"; - attribute: ProductUpdate_productUpdate_product_attributes_attribute; - values: (ProductUpdate_productUpdate_product_attributes_values | null)[]; -} - export interface ProductUpdate_productUpdate_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -133,6 +155,19 @@ export interface ProductUpdate_productUpdate_product_variants_priceOverride { currency: string; } +export interface ProductUpdate_productUpdate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductUpdate_productUpdate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductUpdate_productUpdate_product_variants_stocks_warehouse; +} + export interface ProductUpdate_productUpdate_product_variants { __typename: "ProductVariant"; id: string; @@ -140,21 +175,15 @@ export interface ProductUpdate_productUpdate_product_variants { name: string; priceOverride: ProductUpdate_productUpdate_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface ProductUpdate_productUpdate_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (ProductUpdate_productUpdate_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductUpdate_productUpdate_product { __typename: "Product"; id: string; + attributes: ProductUpdate_productUpdate_product_attributes[]; + productType: ProductUpdate_productUpdate_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -168,12 +197,9 @@ export interface ProductUpdate_productUpdate_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: ProductUpdate_productUpdate_product_attributes[]; pricing: ProductUpdate_productUpdate_product_pricing | null; images: (ProductUpdate_productUpdate_product_images | null)[] | null; variants: (ProductUpdate_productUpdate_product_variants | null)[] | null; - productType: ProductUpdate_productUpdate_product_productType; - url: string; } export interface ProductUpdate_productUpdate { diff --git a/src/products/types/ProductVariant.ts b/src/products/types/ProductVariant.ts index 2d8091000..9a9d2e743 100644 --- a/src/products/types/ProductVariant.ts +++ b/src/products/types/ProductVariant.ts @@ -89,6 +89,19 @@ export interface ProductVariant_product { variants: (ProductVariant_product_variants | null)[] | null; } +export interface ProductVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductVariant_stocks_warehouse; +} + export interface ProductVariant { __typename: "ProductVariant"; id: string; @@ -99,6 +112,6 @@ export interface ProductVariant { priceOverride: ProductVariant_priceOverride | null; product: ProductVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (ProductVariant_stocks | null)[] | null; + trackInventory: boolean; } diff --git a/src/products/types/ProductVariantAttributesFragment.ts b/src/products/types/ProductVariantAttributesFragment.ts new file mode 100644 index 000000000..02d1e7847 --- /dev/null +++ b/src/products/types/ProductVariantAttributesFragment.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ProductVariantAttributesFragment +// ==================================================== + +export interface ProductVariantAttributesFragment_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantAttributesFragment_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariantAttributesFragment_attributes_attribute_values | null)[] | null; +} + +export interface ProductVariantAttributesFragment_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantAttributesFragment_attributes { + __typename: "SelectedAttribute"; + attribute: ProductVariantAttributesFragment_attributes_attribute; + values: (ProductVariantAttributesFragment_attributes_values | null)[]; +} + +export interface ProductVariantAttributesFragment_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantAttributesFragment_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductVariantAttributesFragment_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductVariantAttributesFragment_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductVariantAttributesFragment_productType_variantAttributes | null)[] | null; +} + +export interface ProductVariantAttributesFragment { + __typename: "Product"; + id: string; + attributes: ProductVariantAttributesFragment_attributes[]; + productType: ProductVariantAttributesFragment_productType; +} diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index 0f948a9cc..1bd3a8d52 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -89,6 +89,19 @@ export interface ProductVariantDetails_productVariant_product { variants: (ProductVariantDetails_productVariant_product_variants | null)[] | null; } +export interface ProductVariantDetails_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductVariantDetails_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: ProductVariantDetails_productVariant_stocks_warehouse; +} + export interface ProductVariantDetails_productVariant { __typename: "ProductVariant"; id: string; @@ -99,8 +112,8 @@ export interface ProductVariantDetails_productVariant { priceOverride: ProductVariantDetails_productVariant_priceOverride | null; product: ProductVariantDetails_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (ProductVariantDetails_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface ProductVariantDetails { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 51c33a7e3..a6515ab73 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, ProductVariantInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; +import { AttributeValueInput, ProductVariantInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SimpleProductUpdate @@ -14,6 +14,58 @@ export interface SimpleProductUpdate_productUpdate_errors { field: string | null; } +export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productUpdate_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productUpdate_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productUpdate_product_attributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productUpdate_product_attributes_attribute; + values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[]; +} + +export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values | null)[] | null; +} + +export interface SimpleProductUpdate_productUpdate_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + export interface SimpleProductUpdate_productUpdate_product_category { __typename: "Category"; id: string; @@ -56,36 +108,6 @@ export interface SimpleProductUpdate_productUpdate_product_purchaseCost { stop: SimpleProductUpdate_productUpdate_product_purchaseCost_stop | null; } -export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface SimpleProductUpdate_productUpdate_product_attributes_attribute { - __typename: "Attribute"; - id: string; - slug: string | null; - name: string | null; - inputType: AttributeInputTypeEnum | null; - valueRequired: boolean; - values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; -} - -export interface SimpleProductUpdate_productUpdate_product_attributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; -} - -export interface SimpleProductUpdate_productUpdate_product_attributes { - __typename: "SelectedAttribute"; - attribute: SimpleProductUpdate_productUpdate_product_attributes_attribute; - values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[]; -} - export interface SimpleProductUpdate_productUpdate_product_pricing_priceRange_start_net { __typename: "Money"; amount: number; @@ -133,6 +155,19 @@ export interface SimpleProductUpdate_productUpdate_product_variants_priceOverrid currency: string; } +export interface SimpleProductUpdate_productUpdate_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SimpleProductUpdate_productUpdate_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: SimpleProductUpdate_productUpdate_product_variants_stocks_warehouse; +} + export interface SimpleProductUpdate_productUpdate_product_variants { __typename: "ProductVariant"; id: string; @@ -140,21 +175,15 @@ export interface SimpleProductUpdate_productUpdate_product_variants { name: string; priceOverride: SimpleProductUpdate_productUpdate_product_variants_priceOverride | null; margin: number | null; - quantity: number; - quantityAllocated: number | null; - stockQuantity: number; -} - -export interface SimpleProductUpdate_productUpdate_product_productType { - __typename: "ProductType"; - id: string; - name: string; - hasVariants: boolean; + stocks: (SimpleProductUpdate_productUpdate_product_variants_stocks | null)[] | null; + trackInventory: boolean; } export interface SimpleProductUpdate_productUpdate_product { __typename: "Product"; id: string; + attributes: SimpleProductUpdate_productUpdate_product_attributes[]; + productType: SimpleProductUpdate_productUpdate_product_productType; name: string; descriptionJson: any; seoTitle: string | null; @@ -168,12 +197,9 @@ export interface SimpleProductUpdate_productUpdate_product { isPublished: boolean; chargeTaxes: boolean; publicationDate: any | null; - attributes: SimpleProductUpdate_productUpdate_product_attributes[]; pricing: SimpleProductUpdate_productUpdate_product_pricing | null; images: (SimpleProductUpdate_productUpdate_product_images | null)[] | null; variants: (SimpleProductUpdate_productUpdate_product_variants | null)[] | null; - productType: SimpleProductUpdate_productUpdate_product_productType; - url: string; } export interface SimpleProductUpdate_productUpdate { @@ -271,6 +297,19 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_product variants: (SimpleProductUpdate_productVariantUpdate_productVariant_product_variants | null)[] | null; } +export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: SimpleProductUpdate_productVariantUpdate_productVariant_stocks_warehouse; +} + export interface SimpleProductUpdate_productVariantUpdate_productVariant { __typename: "ProductVariant"; id: string; @@ -281,8 +320,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant { priceOverride: SimpleProductUpdate_productVariantUpdate_productVariant_priceOverride | null; product: SimpleProductUpdate_productVariantUpdate_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (SimpleProductUpdate_productVariantUpdate_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface SimpleProductUpdate_productVariantUpdate { @@ -291,9 +330,380 @@ export interface SimpleProductUpdate_productVariantUpdate { productVariant: SimpleProductUpdate_productVariantUpdate_productVariant | null; } +export interface SimpleProductUpdate_productVariantStocksCreate_errors { + __typename: "BulkStockError"; + code: ProductErrorCode; + field: string | null; + index: number | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes_attribute; + 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_priceOverride { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_thumbnail { + __typename: "Image"; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants_images | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product { + __typename: "Product"; + id: string; + images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_product_images | null)[] | null; + name: string; + thumbnail: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_thumbnail | null; + variants: (SimpleProductUpdate_productVariantStocksCreate_productVariant_product_variants | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks_warehouse; +} + +export interface SimpleProductUpdate_productVariantStocksCreate_productVariant { + __typename: "ProductVariant"; + id: string; + attributes: SimpleProductUpdate_productVariantStocksCreate_productVariant_attributes[]; + costPrice: SimpleProductUpdate_productVariantStocksCreate_productVariant_costPrice | null; + images: (SimpleProductUpdate_productVariantStocksCreate_productVariant_images | null)[] | null; + name: string; + priceOverride: SimpleProductUpdate_productVariantStocksCreate_productVariant_priceOverride | null; + product: SimpleProductUpdate_productVariantStocksCreate_productVariant_product; + sku: string; + stocks: (SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks | null)[] | null; + trackInventory: boolean; +} + +export interface SimpleProductUpdate_productVariantStocksCreate { + __typename: "ProductVariantStocksCreate"; + errors: SimpleProductUpdate_productVariantStocksCreate_errors[]; + productVariant: SimpleProductUpdate_productVariantStocksCreate_productVariant | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_errors { + __typename: "StockError"; + code: StockErrorCode; + field: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes_attribute; + 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_priceOverride { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_thumbnail { + __typename: "Image"; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants_images | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product { + __typename: "Product"; + id: string; + images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_product_images | null)[] | null; + name: string; + thumbnail: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_thumbnail | null; + variants: (SimpleProductUpdate_productVariantStocksDelete_productVariant_product_variants | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks_warehouse; +} + +export interface SimpleProductUpdate_productVariantStocksDelete_productVariant { + __typename: "ProductVariant"; + id: string; + attributes: SimpleProductUpdate_productVariantStocksDelete_productVariant_attributes[]; + costPrice: SimpleProductUpdate_productVariantStocksDelete_productVariant_costPrice | null; + images: (SimpleProductUpdate_productVariantStocksDelete_productVariant_images | null)[] | null; + name: string; + priceOverride: SimpleProductUpdate_productVariantStocksDelete_productVariant_priceOverride | null; + product: SimpleProductUpdate_productVariantStocksDelete_productVariant_product; + sku: string; + stocks: (SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks | null)[] | null; + trackInventory: boolean; +} + +export interface SimpleProductUpdate_productVariantStocksDelete { + __typename: "ProductVariantStocksDelete"; + errors: SimpleProductUpdate_productVariantStocksDelete_errors[]; + productVariant: SimpleProductUpdate_productVariantStocksDelete_productVariant | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_errors { + __typename: "BulkStockError"; + code: ProductErrorCode; + field: string | null; + index: number | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + valueRequired: boolean; + values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes { + __typename: "SelectedAttribute"; + attribute: SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes_attribute; + 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_priceOverride { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_thumbnail { + __typename: "Image"; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants_images | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product { + __typename: "Product"; + id: string; + images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_images | null)[] | null; + name: string; + thumbnail: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_thumbnail | null; + variants: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_variants | null)[] | null; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant { + __typename: "ProductVariant"; + id: string; + attributes: SimpleProductUpdate_productVariantStocksUpdate_productVariant_attributes[]; + costPrice: SimpleProductUpdate_productVariantStocksUpdate_productVariant_costPrice | null; + images: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; + name: string; + priceOverride: SimpleProductUpdate_productVariantStocksUpdate_productVariant_priceOverride | null; + product: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product; + sku: string; + stocks: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null; + trackInventory: boolean; +} + +export interface SimpleProductUpdate_productVariantStocksUpdate { + __typename: "ProductVariantStocksUpdate"; + errors: SimpleProductUpdate_productVariantStocksUpdate_errors[]; + productVariant: SimpleProductUpdate_productVariantStocksUpdate_productVariant | null; +} + export interface SimpleProductUpdate { productUpdate: SimpleProductUpdate_productUpdate | null; productVariantUpdate: SimpleProductUpdate_productVariantUpdate | null; + productVariantStocksCreate: SimpleProductUpdate_productVariantStocksCreate | null; + productVariantStocksDelete: SimpleProductUpdate_productVariantStocksDelete | null; + productVariantStocksUpdate: SimpleProductUpdate_productVariantStocksUpdate | null; } export interface SimpleProductUpdateVariables { @@ -310,4 +720,7 @@ export interface SimpleProductUpdateVariables { productVariantId: string; productVariantInput: ProductVariantInput; seo?: SeoInput | null; + addStocks: StockInput[]; + deleteStocks: string[]; + updateStocks: StockInput[]; } diff --git a/src/products/types/StockErrorFragment.ts b/src/products/types/StockErrorFragment.ts new file mode 100644 index 000000000..18a48980f --- /dev/null +++ b/src/products/types/StockErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { StockErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: StockErrorFragment +// ==================================================== + +export interface StockErrorFragment { + __typename: "StockError"; + code: StockErrorCode; + field: string | null; +} diff --git a/src/products/types/StockFragment.ts b/src/products/types/StockFragment.ts new file mode 100644 index 000000000..bdd9674a3 --- /dev/null +++ b/src/products/types/StockFragment.ts @@ -0,0 +1,20 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: StockFragment +// ==================================================== + +export interface StockFragment_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface StockFragment { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: StockFragment_warehouse; +} diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index da6dd3939..67d363617 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -97,6 +97,19 @@ export interface VariantCreate_productVariantCreate_productVariant_product { variants: (VariantCreate_productVariantCreate_productVariant_product_variants | null)[] | null; } +export interface VariantCreate_productVariantCreate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface VariantCreate_productVariantCreate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: VariantCreate_productVariantCreate_productVariant_stocks_warehouse; +} + export interface VariantCreate_productVariantCreate_productVariant { __typename: "ProductVariant"; id: string; @@ -107,8 +120,8 @@ export interface VariantCreate_productVariantCreate_productVariant { priceOverride: VariantCreate_productVariantCreate_productVariant_priceOverride | null; product: VariantCreate_productVariantCreate_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (VariantCreate_productVariantCreate_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface VariantCreate_productVariantCreate { diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index 5a5c8ad0a..f85d92ee4 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -97,6 +97,19 @@ export interface VariantImageAssign_variantImageAssign_productVariant_product { variants: (VariantImageAssign_variantImageAssign_productVariant_product_variants | null)[] | null; } +export interface VariantImageAssign_variantImageAssign_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface VariantImageAssign_variantImageAssign_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: VariantImageAssign_variantImageAssign_productVariant_stocks_warehouse; +} + export interface VariantImageAssign_variantImageAssign_productVariant { __typename: "ProductVariant"; id: string; @@ -107,8 +120,8 @@ export interface VariantImageAssign_variantImageAssign_productVariant { priceOverride: VariantImageAssign_variantImageAssign_productVariant_priceOverride | null; product: VariantImageAssign_variantImageAssign_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (VariantImageAssign_variantImageAssign_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface VariantImageAssign_variantImageAssign { diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index 4978ee18b..294514c50 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -97,6 +97,19 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_produc variants: (VariantImageUnassign_variantImageUnassign_productVariant_product_variants | null)[] | null; } +export interface VariantImageUnassign_variantImageUnassign_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface VariantImageUnassign_variantImageUnassign_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: VariantImageUnassign_variantImageUnassign_productVariant_stocks_warehouse; +} + export interface VariantImageUnassign_variantImageUnassign_productVariant { __typename: "ProductVariant"; id: string; @@ -107,8 +120,8 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant { priceOverride: VariantImageUnassign_variantImageUnassign_productVariant_priceOverride | null; product: VariantImageUnassign_variantImageUnassign_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (VariantImageUnassign_variantImageUnassign_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface VariantImageUnassign_variantImageUnassign { diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 0028a0887..ca646c23d 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, ProductErrorCode } from "./../../types/globalTypes"; +import { AttributeValueInput, StockInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantUpdate @@ -97,6 +97,19 @@ export interface VariantUpdate_productVariantUpdate_productVariant_product { variants: (VariantUpdate_productVariantUpdate_productVariant_product_variants | null)[] | null; } +export interface VariantUpdate_productVariantUpdate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface VariantUpdate_productVariantUpdate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: VariantUpdate_productVariantUpdate_productVariant_stocks_warehouse; +} + export interface VariantUpdate_productVariantUpdate_productVariant { __typename: "ProductVariant"; id: string; @@ -107,8 +120,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant { priceOverride: VariantUpdate_productVariantUpdate_productVariant_priceOverride | null; product: VariantUpdate_productVariantUpdate_productVariant_product; sku: string; - quantity: number; - quantityAllocated: number | null; + stocks: (VariantUpdate_productVariantUpdate_productVariant_stocks | null)[] | null; + trackInventory: boolean; } export interface VariantUpdate_productVariantUpdate { @@ -117,8 +130,132 @@ export interface VariantUpdate_productVariantUpdate { productVariant: VariantUpdate_productVariantUpdate_productVariant | null; } +export interface VariantUpdate_productVariantStocksUpdate_errors { + __typename: "BulkStockError"; + code: ProductErrorCode; + field: string | null; + index: number | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + valueRequired: boolean; + values: (VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute_values | null)[] | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_attributes { + __typename: "SelectedAttribute"; + attribute: VariantUpdate_productVariantStocksUpdate_productVariant_attributes_attribute; + 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_priceOverride { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_thumbnail { + __typename: "Image"; + url: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_variants_images { + __typename: "ProductImage"; + id: string; + url: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_variants { + __typename: "ProductVariant"; + id: string; + name: string; + sku: string; + images: (VariantUpdate_productVariantStocksUpdate_productVariant_product_variants_images | null)[] | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_product { + __typename: "Product"; + id: string; + images: (VariantUpdate_productVariantStocksUpdate_productVariant_product_images | null)[] | null; + name: string; + thumbnail: VariantUpdate_productVariantStocksUpdate_productVariant_product_thumbnail | null; + variants: (VariantUpdate_productVariantStocksUpdate_productVariant_product_variants | null)[] | null; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks { + __typename: "Stock"; + id: string; + quantity: number; + warehouse: VariantUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse; +} + +export interface VariantUpdate_productVariantStocksUpdate_productVariant { + __typename: "ProductVariant"; + id: string; + attributes: VariantUpdate_productVariantStocksUpdate_productVariant_attributes[]; + costPrice: VariantUpdate_productVariantStocksUpdate_productVariant_costPrice | null; + images: (VariantUpdate_productVariantStocksUpdate_productVariant_images | null)[] | null; + name: string; + priceOverride: VariantUpdate_productVariantStocksUpdate_productVariant_priceOverride | null; + product: VariantUpdate_productVariantStocksUpdate_productVariant_product; + sku: string; + stocks: (VariantUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null; + trackInventory: boolean; +} + +export interface VariantUpdate_productVariantStocksUpdate { + __typename: "ProductVariantStocksUpdate"; + errors: VariantUpdate_productVariantStocksUpdate_errors[]; + productVariant: VariantUpdate_productVariantStocksUpdate_productVariant | null; +} + export interface VariantUpdate { productVariantUpdate: VariantUpdate_productVariantUpdate | null; + productVariantStocksUpdate: VariantUpdate_productVariantStocksUpdate | null; } export interface VariantUpdateVariables { @@ -127,6 +264,6 @@ export interface VariantUpdateVariables { costPrice?: any | null; priceOverride?: any | null; sku?: string | null; - quantity?: number | null; trackInventory: boolean; + stocks: StockInput[]; } diff --git a/src/products/urls.ts b/src/products/urls.ts index 9441c04a5..5160034a8 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -17,7 +17,10 @@ import { const productSection = "/products/"; export const productAddPath = urlJoin(productSection, "add"); -export const productAddUrl = productAddPath; +export type ProductAddUrlDialog = "edit-stocks"; +export type ProductAddUrlQueryParams = Dialog; +export const productAddUrl = (params?: ProductAddUrlQueryParams): string => + productAddPath + "?" + stringifyQs(params); export const productListPath = productSection; export type ProductListUrlDialog = @@ -66,15 +69,17 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string => productListPath + "?" + stringifyQs(params); export const productPath = (id: string) => urlJoin(productSection + id); -export type ProductUrlDialog = "create-variants" | "remove" | "remove-variants"; +export type ProductUrlDialog = "edit-stocks" | "remove" | "remove-variants"; export type ProductUrlQueryParams = BulkAction & Dialog; export const productUrl = (id: string, params?: ProductUrlQueryParams) => productPath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const productVariantEditPath = (productId: string, variantId: string) => urlJoin(productSection, productId, "variant", variantId); -export type ProductVariantEditUrlDialog = "remove"; -export type ProductVariantEditUrlQueryParams = Dialog<"remove">; +export type ProductVariantEditUrlDialog = "edit-stocks" | "remove"; +export type ProductVariantEditUrlQueryParams = Dialog< + ProductVariantEditUrlDialog +>; export const productVariantEditUrl = ( productId: string, variantId: string, @@ -87,10 +92,24 @@ export const productVariantEditUrl = ( "?" + stringifyQs(params); +export const productVariantCreatorPath = (productId: string) => + urlJoin(productSection, productId, "variant-creator"); +export const productVariantCreatorUrl = (productId: string) => + productVariantCreatorPath(encodeURIComponent(productId)); + export const productVariantAddPath = (productId: string) => urlJoin(productSection, productId, "variant/add"); -export const productVariantAddUrl = (productId: string) => - productVariantAddPath(encodeURIComponent(productId)); +export type ProductVariantAddUrlDialog = "edit-stocks"; +export type ProductVariantAddUrlQueryParams = Dialog< + ProductVariantAddUrlDialog +>; +export const productVariantAddUrl = ( + productId: string, + params?: ProductVariantAddUrlQueryParams +): string => + productVariantAddPath(encodeURIComponent(productId)) + + "?" + + stringifyQs(params); export const productImagePath = (productId: string, imageId: string) => urlJoin(productSection, productId, "image", imageId); diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 37036d1a6..f5b6098a5 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -13,6 +13,7 @@ import { ProductAttributeInput } from "../components/ProductAttributes"; import { VariantAttributeInput } from "../components/ProductVariantAttributes"; import { ProductVariant } from "../types/ProductVariant"; import { ProductVariantCreateData_product } from "../types/ProductVariantCreateData"; +import { ProductStockInput } from "../components/ProductStocks"; export interface Collection { id: string; @@ -102,6 +103,19 @@ export function getAttributeInputFromVariant( ); } +export function getStockInputFromVariant( + variant: ProductVariant +): ProductStockInput[] { + return ( + variant?.stocks.map(stock => ({ + data: null, + id: stock.warehouse.id, + label: stock.warehouse.name, + value: stock.quantity.toString() + })) || [] + ); +} + export function getVariantAttributeInputFromProduct( product: ProductVariantCreateData_product ): VariantAttributeInput[] { @@ -117,6 +131,17 @@ export function getVariantAttributeInputFromProduct( ); } +export function getStockInputFromProduct( + product: ProductDetails_product +): ProductStockInput[] { + return product?.variants[0]?.stocks.map(stock => ({ + data: null, + id: stock.warehouse.id, + label: stock.warehouse.name, + value: stock.quantity.toString() + })); +} + export function getCollectionInput( productCollections: ProductDetails_product_collections[] ): Collection[] { @@ -153,7 +178,7 @@ export interface ProductUpdatePageFormData { seoDescription: string; seoTitle: string; sku: string; - stockQuantity: number; + trackInventory: boolean; } export function getProductUpdatePageFormData( @@ -183,14 +208,6 @@ export function getProductUpdatePageFormData( : undefined, "" ), - stockQuantity: maybe( - () => - product.productType.hasVariants - ? undefined - : variants && variants[0] - ? variants[0].quantity - : undefined, - 0 - ) + trackInventory: !!product?.variants[0]?.trackInventory }; } diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index ae7b565e6..84928e85a 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -9,19 +9,31 @@ import useShop from "@saleor/hooks/useShop"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; +import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; import { decimal, maybe } from "../../misc"; import ProductCreatePage, { ProductCreatePageSubmitData } from "../components/ProductCreatePage"; import { TypedProductCreateMutation } from "../mutations"; import { ProductCreate } from "../types/ProductCreate"; -import { productListUrl, productUrl } from "../urls"; +import { + productListUrl, + productUrl, + ProductAddUrlDialog, + ProductAddUrlQueryParams, + productAddUrl +} from "../urls"; +import ProductWarehousesDialog from "../components/ProductWarehousesDialog"; -interface ProductUpdateProps { - id: string; +interface ProductCreateViewProps { + params: ProductAddUrlQueryParams; } -export const ProductUpdate: React.FC = () => { +export const ProductCreateView: React.FC = ({ + params +}) => { const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); @@ -47,6 +59,20 @@ export const ProductUpdate: React.FC = () => { } = useProductTypeSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { result: searchWarehousesOpts } = useWarehouseSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 20 + } + }); + const [warehouses, setWarehouses] = React.useState< + SearchWarehouses_search_edges_node[] + >([]); + + const [openModal, closeModal] = createDialogActionHandlers< + ProductAddUrlDialog, + ProductAddUrlQueryParams + >(navigate, productAddUrl, params); const handleBack = () => navigate(productListUrl()); @@ -88,8 +114,11 @@ export const ProductUpdate: React.FC = () => { title: formData.seoTitle }, sku: formData.sku, - stockQuantity: - formData.stockQuantity !== null ? formData.stockQuantity : 0 + stocks: formData.stocks.map(stock => ({ + quantity: parseInt(stock.value, 0), + warehouse: stock.id + })), + trackInventory: formData.trackInventory } }); }; @@ -124,6 +153,7 @@ export const ProductUpdate: React.FC = () => { productTypes={maybe(() => searchProductTypesOpts.data.search.edges.map(edge => edge.node) )} + warehouses={warehouses} onBack={handleBack} onSubmit={handleSubmit} saveButtonBarState={productCreateOpts.status} @@ -148,6 +178,32 @@ export const ProductUpdate: React.FC = () => { loading: searchProductTypesOpts.loading, onFetchMore: loadMoreProductTypes }} + onWarehouseEdit={() => openModal("edit-stocks")} + /> + edge.node + )} + warehousesWithStocks={warehouses.map(warehouse => warehouse.id)} + onConfirm={data => { + setWarehouses( + [ + ...warehouses, + ...data.added.map( + addedId => + searchWarehousesOpts.data.search.edges.find( + edge => edge.node.id === addedId + ).node + ) + ].filter(warehouse => !data.removed.includes(warehouse.id)) + ); + closeModal(); + }} /> ); @@ -155,4 +211,4 @@ export const ProductUpdate: React.FC = () => { ); }; -export default ProductUpdate; +export default ProductCreateView; diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index de9ebe432..302006ca0 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -306,7 +306,7 @@ export const ProductList: React.FC = ({ params }) => { .hasNextPage, false )} - onAdd={() => navigate(productAddUrl)} + onAdd={() => navigate(productAddUrl())} disabled={loading} products={maybe(() => data.products.edges.map(edge => edge.node) diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 4de424226..c0c35acf8 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -11,14 +11,14 @@ import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; -import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; -import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog"; -import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import NotFoundPage from "@saleor/components/NotFoundPage"; +import ProductWarehousesDialog from "@saleor/products/components/ProductWarehousesDialog"; +import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import { useAddOrRemoveStocks } from "@saleor/products/mutations"; import { getMutationState, maybe } from "../../../misc"; import ProductUpdatePage from "../../components/ProductUpdatePage"; import ProductUpdateOperations from "../../containers/ProductUpdateOperations"; @@ -36,7 +36,8 @@ import { ProductUrlQueryParams, productVariantAddUrl, productVariantEditUrl, - ProductUrlDialog + ProductUrlDialog, + productVariantCreatorUrl } from "../../urls"; import { createImageReorderHandler, @@ -56,7 +57,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { params.ids ); const intl = useIntl(); - const shop = useShop(); const { loadMore: loadMoreCategories, search: searchCategories, @@ -71,6 +71,26 @@ export const ProductUpdate: React.FC = ({ id, params }) => { } = useCollectionSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const { result: searchWarehousesOpts } = useWarehouseSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 20 + } + }); + + const [addOrRemoveStocks, addOrRemoveStocksOpts] = useAddOrRemoveStocks({ + onCompleted: data => { + if ( + data.productVariantStocksCreate.errors.length === 0 && + data.productVariantStocksDelete.errors.length === 0 + ) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); const [openModal, closeModal] = createDialogActionHandlers< ProductUrlDialog, @@ -121,15 +141,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { }); const handleVariantAdd = () => navigate(productVariantAddUrl(id)); - const handleBulkProductVariantCreate = ( - data: ProductVariantBulkCreate - ) => { - if (data.productVariantBulkCreate.errors.length === 0) { - closeModal(); - refetch(); - } - }; - const handleBulkProductVariantDelete = ( data: ProductVariantBulkDelete ) => { @@ -143,7 +154,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { return ( = ({ id, params }) => { onUpdate={handleUpdate} > {({ - bulkProductVariantCreate, bulkProductVariantDelete, createProductImage, deleteProduct, @@ -232,15 +241,10 @@ export const ProductUpdate: React.FC = ({ id, params }) => { variants={maybe(() => product.variants)} onBack={handleBack} onDelete={() => openModal("remove")} - onProductShow={() => { - if (product) { - window.open(product.url); - } - }} onImageReorder={handleImageReorder} onSubmit={handleSubmit} onVariantAdd={handleVariantAdd} - onVariantsAdd={() => openModal("create-variants")} + onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => navigate(productVariantEditUrl(product.id, variantId))} onImageUpload={handleImageUpload} @@ -278,6 +282,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { loading: searchCollectionsOpts.loading, onFetchMore: loadMoreCollections }} + onWarehousesEdit={() => openModal("edit-stocks")} /> = ({ id, params }) => { /> - - data.product.basePrice.amount.toFixed(2) - )} - errors={ - bulkProductVariantCreate.opts.data - ?.productVariantBulkCreate.errors || [] - } - open={params.action === "create-variants"} - attributes={maybe( - () => data.product.productType.variantAttributes, - [] - )} - currencySymbol={maybe(() => shop.defaultCurrency)} - onClose={closeModal} - onSubmit={inputs => - bulkProductVariantCreate.mutate({ - id, - inputs - }) - } - /> + {!product?.productType?.hasVariants && ( + edge.node + )} + warehousesWithStocks={ + product?.variants[0].stocks.map( + stock => stock.warehouse.id + ) || [] + } + onConfirm={data => + addOrRemoveStocks({ + variables: { + add: data.added.map(id => ({ + quantity: 0, + warehouse: id + })), + remove: data.removed, + variantId: product.variants[0].id + } + }) + } + /> + )} ); }} diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index fd356f33a..3fad989ff 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -40,11 +40,17 @@ export function createUpdateHandler( } else { updateSimpleProduct({ ...productVariables, + addStocks: [], + deleteStocks: [], productVariantId: product.variants[0].id, productVariantInput: { - quantity: data.stockQuantity, - sku: data.sku - } + sku: data.sku, + trackInventory: data.trackInventory + }, + updateStocks: data.stocks.map(stock => ({ + quantity: parseInt(stock.value, 0), + warehouse: stock.id + })) }); } }; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 6a68c2d49..b03f3a33e 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -7,7 +7,10 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; import NotFoundPage from "@saleor/components/NotFoundPage"; -import { decimal, maybe } from "../../misc"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import { decimal } from "../../misc"; import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog"; import ProductVariantPage, { ProductVariantPageSubmitData @@ -22,8 +25,11 @@ import { productUrl, productVariantAddUrl, productVariantEditUrl, - ProductVariantEditUrlQueryParams + ProductVariantEditUrlQueryParams, + ProductVariantEditUrlDialog } from "../urls"; +import ProductWarehousesDialog from "../components/ProductWarehousesDialog"; +import { useAddOrRemoveStocks } from "../mutations"; interface ProductUpdateProps { variantId: string; @@ -46,6 +52,36 @@ export const ProductVariant: React.FC = ({ setErrors([]); }, [variantId]); + const { result: searchWarehousesOpts } = useWarehouseSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 20 + } + }); + + const [addOrRemoveStocks, addOrRemoveStocksOpts] = useAddOrRemoveStocks({ + onCompleted: data => { + if ( + data.productVariantStocksCreate.errors.length === 0 && + data.productVariantStocksDelete.errors.length === 0 + ) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const [openModal, closeModal] = createDialogActionHandlers< + ProductVariantEditUrlDialog, + ProductVariantEditUrlQueryParams + >( + navigate, + params => productVariantEditUrl(productId, variantId, params), + params + ); + const handleBack = () => navigate(productUrl(productId)); return ( @@ -107,14 +143,14 @@ export const ProductVariant: React.FC = ({ return ( <> - data.productVariant.name)} /> + navigate(productVariantAddUrl(productId))} onBack={handleBack} onDelete={() => @@ -125,25 +161,27 @@ export const ProductVariant: React.FC = ({ ) } onImageSelect={handleImageSelect} - onSubmit={(data: ProductVariantPageSubmitData) => { - if (variant) { - updateVariant.mutate({ - attributes: data.attributes.map(attribute => ({ - id: attribute.id, - values: [attribute.value] - })), - costPrice: decimal(data.costPrice), - id: variantId, - priceOverride: decimal(data.priceOverride), - quantity: parseInt(data.quantity, 0), - sku: data.sku, - trackInventory: true // FIXME: missing in UI - }); - } - }} + onSubmit={(data: ProductVariantPageSubmitData) => + updateVariant.mutate({ + attributes: data.attributes.map(attribute => ({ + id: attribute.id, + values: [attribute.value] + })), + costPrice: decimal(data.costPrice), + id: variantId, + priceOverride: decimal(data.priceOverride), + sku: data.sku, + stocks: data.stocks.map(stock => ({ + quantity: parseInt(stock.value, 10), + warehouse: stock.id + })), + trackInventory: data.trackInventory + }) + } onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} + onWarehousesEdit={() => openModal("edit-stocks")} /> = ({ }) } open={params.action === "remove"} - name={maybe(() => data.productVariant.name)} + name={data?.productVariant?.name} + /> + edge.node + )} + warehousesWithStocks={ + variant?.stocks.map(stock => stock.warehouse.id) || [] + } + onConfirm={data => + addOrRemoveStocks({ + variables: { + add: data.added.map(id => ({ + quantity: 0, + warehouse: id + })), + remove: data.removed, + variantId + } + }) + } /> ); diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index c2e446ab1..aeeb3643a 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -7,24 +7,54 @@ import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { commonMessages } from "@saleor/intl"; -import { decimal, maybe } from "../../misc"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import { decimal } from "../../misc"; import ProductVariantCreatePage, { ProductVariantCreatePageSubmitData } from "../components/ProductVariantCreatePage"; import { TypedVariantCreateMutation } from "../mutations"; import { TypedProductVariantCreateQuery } from "../queries"; import { VariantCreate } from "../types/VariantCreate"; -import { productUrl, productVariantEditUrl, productListUrl } from "../urls"; +import { + productUrl, + productVariantEditUrl, + productListUrl, + productVariantAddUrl, + ProductVariantAddUrlDialog, + ProductVariantAddUrlQueryParams +} from "../urls"; +import ProductWarehousesDialog from "../components/ProductWarehousesDialog"; -interface ProductUpdateProps { +interface ProductVariantCreateProps { + params: ProductVariantAddUrlQueryParams; productId: string; } -export const ProductVariant: React.FC = ({ productId }) => { +export const ProductVariant: React.FC = ({ + params, + productId +}) => { const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); const intl = useIntl(); + const { result: searchWarehousesOpts } = useWarehouseSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 20 + } + }); + const [warehouses, setWarehouses] = React.useState< + SearchWarehouses_search_edges_node[] + >([]); + + const [openModal, closeModal] = createDialogActionHandlers< + ProductVariantAddUrlDialog, + ProductVariantAddUrlQueryParams + >(navigate, params => productVariantAddUrl(productId, params), params); return ( @@ -68,8 +98,11 @@ export const ProductVariant: React.FC = ({ productId }) => { costPrice: decimal(formData.costPrice), priceOverride: decimal(formData.priceOverride), product: productId, - quantity: parseInt(formData.quantity, 0), sku: formData.sku, + stocks: formData.stocks.map(stock => ({ + quantity: parseInt(stock.value, 0), + warehouse: stock.id + })), trackInventory: true } } @@ -88,7 +121,8 @@ export const ProductVariant: React.FC = ({ productId }) => { })} /> shop.defaultCurrency)} + currencySymbol={shop?.defaultCurrency} + disabled={disableForm} errors={ variantCreateResult.data?.productVariantCreate.errors || [] @@ -97,12 +131,42 @@ export const ProductVariant: React.FC = ({ productId }) => { defaultMessage: "Create Variant", description: "header" })} - loading={disableForm} product={data?.product} onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} saveButtonBarState={variantCreateResult.status} + warehouses={warehouses} + onWarehouseEdit={() => openModal("edit-stocks")} + /> + edge.node + )} + warehousesWithStocks={warehouses.map( + warehouse => warehouse.id + )} + onConfirm={data => { + setWarehouses( + [ + ...warehouses, + ...data.added.map( + addedId => + searchWarehousesOpts.data.search.edges.find( + edge => edge.node.id === addedId + ).node + ) + ].filter( + warehouse => !data.removed.includes(warehouse.id) + ) + ); + closeModal(); + }} /> ); diff --git a/src/products/views/ProductVariantCreator/ProductVariantCreator.tsx b/src/products/views/ProductVariantCreator/ProductVariantCreator.tsx new file mode 100644 index 000000000..b6854c310 --- /dev/null +++ b/src/products/views/ProductVariantCreator/ProductVariantCreator.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { useCreateMultipleVariantsData } from "@saleor/products/queries"; +import { useProductVariantBulkCreateMutation } from "@saleor/products/mutations"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { productUrl } from "@saleor/products/urls"; +import useShop from "@saleor/hooks/useShop"; +import ProductVariantCreatorPage from "../../components/ProductVariantCreatorPage"; + +interface ProductVariantCreatorProps { + id: string; +} + +const ProductVariantCreator: React.FC = ({ + id +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const intl = useIntl(); + const { data } = useCreateMultipleVariantsData({ + displayLoader: true, + variables: { id } + }); + const [ + bulkProductVariantCreate, + bulkProductVariantCreateOpts + ] = useProductVariantBulkCreateMutation({ + onCompleted: data => { + if (data.productVariantBulkCreate.errors.length === 0) { + notify({ + text: intl.formatMessage({ + defaultMessage: "Successfully created variants", + description: "success message" + }) + }); + navigate(productUrl(id)); + } + } + }); + const shop = useShop(); + + return ( + <> + + + bulkProductVariantCreate({ + variables: { id, inputs } + }) + } + warehouses={data?.warehouses.edges.map(edge => edge.node) || []} + /> + + ); +}; +ProductVariantCreator.displayName = "ProductVariantCreator"; +export default ProductVariantCreator; diff --git a/src/products/views/ProductVariantCreator/index.ts b/src/products/views/ProductVariantCreator/index.ts new file mode 100644 index 000000000..033e63894 --- /dev/null +++ b/src/products/views/ProductVariantCreator/index.ts @@ -0,0 +1,2 @@ +export * from "./ProductVariantCreator"; +export { default } from "./ProductVariantCreator"; diff --git a/src/searches/types/SearchWarehouses.ts b/src/searches/types/SearchWarehouses.ts new file mode 100644 index 000000000..beab7185b --- /dev/null +++ b/src/searches/types/SearchWarehouses.ts @@ -0,0 +1,42 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: SearchWarehouses +// ==================================================== + +export interface SearchWarehouses_search_edges_node { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface SearchWarehouses_search_edges { + __typename: "WarehouseCountableEdge"; + node: SearchWarehouses_search_edges_node; +} + +export interface SearchWarehouses_search_pageInfo { + __typename: "PageInfo"; + endCursor: string | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; +} + +export interface SearchWarehouses_search { + __typename: "WarehouseCountableConnection"; + edges: SearchWarehouses_search_edges[]; + pageInfo: SearchWarehouses_search_pageInfo; +} + +export interface SearchWarehouses { + search: SearchWarehouses_search | null; +} + +export interface SearchWarehousesVariables { + after?: string | null; + first: number; + query: string; +} diff --git a/src/searches/useWarehouseSearch.ts b/src/searches/useWarehouseSearch.ts new file mode 100644 index 000000000..b5b0e6048 --- /dev/null +++ b/src/searches/useWarehouseSearch.ts @@ -0,0 +1,33 @@ +import gql from "graphql-tag"; + +import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch"; +import { pageInfoFragment } from "@saleor/queries"; +import { + SearchWarehouses, + SearchWarehousesVariables +} from "./types/SearchWarehouses"; + +export const searchWarehouses = gql` + ${pageInfoFragment} + query SearchWarehouses($after: String, $first: Int!, $query: String!) { + search: warehouses( + after: $after + first: $first + filter: { search: $query } + ) { + edges { + node { + id + name + } + } + pageInfo { + ...PageInfoFragment + } + } + } +`; + +export default makeTopLevelSearch( + searchWarehouses +); diff --git a/src/shipping/components/ShippingZoneAddWarehouseDialog/ShippingZoneAddWarehouseDialog.tsx b/src/shipping/components/ShippingZoneAddWarehouseDialog/ShippingZoneAddWarehouseDialog.tsx new file mode 100644 index 000000000..4315c31c1 --- /dev/null +++ b/src/shipping/components/ShippingZoneAddWarehouseDialog/ShippingZoneAddWarehouseDialog.tsx @@ -0,0 +1,163 @@ +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 TextField from "@material-ui/core/TextField"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import makeStyles from "@material-ui/core/styles/makeStyles"; + +import ConfirmButton, { + ConfirmButtonTransitionState +} from "@saleor/components/ConfirmButton"; +import { DialogProps } from "@saleor/types"; +import { AddressTypeInput } from "@saleor/customers/types"; +import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import CompanyAddressForm from "@saleor/components/CompanyAddressInput/CompanyAddressForm"; +import { buttonMessages } from "@saleor/intl"; +import Hr from "@saleor/components/Hr"; +import Form from "@saleor/components/Form"; +import useAddressValidation from "@saleor/hooks/useAddressValidation"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import FormSpacer from "@saleor/components/FormSpacer"; +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; + +export interface ShippingZoneAddWarehouseDialogSubmitData + extends AddressTypeInput { + name: string; +} +export interface ShippingZoneAddWarehouseDialogProps extends DialogProps { + confirmButtonState: ConfirmButtonTransitionState; + countries: ShopInfo_shop_countries[]; + disabled: boolean; + errors: WarehouseErrorFragment[]; + onSubmit: (data: ShippingZoneAddWarehouseDialogSubmitData) => void; +} + +const initialForm: ShippingZoneAddWarehouseDialogSubmitData = { + city: "", + cityArea: "", + companyName: "", + country: "", + countryArea: "", + firstName: "", + lastName: "", + name: "", + phone: "", + postalCode: "", + streetAddress1: "", + streetAddress2: "" +}; + +const useStyles = makeStyles( + { + overflow: { + overflowY: "visible" + } + }, + { + name: "ShippingZoneAddWarehouseDialog" + } +); + +const ShippingZoneAddWarehouseDialog: React.FC = ({ + confirmButtonState, + countries, + disabled, + errors: apiErrors, + open, + onClose, + onSubmit +}) => { + const classes = useStyles({}); + const [countryDisplayName, setCountryDisplayName] = useStateFromProps(""); + const { + errors: validationErrors, + submit: handleSubmit + } = useAddressValidation(onSubmit); + const errors = useModalDialogErrors( + [...apiErrors, ...validationErrors], + open + ); + useModalDialogOpen(open, {}); + const intl = useIntl(); + + const countryChoices = countries.map(country => ({ + label: country.country, + value: country.code + })); + + return ( + + + + +
+ {({ change, data }) => { + const handleCountrySelect = createSingleAutocompleteSelectHandler( + change, + setCountryDisplayName, + countryChoices + ); + + return ( + <> + + + +
+ + +
+ + + + + + + + ); + }} +
+
+ ); +}; + +ShippingZoneAddWarehouseDialog.displayName = "ShippingZoneAddWarehouseDialog"; +export default ShippingZoneAddWarehouseDialog; diff --git a/src/shipping/components/ShippingZoneAddWarehouseDialog/index.ts b/src/shipping/components/ShippingZoneAddWarehouseDialog/index.ts new file mode 100644 index 000000000..3d5129332 --- /dev/null +++ b/src/shipping/components/ShippingZoneAddWarehouseDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ShippingZoneAddWarehouseDialog"; +export * from "./ShippingZoneAddWarehouseDialog"; diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index fa48d8624..84167ace5 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -11,21 +11,34 @@ import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ShippingErrorFragment } from "@saleor/shipping/types/ShippingErrorFragment"; -import { maybe, getStringOrPlaceholder } from "../../../misc"; +import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { getStringOrPlaceholder } from "../../../misc"; +import { FetchMoreProps, SearchProps } from "../../../types"; import { ShippingMethodTypeEnum } from "../../../types/globalTypes"; -import { ShippingZoneDetailsFragment } from "../../types/ShippingZoneDetailsFragment"; +import { + ShippingZoneDetailsFragment, + ShippingZoneDetailsFragment_warehouses +} from "../../types/ShippingZoneDetailsFragment"; import ShippingZoneInfo from "../ShippingZoneInfo"; import ShippingZoneRates from "../ShippingZoneRates"; +import ShippingZoneWarehouses from "../ShippingZoneWarehouses"; export interface FormData { name: string; + warehouses: string[]; } -export interface ShippingZoneDetailsPageProps { +export interface ShippingZoneDetailsPageProps + extends FetchMoreProps, + SearchProps { disabled: boolean; errors: ShippingErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; shippingZone: ShippingZoneDetailsFragment; + warehouses: ShippingZoneDetailsFragment_warehouses[]; onBack: () => void; onCountryAdd: () => void; onCountryRemove: (code: string) => void; @@ -34,55 +47,91 @@ export interface ShippingZoneDetailsPageProps { onPriceRateEdit: (id: string) => void; onRateRemove: (rateId: string) => void; onSubmit: (data: FormData) => void; + onWarehouseAdd: () => void; onWeightRateAdd: () => void; onWeightRateEdit: (id: string) => void; } +function warehouseToChoice( + warehouse: Record<"id" | "name", string> +): SingleAutocompleteChoiceType { + return { + label: warehouse.name, + value: warehouse.id + }; +} + const ShippingZoneDetailsPage: React.FC = ({ disabled, errors, + hasMore, + loading, onBack, onCountryAdd, onCountryRemove, onDelete, + onFetchMore, onPriceRateAdd, onPriceRateEdit, onRateRemove, + onSearchChange, onSubmit, + onWarehouseAdd, onWeightRateAdd, onWeightRateEdit, saveButtonBarState, - shippingZone + shippingZone, + warehouses }) => { const intl = useIntl(); const initialForm: FormData = { - name: shippingZone?.name || "" + name: shippingZone?.name || "", + warehouses: shippingZone?.warehouses?.map(warehouse => warehouse.id) || [] }; + const [warehouseDisplayValues, setWarehouseDisplayValues] = useStateFromProps< + MultiAutocompleteChoiceType[] + >( + shippingZone?.warehouses?.map(warehouse => ({ + label: warehouse.name, + value: warehouse.id + })) || [] + ); + + const warehouseChoices = warehouses.map(warehouseToChoice); return (
- {({ change, data, hasChanged, submit }) => ( - - - - - - -
- - - - shippingZone.default + {({ change, data, hasChanged, submit, toggleValue }) => { + const handleWarehouseChange = createMultiAutocompleteSelectHandler( + toggleValue, + setWarehouseDisplayValues, + warehouseDisplayValues, + warehouseChoices + ); + + return ( + + + + + + +
+ + + = ({ : intl.formatMessage({ defaultMessage: "Currently, there are no countries assigned to this shipping zone" - }), - "..." - )} - onCountryAssign={onCountryAdd} - onCountryUnassign={onCountryRemove} - title={intl.formatMessage({ - defaultMessage: "Countries" - })} - /> - - - shippingZone.shippingMethods.filter( + }) + )} + onCountryAssign={onCountryAdd} + onCountryUnassign={onCountryRemove} + title={intl.formatMessage({ + defaultMessage: "Countries" + })} + /> + + method.type === ShippingMethodTypeEnum.PRICE - ) - )} - variant="price" - /> - - - shippingZone.shippingMethods.filter( + )} + variant="price" + /> + + method.type === ShippingMethodTypeEnum.WEIGHT - ) - )} - variant="weight" - /> -
-
- -
- )} + )} + variant="weight" + /> +
+
+ +
+
+ +
+ ); + }}
); }; diff --git a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx index 0ad8e0d08..6aeacf402 100644 --- a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx +++ b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx @@ -9,10 +9,9 @@ import { commonMessages } from "@saleor/intl"; import { getFormErrors } from "@saleor/utils/errors"; import getShippingErrorMessage from "@saleor/utils/errors/shipping"; import { ShippingErrorFragment } from "@saleor/shipping/types/ShippingErrorFragment"; -import { FormData } from "../ShippingZoneDetailsPage"; export interface ShippingZoneInfoProps { - data: FormData; + data: Record<"name", string>; disabled: boolean; errors: ShippingErrorFragment[]; onChange: (event: React.ChangeEvent) => void; @@ -42,7 +41,7 @@ const ShippingZoneInfo: React.FC = ({ label={intl.formatMessage({ defaultMessage: "Shipping Zone Name" })} - name={"name" as keyof FormData} + name="name" value={data.name} onChange={onChange} /> diff --git a/src/shipping/components/ShippingZoneWarehouses/ShippingZoneWarehouses.tsx b/src/shipping/components/ShippingZoneWarehouses/ShippingZoneWarehouses.tsx new file mode 100644 index 000000000..e34264660 --- /dev/null +++ b/src/shipping/components/ShippingZoneWarehouses/ShippingZoneWarehouses.tsx @@ -0,0 +1,83 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import React from "react"; +import { useIntl } from "react-intl"; + +import CardTitle from "@saleor/components/CardTitle"; +import { FetchMoreProps, SearchProps } from "@saleor/types"; +import { FormChange } from "@saleor/hooks/useForm"; +import MultiAutocompleteSelectField, { + MultiAutocompleteChoiceType +} from "@saleor/components/MultiAutocompleteSelectField"; + +interface ShippingZoneWarehousesFormData { + warehouses: string[]; +} +interface ShippingZonewWarehousesProps extends FetchMoreProps, SearchProps { + data: ShippingZoneWarehousesFormData; + displayValues: MultiAutocompleteChoiceType[]; + warehouses: MultiAutocompleteChoiceType[]; + onChange: FormChange; + onWarehouseAdd: () => void; +} + +export const ShippingZoneWarehouses: React.FC = props => { + const { + data, + displayValues, + hasMore, + loading, + warehouses, + onChange, + onFetchMore, + onSearchChange, + onWarehouseAdd + } = props; + const intl = useIntl(); + + return ( + + + + + + + ); +}; +ShippingZoneWarehouses.displayName = "ShippingZoneWarehouses"; +export default ShippingZoneWarehouses; diff --git a/src/shipping/components/ShippingZoneWarehouses/index.ts b/src/shipping/components/ShippingZoneWarehouses/index.ts new file mode 100644 index 000000000..9719aa5e7 --- /dev/null +++ b/src/shipping/components/ShippingZoneWarehouses/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ShippingZoneWarehouses"; +export * from "./ShippingZoneWarehouses"; diff --git a/src/shipping/fixtures.ts b/src/shipping/fixtures.ts index cefe56d87..77eab37f9 100644 --- a/src/shipping/fixtures.ts +++ b/src/shipping/fixtures.ts @@ -1644,5 +1644,17 @@ export const shippingZone: ShippingZoneDetailsFragment = { }, type: ShippingMethodTypeEnum.PRICE } + ], + warehouses: [ + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOmEzMThmMGZlLTcwMmYtNDNjYy1hYmFjLWZmZmMzN2Y3ZTliYw==", + name: "C our wares" + }, + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOjJmN2UyOTlmLWEwMzMtNDhjZS1iYmM5LTFkZDM4NjU2ZjMwYw==", + name: "Be stocked" + } ] }; diff --git a/src/shipping/mutations.ts b/src/shipping/mutations.ts index ab550e936..83ffa811a 100644 --- a/src/shipping/mutations.ts +++ b/src/shipping/mutations.ts @@ -1,6 +1,6 @@ import gql from "graphql-tag"; -import { TypedMutation } from "../mutations"; +import makeMutation from "@saleor/hooks/makeMutation"; import { countryFragment } from "../taxes/queries"; import { shippingMethodFragment, shippingZoneDetailsFragment } from "./queries"; import { @@ -57,7 +57,7 @@ const deleteShippingZone = gql` } } `; -export const TypedDeleteShippingZone = TypedMutation< +export const useShippingZoneDelete = makeMutation< DeleteShippingZone, DeleteShippingZoneVariables >(deleteShippingZone); @@ -72,7 +72,7 @@ const bulkDeleteShippingZone = gql` } } `; -export const TypedBulkDeleteShippingZone = TypedMutation< +export const useShippingZoneBulkDelete = makeMutation< BulkDeleteShippingZone, BulkDeleteShippingZoneVariables >(bulkDeleteShippingZone); @@ -90,7 +90,7 @@ const updateDefaultWeightUnit = gql` } } `; -export const TypedUpdateDefaultWeightUnit = TypedMutation< +export const useDefaultWeightUnitUpdate = makeMutation< UpdateDefaultWeightUnit, UpdateDefaultWeightUnitVariables >(updateDefaultWeightUnit); @@ -114,7 +114,7 @@ const createShippingZone = gql` } } `; -export const TypedCreateShippingZone = TypedMutation< +export const useShippingZoneCreate = makeMutation< CreateShippingZone, CreateShippingZoneVariables >(createShippingZone); @@ -138,7 +138,7 @@ const updateShippingZone = gql` } } `; -export const TypedUpdateShippingZone = TypedMutation< +export const useShippingZoneUpdate = makeMutation< UpdateShippingZone, UpdateShippingZoneVariables >(updateShippingZone); @@ -157,7 +157,7 @@ const updateShippingRate = gql` } } `; -export const TypedUpdateShippingRate = TypedMutation< +export const useShippingRateUpdate = makeMutation< UpdateShippingRate, UpdateShippingRateVariables >(updateShippingRate); @@ -176,7 +176,7 @@ const createShippingRate = gql` } } `; -export const TypedCreateShippingRate = TypedMutation< +export const useShippingRateCreate = makeMutation< CreateShippingRate, CreateShippingRateVariables >(createShippingRate); @@ -195,7 +195,7 @@ const deleteShippingRate = gql` } } `; -export const TypedDeleteShippingRate = TypedMutation< +export const useShippingRateDelete = makeMutation< DeleteShippingRate, DeleteShippingRateVariables >(deleteShippingRate); @@ -210,7 +210,7 @@ const bulkDeleteShippingRate = gql` } } `; -export const TypedBulkDeleteShippingRate = TypedMutation< +export const useShippingRateBulkDelete = makeMutation< BulkDeleteShippingRate, BulkDeleteShippingRateVariables >(bulkDeleteShippingRate); diff --git a/src/shipping/queries.ts b/src/shipping/queries.ts index 9a4a1b4e9..771a32b3f 100644 --- a/src/shipping/queries.ts +++ b/src/shipping/queries.ts @@ -1,6 +1,7 @@ import gql from "graphql-tag"; -import { pageInfoFragment, TypedQuery } from "../queries"; +import makeQuery from "@saleor/hooks/makeQuery"; +import { pageInfoFragment } from "../queries"; import { ShippingZone, ShippingZoneVariables } from "./types/ShippingZone"; import { ShippingZones, ShippingZonesVariables } from "./types/ShippingZones"; @@ -50,6 +51,10 @@ export const shippingZoneDetailsFragment = gql` shippingMethods { ...ShippingMethodFragment } + warehouses { + id + name + } } `; @@ -74,7 +79,7 @@ const shippingZones = gql` } } `; -export const TypedShippingZones = TypedQuery< +export const useShippingZoneList = makeQuery< ShippingZones, ShippingZonesVariables >(shippingZones); @@ -87,7 +92,6 @@ const shippingZone = gql` } } `; -export const TypedShippingZone = TypedQuery< - ShippingZone, - ShippingZoneVariables ->(shippingZone); +export const useShippingZone = makeQuery( + shippingZone +); diff --git a/src/shipping/types/CreateShippingRate.ts b/src/shipping/types/CreateShippingRate.ts index c0d3a1e4a..7a29f3395 100644 --- a/src/shipping/types/CreateShippingRate.ts +++ b/src/shipping/types/CreateShippingRate.ts @@ -62,6 +62,12 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMet type: ShippingMethodTypeEnum | null; } +export interface CreateShippingRate_shippingPriceCreate_shippingZone_warehouses { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface CreateShippingRate_shippingPriceCreate_shippingZone { __typename: "ShippingZone"; id: string; @@ -69,6 +75,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone { name: string; default: boolean; shippingMethods: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods | null)[] | null; + warehouses: (CreateShippingRate_shippingPriceCreate_shippingZone_warehouses | null)[] | null; } export interface CreateShippingRate_shippingPriceCreate { diff --git a/src/shipping/types/DeleteShippingRate.ts b/src/shipping/types/DeleteShippingRate.ts index b4ffa0352..d3fafa9b6 100644 --- a/src/shipping/types/DeleteShippingRate.ts +++ b/src/shipping/types/DeleteShippingRate.ts @@ -62,6 +62,12 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMet type: ShippingMethodTypeEnum | null; } +export interface DeleteShippingRate_shippingPriceDelete_shippingZone_warehouses { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface DeleteShippingRate_shippingPriceDelete_shippingZone { __typename: "ShippingZone"; id: string; @@ -69,6 +75,7 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone { name: string; default: boolean; shippingMethods: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods | null)[] | null; + warehouses: (DeleteShippingRate_shippingPriceDelete_shippingZone_warehouses | null)[] | null; } export interface DeleteShippingRate_shippingPriceDelete { diff --git a/src/shipping/types/ShippingZone.ts b/src/shipping/types/ShippingZone.ts index 7b8dc090b..091776cd3 100644 --- a/src/shipping/types/ShippingZone.ts +++ b/src/shipping/types/ShippingZone.ts @@ -56,6 +56,12 @@ export interface ShippingZone_shippingZone_shippingMethods { type: ShippingMethodTypeEnum | null; } +export interface ShippingZone_shippingZone_warehouses { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface ShippingZone_shippingZone { __typename: "ShippingZone"; id: string; @@ -63,6 +69,7 @@ export interface ShippingZone_shippingZone { name: string; default: boolean; shippingMethods: (ShippingZone_shippingZone_shippingMethods | null)[] | null; + warehouses: (ShippingZone_shippingZone_warehouses | null)[] | null; } export interface ShippingZone { diff --git a/src/shipping/types/ShippingZoneDetailsFragment.ts b/src/shipping/types/ShippingZoneDetailsFragment.ts index 981e085a9..13bf1a398 100644 --- a/src/shipping/types/ShippingZoneDetailsFragment.ts +++ b/src/shipping/types/ShippingZoneDetailsFragment.ts @@ -56,6 +56,12 @@ export interface ShippingZoneDetailsFragment_shippingMethods { type: ShippingMethodTypeEnum | null; } +export interface ShippingZoneDetailsFragment_warehouses { + __typename: "Warehouse"; + id: string; + name: string; +} + export interface ShippingZoneDetailsFragment { __typename: "ShippingZone"; id: string; @@ -63,4 +69,5 @@ export interface ShippingZoneDetailsFragment { name: string; default: boolean; shippingMethods: (ShippingZoneDetailsFragment_shippingMethods | null)[] | null; + warehouses: (ShippingZoneDetailsFragment_warehouses | null)[] | null; } diff --git a/src/shipping/urls.ts b/src/shipping/urls.ts index be4d3d316..c9dc4d062 100644 --- a/src/shipping/urls.ts +++ b/src/shipping/urls.ts @@ -20,6 +20,7 @@ export const shippingZonePath = (id: string) => urlJoin(shippingZonesListPath, id); export type ShippingZoneUrlDialog = | "add-rate" + | "add-warehouse" | "assign-country" | "edit-rate" | "remove" diff --git a/src/shipping/views/ShippingZoneCreate.tsx b/src/shipping/views/ShippingZoneCreate.tsx index bf063090d..95928cc08 100644 --- a/src/shipping/views/ShippingZoneCreate.tsx +++ b/src/shipping/views/ShippingZoneCreate.tsx @@ -5,45 +5,41 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; -import { maybe } from "../../misc"; import ShippingZoneCreatePage from "../components/ShippingZoneCreatePage"; -import { TypedCreateShippingZone } from "../mutations"; -import { CreateShippingZone } from "../types/CreateShippingZone"; +import { useShippingZoneCreate } from "../mutations"; import { shippingZonesListUrl, shippingZoneUrl } from "../urls"; const ShippingZoneCreate: React.FC<{}> = () => { const navigate = useNavigator(); - const pushMessage = useNotifier(); + const notify = useNotifier(); const shop = useShop(); const intl = useIntl(); - const onShippingZoneCreate = (data: CreateShippingZone) => { - if (data.shippingZoneCreate.errors.length === 0) { - pushMessage({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - navigate(shippingZoneUrl(data.shippingZoneCreate.shippingZone.id)); + const [createShippingZone, createShippingZoneOpts] = useShippingZoneCreate({ + onCompleted: data => { + if (data.shippingZoneCreate.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate(shippingZoneUrl(data.shippingZoneCreate.shippingZone.id)); + } } - }; + }); return ( - - {(createShippingZone, createShippingZoneOpts) => ( - shop.countries, [])} - disabled={createShippingZoneOpts.loading} - errors={createShippingZoneOpts.data?.shippingZoneCreate.errors || []} - onBack={() => navigate(shippingZonesListUrl())} - onSubmit={formData => - createShippingZone({ - variables: { - input: formData - } - }) + navigate(shippingZonesListUrl())} + onSubmit={formData => + createShippingZone({ + variables: { + input: formData } - saveButtonBarState={createShippingZoneOpts.status} - /> - )} - + }) + } + saveButtonBarState={createShippingZoneOpts.status} + /> ); }; export default ShippingZoneCreate; diff --git a/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx b/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx deleted file mode 100644 index 36f346b39..000000000 --- a/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import DialogContentText from "@material-ui/core/DialogContentText"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import ActionDialog from "@saleor/components/ActionDialog"; -import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import useNavigator from "@saleor/hooks/useNavigator"; -import useShop from "@saleor/hooks/useShop"; -import { getStringOrPlaceholder } from "../../../misc"; -import { ShippingMethodTypeEnum } from "../../../types/globalTypes"; -import ShippingZoneCountriesAssignDialog from "../../components/ShippingZoneCountriesAssignDialog"; -import ShippingZoneRateDialog from "../../components/ShippingZoneRateDialog"; -import { ShippingZoneDetailsFragment } from "../../types/ShippingZoneDetailsFragment"; -import { shippingZoneUrl, ShippingZoneUrlQueryParams } from "../../urls"; -import { ShippingZoneOperationsOutput } from "./ShippingZoneOperations"; - -export interface ShippingZoneDetailsDialogsProps { - assignCountryTransitionState: ConfirmButtonTransitionState; - createRateTransitionState: ConfirmButtonTransitionState; - deleteRateTransitionState: ConfirmButtonTransitionState; - deleteZoneTransitionState: ConfirmButtonTransitionState; - id: string; - ops: ShippingZoneOperationsOutput; - params: ShippingZoneUrlQueryParams; - shippingZone: ShippingZoneDetailsFragment; - unassignCountryTransitionState: ConfirmButtonTransitionState; - updateRateTransitionState: ConfirmButtonTransitionState; -} - -const ShippingZoneDetailsDialogs: React.FC = ({ - assignCountryTransitionState, - createRateTransitionState, - deleteRateTransitionState, - deleteZoneTransitionState, - id, - ops, - params, - shippingZone, - unassignCountryTransitionState, - updateRateTransitionState -}) => { - const navigate = useNavigator(); - const shop = useShop(); - const intl = useIntl(); - - const closeModal = () => navigate(shippingZoneUrl(id), true); - - const rate = shippingZone?.shippingMethods?.find( - rate => rate.id === params.id - ); - - return ( - <> - - ops.shippingRateUpdate.mutate({ - id: params.id, - input: { - maximumOrderPrice: formData.noLimits - ? null - : parseFloat(formData.maxValue), - minimumOrderPrice: formData.noLimits - ? null - : parseFloat(formData.minValue), - name: formData.name, - price: formData.isFree ? 0 : parseFloat(formData.price), - shippingZone: id, - type: rate?.type - } - }) - } - open={params.action === "edit-rate"} - rate={rate} - variant={rate?.type} - /> - - ops.shippingRateDelete.mutate({ - id: params.id - }) - } - open={params.action === "remove-rate"} - title={intl.formatMessage({ - defaultMessage: "Delete Shipping Method", - description: "dialog header" - })} - variant="delete" - > - - - - - - ops.shippingRateCreate.mutate({ - input: { - maximumOrderPrice: - params.type === ShippingMethodTypeEnum.PRICE - ? formData.noLimits - ? null - : parseFloat(formData.maxValue) - : null, - maximumOrderWeight: - params.type === ShippingMethodTypeEnum.WEIGHT - ? formData.noLimits - ? null - : parseFloat(formData.maxValue) - : null, - - minimumOrderPrice: - params.type === ShippingMethodTypeEnum.PRICE - ? formData.noLimits - ? null - : parseFloat(formData.minValue) - : null, - minimumOrderWeight: - params.type === ShippingMethodTypeEnum.WEIGHT - ? formData.noLimits - ? null - : parseFloat(formData.minValue) - : null, - name: formData.name, - price: formData.isFree ? 0 : parseFloat(formData.price), - shippingZone: id, - type: params.type - } - }) - } - open={params.action === "add-rate"} - rate={undefined} - variant={params.type} - /> - - ops.shippingZoneDelete.mutate({ - id - }) - } - open={params.action === "remove"} - title={intl.formatMessage({ - defaultMessage: "Delete Shipping Zone", - description: "dialog header" - })} - variant="delete" - > - - {getStringOrPlaceholder(shippingZone?.name)} - ) - }} - /> - - - country.code) || []} - isDefault={!!shippingZone?.default} - onClose={closeModal} - onConfirm={formData => - ops.shippingZoneUpdate.mutate({ - id, - input: { - countries: formData.countries, - default: formData.restOfTheWorld - } - }) - } - open={params.action === "assign-country"} - /> - - ops.shippingZoneUpdate.mutate({ - id, - input: { - countries: shippingZone.countries - .filter(country => country.code !== params.id) - .map(country => country.code) - } - }) - } - open={params.action === "unassign-country"} - title={intl.formatMessage({ - defaultMessage: "Delete from Shipping Zone", - description: "unassign country, dialog header" - })} - variant="delete" - > - - - {getStringOrPlaceholder( - shippingZone?.countries.find( - country => country.code === params.id - )?.country - )} - - ) - }} - /> - - - - ); -}; -export default ShippingZoneDetailsDialogs; diff --git a/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx b/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx deleted file mode 100644 index cbf7db83b..000000000 --- a/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import React from "react"; - -import { getMutationProviderData } from "../../../misc"; -import { PartialMutationProviderOutput } from "../../../types"; -import { - TypedCreateShippingRate, - TypedDeleteShippingRate, - TypedDeleteShippingZone, - TypedUpdateShippingRate, - TypedUpdateShippingZone -} from "../../mutations"; -import { - CreateShippingRate, - CreateShippingRateVariables -} from "../../types/CreateShippingRate"; -import { - DeleteShippingRate, - DeleteShippingRateVariables -} from "../../types/DeleteShippingRate"; -import { - DeleteShippingZone, - DeleteShippingZoneVariables -} from "../../types/DeleteShippingZone"; -import { - UpdateShippingRate, - UpdateShippingRateVariables -} from "../../types/UpdateShippingRate"; -import { - UpdateShippingZone, - UpdateShippingZoneVariables -} from "../../types/UpdateShippingZone"; - -export interface ShippingZoneOperationsOutput { - shippingRateCreate: PartialMutationProviderOutput< - CreateShippingRate, - CreateShippingRateVariables - >; - shippingRateDelete: PartialMutationProviderOutput< - DeleteShippingRate, - DeleteShippingRateVariables - >; - shippingRateUpdate: PartialMutationProviderOutput< - UpdateShippingRate, - UpdateShippingRateVariables - >; - shippingZoneDelete: PartialMutationProviderOutput< - DeleteShippingZone, - DeleteShippingZoneVariables - >; - shippingZoneUpdate: PartialMutationProviderOutput< - UpdateShippingZone, - UpdateShippingZoneVariables - >; -} -interface ShippingZoneOperationsProps { - children: (props: ShippingZoneOperationsOutput) => React.ReactNode; - onShippingRateCreate: (data: CreateShippingRate) => void; - onShippingRateDelete: (data: DeleteShippingRate) => void; - onShippingRateUpdate: (data: UpdateShippingRate) => void; - onShippingZoneDelete: (data: DeleteShippingZone) => void; - onShippingZoneUpdate: (data: UpdateShippingZone) => void; -} - -const ShippingZoneOperations: React.FC = ({ - children, - onShippingRateCreate, - onShippingRateDelete, - onShippingRateUpdate, - onShippingZoneDelete, - onShippingZoneUpdate -}) => ( - - {(...shippingRateCreate) => ( - - {(...shippingRateDelete) => ( - - {(...shippingRateUpdate) => ( - - {(...shippingZoneDelete) => ( - - {(...shippingZoneUpdate) => - children({ - shippingRateCreate: getMutationProviderData( - ...shippingRateCreate - ), - shippingRateDelete: getMutationProviderData( - ...shippingRateDelete - ), - shippingRateUpdate: getMutationProviderData( - ...shippingRateUpdate - ), - shippingZoneDelete: getMutationProviderData( - ...shippingZoneDelete - ), - shippingZoneUpdate: getMutationProviderData( - ...shippingZoneUpdate - ) - }) - } - - )} - - )} - - )} - - )} - -); -ShippingZoneOperations.displayName = "ShippingZoneOperations"; -export default ShippingZoneOperations; diff --git a/src/shipping/views/ShippingZoneDetails/data.ts b/src/shipping/views/ShippingZoneDetails/data.ts new file mode 100644 index 000000000..0aadca15c --- /dev/null +++ b/src/shipping/views/ShippingZoneDetails/data.ts @@ -0,0 +1,62 @@ +import { ShippingZoneUrlQueryParams } from "@saleor/shipping/urls"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { UpdateShippingRateVariables } from "@saleor/shipping/types/UpdateShippingRate"; +import { CreateShippingRateVariables } from "@saleor/shipping/types/CreateShippingRate"; +import { FormData as ShippingZoneRateDialogFormData } from "../../components/ShippingZoneRateDialog"; + +export function getCreateShippingRateVariables( + data: ShippingZoneRateDialogFormData, + params: ShippingZoneUrlQueryParams, + id: string +): CreateShippingRateVariables { + return { + input: { + maximumOrderPrice: + params.type === ShippingMethodTypeEnum.PRICE + ? data.noLimits + ? null + : parseFloat(data.maxValue) + : null, + maximumOrderWeight: + params.type === ShippingMethodTypeEnum.WEIGHT + ? data.noLimits + ? null + : parseFloat(data.maxValue) + : null, + + minimumOrderPrice: + params.type === ShippingMethodTypeEnum.PRICE + ? data.noLimits + ? null + : parseFloat(data.minValue) + : null, + minimumOrderWeight: + params.type === ShippingMethodTypeEnum.WEIGHT + ? data.noLimits + ? null + : parseFloat(data.minValue) + : null, + name: data.name, + price: data.isFree ? 0 : parseFloat(data.price), + shippingZone: id, + type: params.type + } + }; +} + +export function getUpdateShippingRateVariables( + data: ShippingZoneRateDialogFormData, + params: ShippingZoneUrlQueryParams, + id: string +): UpdateShippingRateVariables { + return { + id: params.id, + input: { + maximumOrderPrice: data.noLimits ? null : parseFloat(data.maxValue), + minimumOrderPrice: data.noLimits ? null : parseFloat(data.minValue), + name: data.name, + price: data.isFree ? 0 : parseFloat(data.price), + shippingZone: id + } + }; +} diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index bf03ddef6..5bb5f6791 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -1,25 +1,47 @@ +import { diff } from "fast-array-diff"; +import DialogContentText from "@material-ui/core/DialogContentText"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; +import ActionDialog from "@saleor/components/ActionDialog"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; -import { maybe } from "../../../misc"; -import { ShippingMethodTypeEnum } from "../../../types/globalTypes"; -import ShippingZoneDetailsPage from "../../components/ShippingZoneDetailsPage"; -import { TypedShippingZone } from "../../queries"; -import { CreateShippingRate } from "../../types/CreateShippingRate"; -import { DeleteShippingRate } from "../../types/DeleteShippingRate"; -import { DeleteShippingZone } from "../../types/DeleteShippingZone"; -import { UpdateShippingRate } from "../../types/UpdateShippingRate"; -import { UpdateShippingZone } from "../../types/UpdateShippingZone"; +import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { + useShippingRateCreate, + useShippingRateUpdate, + useShippingRateDelete, + useShippingZoneDelete, + useShippingZoneUpdate +} from "@saleor/shipping/mutations"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import ShippingZoneRateDialog from "@saleor/shipping/components/ShippingZoneRateDialog"; +import useShop from "@saleor/hooks/useShop"; +import ShippingZoneCountriesAssignDialog from "@saleor/shipping/components/ShippingZoneCountriesAssignDialog"; +import NotFoundPage from "@saleor/components/NotFoundPage"; +import ShippingZoneAddWarehouseDialog from "@saleor/shipping/components/ShippingZoneAddWarehouseDialog"; +import { useWarehouseCreate } from "@saleor/warehouses/mutations"; +import { getStringOrPlaceholder, findValueInEnum } from "../../../misc"; +import { + ShippingMethodTypeEnum, + CountryCode +} from "../../../types/globalTypes"; +import ShippingZoneDetailsPage, { + FormData +} from "../../components/ShippingZoneDetailsPage"; +import { useShippingZone } from "../../queries"; import { shippingZonesListUrl, shippingZoneUrl, - ShippingZoneUrlQueryParams + ShippingZoneUrlQueryParams, + ShippingZoneUrlDialog } from "../../urls"; -import ShippingZoneDetailsDialogs from "./ShippingZoneDetailsDialogs"; -import ShippingZoneOperations from "./ShippingZoneOperations"; +import { + getCreateShippingRateVariables, + getUpdateShippingRateVariables +} from "./data"; export interface ShippingZoneDetailsProps { id: string; @@ -33,167 +55,347 @@ const ShippingZoneDetails: React.FC = ({ const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); + const shop = useShop(); - const closeModal = () => navigate(shippingZoneUrl(id), true); - - const onShippingRateCreate = (data: CreateShippingRate) => { - if (data.shippingPriceCreate.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); + const { result: searchWarehousesOpts, loadMore, search } = useWarehouseSearch( + { + variables: DEFAULT_INITIAL_SEARCH_DATA } + ); + + const { data, loading } = useShippingZone({ + displayLoader: true, + variables: { id } + }); + + const [openModal, closeModal] = createDialogActionHandlers< + ShippingZoneUrlDialog, + ShippingZoneUrlQueryParams + >(navigate, params => shippingZoneUrl(id, params), params); + const rate = data?.shippingZone?.shippingMethods.find( + rate => rate.id === params.id + ); + + const [createShippingRate, createShippingRateOpts] = useShippingRateCreate({ + onCompleted: data => { + if (data.shippingPriceCreate.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const [updateShippingRate, updateShippingRateOpts] = useShippingRateUpdate({ + onCompleted: data => { + if (data.shippingPriceUpdate.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const [deleteShippingRate, deleteShippingRateOpts] = useShippingRateDelete({ + onCompleted: data => { + if (data.shippingPriceDelete.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const [deleteShippingZone, deleteShippingZoneOpts] = useShippingZoneDelete({ + onCompleted: data => { + if (data.shippingZoneDelete.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate(shippingZonesListUrl(), true); + } + } + }); + + const [updateShippingZone, updateShippingZoneOpts] = useShippingZoneUpdate({ + onCompleted: data => { + if (data.shippingZoneUpdate.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const [createWarehouse, createWarehouseOpts] = useWarehouseCreate({ + onCompleted: data => { + if (data.createWarehouse.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + } + } + }); + + const handleSubmit = (submitData: FormData) => { + const warehouseDiff = diff( + data.shippingZone.warehouses.map(warehouse => warehouse.id), + submitData.warehouses + ); + + updateShippingZone({ + variables: { + id, + input: { + addWarehouses: warehouseDiff.added, + name: submitData.name, + removeWarehouses: warehouseDiff.removed + } + } + }); }; - const onShippingRateUpdate = (data: UpdateShippingRate) => { - if (data.shippingPriceUpdate.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - }; - - const onShippingRateDelete = (data: DeleteShippingRate) => { - if (data.shippingPriceDelete.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - }; - - const onShippingZoneDelete = (data: DeleteShippingZone) => { - if (data.shippingZoneDelete.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - navigate(shippingZonesListUrl(), true); - } - }; - - const onShippingZoneUpdate = (data: UpdateShippingZone) => { - if (data.shippingZoneUpdate.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - } - }; + if (data?.shippingZone === null) { + return navigate(shippingZonesListUrl())} />; + } return ( - - {ops => ( - - {({ data, loading }) => ( - <> - navigate(shippingZonesListUrl())} - onCountryAdd={() => - navigate( - shippingZoneUrl(id, { - action: "assign-country" - }) - ) - } - onCountryRemove={code => - navigate( - shippingZoneUrl(id, { - action: "unassign-country", - id: code - }) - ) - } - onDelete={() => - navigate( - shippingZoneUrl(id, { - action: "remove" - }) - ) - } - onPriceRateAdd={() => - navigate( - shippingZoneUrl(id, { - action: "add-rate", - type: ShippingMethodTypeEnum.PRICE - }) - ) - } - onPriceRateEdit={rateId => - navigate( - shippingZoneUrl(id, { - action: "edit-rate", - id: rateId - }) - ) - } - onRateRemove={rateId => - navigate( - shippingZoneUrl(id, { - action: "remove-rate", - id: rateId - }) - ) - } - onSubmit={formData => - ops.shippingZoneUpdate.mutate({ - id, - input: { - name: formData.name - } - }) - } - onWeightRateAdd={() => - navigate( - shippingZoneUrl(id, { - action: "add-rate", - type: ShippingMethodTypeEnum.WEIGHT - }) - ) - } - onWeightRateEdit={rateId => - navigate( - shippingZoneUrl(id, { - action: "edit-rate", - id: rateId - }) - ) - } - saveButtonBarState={ops.shippingZoneUpdate.opts.status} - shippingZone={maybe(() => data.shippingZone)} - /> - - - )} - - )} - + <> + navigate(shippingZonesListUrl())} + onCountryAdd={() => openModal("assign-country")} + onCountryRemove={code => + openModal("unassign-country", { + id: code + }) + } + onDelete={() => openModal("remove")} + onPriceRateAdd={() => + openModal("add-rate", { + type: ShippingMethodTypeEnum.PRICE + }) + } + onPriceRateEdit={rateId => + openModal("edit-rate", { + id: rateId + }) + } + onRateRemove={rateId => + openModal("remove-rate", { + id: rateId + }) + } + onSubmit={handleSubmit} + onWarehouseAdd={() => openModal("add-warehouse")} + onWeightRateAdd={() => + openModal("add-rate", { + type: ShippingMethodTypeEnum.WEIGHT + }) + } + onWeightRateEdit={rateId => + openModal("edit-rate", { + id: rateId + }) + } + saveButtonBarState={updateShippingZoneOpts.status} + shippingZone={data?.shippingZone} + warehouses={ + searchWarehousesOpts.data?.search.edges.map(edge => edge.node) || [] + } + hasMore={searchWarehousesOpts.data?.search.pageInfo.hasNextPage} + loading={searchWarehousesOpts.loading} + onFetchMore={loadMore} + onSearchChange={search} + /> + + updateShippingRate({ + variables: getUpdateShippingRateVariables(data, params, id) + }) + } + open={params.action === "edit-rate"} + rate={rate} + variant={rate?.type} + /> + + deleteShippingRate({ + variables: { + id: params.id + } + }) + } + 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} + /> + + deleteShippingZone({ + variables: { + id + } + }) + } + open={params.action === "remove"} + title={intl.formatMessage({ + defaultMessage: "Delete Shipping Zone", + description: "dialog header" + })} + variant="delete" + > + + + {getStringOrPlaceholder(data?.shippingZone.name)} + + ) + }} + /> + + + country.code) || [] + } + isDefault={data?.shippingZone?.default} + onClose={closeModal} + onConfirm={formData => + updateShippingZone({ + variables: { + id, + input: { + countries: formData.countries, + default: formData.restOfTheWorld + } + } + }) + } + open={params.action === "assign-country"} + /> + + updateShippingZone({ + variables: { + id, + input: { + countries: data.shippingZone.countries + .filter(country => country.code !== params.id) + .map(country => country.code) + } + } + }) + } + open={params.action === "unassign-country"} + title={intl.formatMessage({ + defaultMessage: "Delete from Shipping Zone", + description: "unassign country, dialog header" + })} + variant="delete" + > + + + {getStringOrPlaceholder( + data?.shippingZone?.countries.find( + country => country.code === params.id + )?.country + )} + + ) + }} + /> + + + + createWarehouse({ + variables: { + input: { + address: { + city: data.city, + cityArea: data.cityArea, + country: findValueInEnum(data.country, CountryCode), + countryArea: data.countryArea, + phone: data.phone, + postalCode: data.postalCode, + streetAddress1: data.streetAddress1, + streetAddress2: data.streetAddress2 + }, + companyName: data.companyName, + name: data.name + } + } + }) + } + /> + ); }; export default ShippingZoneDetails; diff --git a/src/shipping/views/ShippingZonesList.tsx b/src/shipping/views/ShippingZonesList.tsx index 488e629ae..dd2ab2241 100644 --- a/src/shipping/views/ShippingZonesList.tsx +++ b/src/shipping/views/ShippingZonesList.tsx @@ -16,23 +16,22 @@ import usePaginator, { import useShop from "@saleor/hooks/useShop"; import useUser from "@saleor/hooks/useUser"; import { commonMessages } from "@saleor/intl"; -import { maybe } from "@saleor/misc"; +import { maybe, getStringOrPlaceholder } from "@saleor/misc"; import { ListViews } from "@saleor/types"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import ShippingZonesListPage from "../components/ShippingZonesListPage"; import { - TypedBulkDeleteShippingZone, - TypedDeleteShippingZone, - TypedUpdateDefaultWeightUnit + useShippingZoneBulkDelete, + useShippingZoneDelete, + useDefaultWeightUnitUpdate } from "../mutations"; -import { TypedShippingZones } from "../queries"; -import { BulkDeleteShippingZone } from "../types/BulkDeleteShippingZone"; -import { DeleteShippingZone } from "../types/DeleteShippingZone"; -import { UpdateDefaultWeightUnit } from "../types/UpdateDefaultWeightUnit"; +import { useShippingZoneList } from "../queries"; import { shippingZoneAddUrl, shippingZonesListUrl, ShippingZonesListUrlQueryParams, - shippingZoneUrl + shippingZoneUrl, + ShippingZonesListUrlDialog } from "../urls"; interface ShippingZonesListProps { @@ -57,206 +56,176 @@ export const ShippingZonesList: React.FC = ({ const paginationState = createPaginationState(settings.rowNumber, params); + const [openModal, closeModal] = createDialogActionHandlers< + ShippingZonesListUrlDialog, + ShippingZonesListUrlQueryParams + >(navigate, shippingZonesListUrl, params); + + const { data, loading, refetch } = useShippingZoneList({ + displayLoader: true, + variables: paginationState + }); + + const [deleteShippingZone, deleteShippingZoneOpts] = useShippingZoneDelete({ + onCompleted: data => { + if (data.shippingZoneDelete.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + refetch(); + } + } + }); + + const [ + updateDefaultWeightUnit, + updateDefaultWeightUnitOpts + ] = useDefaultWeightUnitUpdate({ + onCompleted: data => { + if (data.shopSettingsUpdate.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + } + } + }); + + const [ + bulkDeleteShippingZone, + bulkDeleteShippingZoneOpts + ] = useShippingZoneBulkDelete({ + onCompleted: data => { + if (data.shippingZoneBulkDelete.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + reset(); + refetch(); + } + } + }); + + const { loadNextPage, loadPreviousPage, pageInfo } = paginate( + maybe(() => data.shippingZones.pageInfo), + paginationState, + params + ); return ( - - {({ data, loading, refetch }) => { - const handleUpdateDefaultWeightUnit = ( - data: UpdateDefaultWeightUnit - ) => { - if (data.shopSettingsUpdate.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - } - }; + <> + shop.defaultWeightUnit)} + settings={settings} + disabled={ + loading || + deleteShippingZoneOpts.loading || + updateDefaultWeightUnitOpts.loading + } + shippingZones={maybe(() => + data.shippingZones.edges.map(edge => edge.node) + )} + pageInfo={pageInfo} + onAdd={() => navigate(shippingZoneAddUrl)} + onBack={() => navigate(configurationMenuUrl)} + onUpdateListSettings={updateListSettings} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onRemove={id => + openModal("remove", { + id + }) + } + onRowClick={id => () => navigate(shippingZoneUrl(id))} + onSubmit={unit => + updateDefaultWeightUnit({ + variables: { unit } + }) + } + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + toolbar={ + + openModal("remove-many", { + ids: listElements + }) + } + > + + + } + userPermissions={user?.userPermissions || []} + /> - const closeModal = () => - navigate( - shippingZonesListUrl({ - ...params, - action: undefined, - ids: undefined - }), - true - ); - - const handleShippingZoneDelete = (data: DeleteShippingZone) => { - if (data.shippingZoneDelete.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - refetch(); - } - }; - - const handleBulkDeleteShippingZone = (data: BulkDeleteShippingZone) => { - if (data.shippingZoneBulkDelete.errors.length === 0) { - notify({ - text: intl.formatMessage(commonMessages.savedChanges) - }); - closeModal(); - reset(); - refetch(); - } - }; - return ( - - {(deleteShippingZone, deleteShippingZoneOpts) => ( - - {(updateDefaultWeightUnit, updateDefaultWeightUnitOpts) => ( - - {(bulkDeleteShippingZone, bulkDeleteShippingZoneOpts) => { - const { - loadNextPage, - loadPreviousPage, - pageInfo - } = paginate( - maybe(() => data.shippingZones.pageInfo), - paginationState, - params - ); - - return ( - <> - shop.defaultWeightUnit - )} - settings={settings} - disabled={ - loading || - deleteShippingZoneOpts.loading || - updateDefaultWeightUnitOpts.loading - } - shippingZones={maybe(() => - data.shippingZones.edges.map(edge => edge.node) - )} - pageInfo={pageInfo} - onAdd={() => navigate(shippingZoneAddUrl)} - onBack={() => navigate(configurationMenuUrl)} - onUpdateListSettings={updateListSettings} - onNextPage={loadNextPage} - onPreviousPage={loadPreviousPage} - onRemove={id => - navigate( - shippingZonesListUrl({ - ...params, - action: "remove", - id - }) - ) - } - onRowClick={id => () => - navigate(shippingZoneUrl(id))} - onSubmit={unit => - updateDefaultWeightUnit({ - variables: { unit } - }) - } - isChecked={isSelected} - selected={listElements.length} - toggle={toggle} - toggleAll={toggleAll} - toolbar={ - - navigate( - shippingZonesListUrl({ - action: "remove-many", - ids: listElements - }) - ) - } - > - - - } - userPermissions={user?.userPermissions || []} - /> - - - deleteShippingZone({ - variables: { id: params.id } - }) - } - > - - - {maybe( - () => - data.shippingZones.edges.find( - edge => edge.node.id === params.id - ).node.name, - "..." - )} - - ) - }} - /> - - - - bulkDeleteShippingZone({ - variables: { ids: params.ids } - }) - } - > - - params.ids.length), - displayQuantity: ( - - {maybe(() => params.ids.length)} - - ) - }} - /> - - - - ); - }} - - )} - - )} - - ); - }} - + + deleteShippingZone({ + variables: { id: params.id } + }) + } + > + + + {maybe( + () => + data.shippingZones.edges.find( + edge => edge.node.id === params.id + ).node.name, + "..." + )} + + ) + }} + /> + + + + bulkDeleteShippingZone({ + variables: { ids: params.ids } + }) + } + > + + + {getStringOrPlaceholder(params.ids?.length.toString())} + + ) + }} + /> + + + ); }; ShippingZonesList.displayName = "ShippingZonesList"; diff --git a/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx b/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx deleted file mode 100644 index bbf07bee7..000000000 --- a/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import { makeStyles } from "@material-ui/core/styles"; -import TextField from "@material-ui/core/TextField"; -import React from "react"; -import { useIntl, IntlShape } from "react-intl"; - -import CardTitle from "@saleor/components/CardTitle"; -import FormSpacer from "@saleor/components/FormSpacer"; -import Grid from "@saleor/components/Grid"; -import SingleAutocompleteSelectField, { - SingleAutocompleteChoiceType -} from "@saleor/components/SingleAutocompleteSelectField"; -import { ChangeEvent } from "@saleor/hooks/useForm"; -import { getFormErrors } from "@saleor/utils/errors"; -import { ShopErrorFragment } from "@saleor/siteSettings/types/ShopErrorFragment"; -import getShopErrorMessage from "@saleor/utils/errors/shop"; -import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment"; -import getAccountErrorMessage from "@saleor/utils/errors/account"; -import { SiteSettingsPageFormData } from "../SiteSettingsPage"; - -interface SiteSettingsAddressProps { - countries: SingleAutocompleteChoiceType[]; - data: SiteSettingsPageFormData; - displayCountry: string; - errors: Array; - disabled: boolean; - onChange: (event: ChangeEvent) => void; - onCountryChange: (event: ChangeEvent) => void; -} - -const useStyles = makeStyles( - { - root: { - overflow: "visible" - } - }, - { name: "SiteSettingsAddress" } -); - -function getErrorMessage( - err: AccountErrorFragment | ShopErrorFragment, - intl: IntlShape -): string { - if (err?.__typename === "AccountError") { - return getAccountErrorMessage(err, intl); - } - - return getShopErrorMessage(err, intl); -} - -const SiteSettingsAddress: React.FC = props => { - const { - countries, - data, - disabled, - displayCountry, - errors, - onChange, - onCountryChange - } = props; - - const classes = useStyles(props); - const intl = useIntl(); - - const formFields = [ - "companyName", - "streetAddress1", - "streetAddress2", - "city", - "postalCode", - "country", - "companyArea", - "phone" - ]; - const formErrors = getFormErrors(formFields, errors); - - return ( - - - - - - - - - - - - - - - - - - - - - - - ); -}; -SiteSettingsAddress.displayName = "SiteSettingsAddress"; -export default SiteSettingsAddress; diff --git a/src/siteSettings/components/SiteSettingsAddress/index.ts b/src/siteSettings/components/SiteSettingsAddress/index.ts deleted file mode 100644 index b15a23918..000000000 --- a/src/siteSettings/components/SiteSettingsAddress/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./SiteSettingsAddress"; -export * from "./SiteSettingsAddress"; diff --git a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx index dab35c263..7fa681fa7 100644 --- a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx +++ b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx @@ -17,10 +17,10 @@ import { commonMessages, sectionNames } from "@saleor/intl"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { mapCountriesToChoices } from "@saleor/utils/maps"; import { ShopErrorFragment } from "@saleor/siteSettings/types/ShopErrorFragment"; +import CompanyAddressInput from "@saleor/components/CompanyAddressInput"; import { maybe } from "../../../misc"; import { AuthorizationKeyType } from "../../../types/globalTypes"; import { SiteSettings_shop } from "../../types/SiteSettings"; -import SiteSettingsAddress from "../SiteSettingsAddress/SiteSettingsAddress"; import SiteSettingsDetails from "../SiteSettingsDetails/SiteSettingsDetails"; import SiteSettingsKeys from "../SiteSettingsKeys/SiteSettingsKeys"; import SiteSettingsMailing, { @@ -200,12 +200,16 @@ const SiteSettingsPage: React.FC = props => {
- diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 256772bbf..7d474c182 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -2640,6 +2640,49 @@ exports[`Storyshots Generics / Global messages with undo action 1`] = `
`; +exports[`Storyshots Generics / Link with choices default 1`] = ` +
+
+
+
+ + + Ă…land Islands + + + +
+
+
+
+`; + exports[`Storyshots Generics / Money formatting default 1`] = `
`; +exports[`Storyshots Generics / Select with autocomplete with add 1`] = ` +
+
+
+
+
+ +
+ + + + + + + + + + +
+
+
+ +
+
+
+
+
+
+`; + exports[`Storyshots Generics / SingleSelectField with error hint 1`] = `
-
-
-
- -
-
-
- Shipping Methods -
-
- Manage how you ship out orders -
-
-
-
@@ -34586,6 +34743,99 @@ exports[`Storyshots Views / Configuration default 1`] = `
+
+
+
+ Product Settings +
+
+
+
+
+
+ +
+
+
+ Shipping Methods +
+
+ Manage how you ship out orders +
+
+
+
+
+
+
+ +
+
+
+ Warehouses +
+
+ Manage and update your warehouse information +
+
+
+
+
+
@@ -35009,6 +35259,61 @@ exports[`Storyshots Views / Configuration partial access 1`] = `
+
+
+
+ Product Settings +
+
+
+
+
+
+ +
+
+
+ Warehouses +
+
+ Manage and update your warehouse information +
+
+
+
+
+
@@ -64003,6 +64308,1543 @@ exports[`Storyshots Views / Orders / Draft order list when no data 1`] = `
`; +exports[`Storyshots Views / Orders / Fulfill order default 1`] = ` +
+
+
+
+ Order no. 9123 - Add Fulfillment +
+
+
+
+
+
+
+
+ + Items ready to ship + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Product name + + SKU + + C our wares + + Be stocked + + A Warehouse + + Darkwares + + Quantity to fulfill +
+
+
+ +
+
+ T-Shirt +
+ S +
+
+
+
+ 62783187 + +
+
+
+ + +
+
+
+ / 1207 +
+
+
+
+
+
+ + +
+
+
+ / 1197 +
+
+
+
+
+
+ + +
+
+
+ / 1213 +
+
+
+
+
+
+ + +
+
+
+ / 1213 +
+
+
+ + 0 + + / 2 +
+
+
+ +
+
+ Lemon Juice +
+ 2.5l +
+
+
+
+ 998323583 + + No Stock + +
+
+
+ + +
+
+
+ / 758 +
+
+
+
+
+
+ + +
+
+
+ / 727 +
+
+
+
+
+
+ + +
+
+
+ / 756 +
+
+
+ + 0 + + / 4 +
+
+
+ +
+
+ Orange Juice +
+ 5l +
+
+
+
+ 998323584 + +
+
+
+ + +
+
+
+ / 587 +
+
+
+ No Stock + +
+
+
+ + +
+
+
+ / 586 +
+
+
+ No Stock + + + 0 + + / 1 +
+
+
+ +
+
+ +
+
+`; + +exports[`Storyshots Views / Orders / Fulfill order error 1`] = ` +
+
+
+
+ Order no. 9123 - Add Fulfillment +
+
+
+
+
+
+
+
+ + Items ready to ship + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Product name + + SKU + + C our wares + + Be stocked + + A Warehouse + + Darkwares + + Quantity to fulfill +
+
+
+ +
+
+ T-Shirt +
+ S +
+
+
+
+ 62783187 + +
+
+
+ + +
+
+
+ / 1207 +
+
+
+
+
+
+ + +
+
+
+ / 1197 +
+
+
+
+
+
+ + +
+
+
+ / 1213 +
+
+
+
+
+
+ + +
+
+
+ / 1213 +
+
+
+ + 0 + + / 2 +
+
+
+ +
+
+ Lemon Juice +
+ 2.5l +
+
+
+
+ 998323583 + + No Stock + +
+
+
+ + +
+
+
+ / 758 +
+
+
+
+
+
+ + +
+
+
+ / 727 +
+
+
+
+
+
+ + +
+
+
+ / 756 +
+
+
+ + 0 + + / 4 +
+
+
+ +
+
+ Orange Juice +
+ 5l +
+
+
+
+ 998323584 + +
+
+
+ + +
+
+
+ / 587 +
+
+
+ No Stock + +
+
+
+ + +
+
+
+ / 586 +
+
+
+ No Stock + + + 0 + + / 1 +
+
+
+ +
+
+ +
+
+`; + +exports[`Storyshots Views / Orders / Fulfill order loading 1`] = ` +
+
+
+
+ Order no. - Add Fulfillment +
+
+
+
+
+
+
+
+ + Items ready to ship + +
+
+
+
+
+ + + + + + + + + + + + + + + +
+ Product name + + SKU + + Quantity to fulfill +
+
+
+ +
+
+ + ‌ + +
+
+
+ + ‌ + + + + + ‌ + +
+
+
+ +
+
+ +
+
+`; + exports[`Storyshots Views / Orders / Order details cancelled 1`] = `
- Fulfilled (1) + Cancelled (1)
@@ -64277,6 +66119,28 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -64296,7 +66160,7 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = `
- Fulfilled (1) + Cancelled (1)
@@ -64399,6 +66263,35 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
@@ -65130,6 +67023,28 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -65293,24 +67208,38 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -66283,24 +68234,38 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ ... +
+
+
+ +
@@ -67658,6 +69645,28 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -67821,24 +69830,38 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -68811,24 +70856,38 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -69801,24 +71882,38 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -70791,24 +72908,38 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -71781,24 +73934,38 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -72771,24 +74960,38 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -73761,24 +75986,38 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -74751,24 +77012,38 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -75741,24 +78038,38 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
+ + +
+ Fulfilled from: +
+ Be stocked +
+
+
+ +
@@ -76731,24 +79064,38 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` $79.71 + + +
+ Fulfilled from: +
+ C our wares +
+
+
+ Tracking Number: +
+ 01nn12399su12nndfsy +
+
+ +
-
- -
- - - - +
- - - @@ -79614,21 +81928,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79677,21 +81976,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79740,21 +82024,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79803,21 +82072,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79866,21 +82120,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79929,21 +82168,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -79992,21 +82216,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80055,21 +82264,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80118,21 +82312,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80181,21 +82360,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80244,21 +82408,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80307,21 +82456,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80370,21 +82504,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80433,21 +82552,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80496,21 +82600,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80559,21 +82648,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80622,21 +82696,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80685,21 +82744,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80748,21 +82792,6 @@ exports[`Storyshots Views / Orders / Order list default 1`] = ` - - - @@ -80958,27 +82987,7 @@ exports[`Storyshots Views / Orders / Order list loading 1`] = ` - - - - +
- - - @@ -81415,9 +83407,7 @@ exports[`Storyshots Views / Orders / Order list when no data 1`] = ` - +
No orders found @@ -97185,1781 +99175,274 @@ exports[`Storyshots Views / Product types / Unassign multiple attributes default /> `; -exports[`Storyshots Views / Products / Create multiple variants / summary default 1`] = ` +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs apply to all 1`] = `
-
-
+ -
+
+
+
+
+
+
+
-
-`; - -exports[`Storyshots Views / Products / Create multiple variants / summary errors 1`] = ` -
-
-
-
-
-
-
- Select Values -
-
-
-
- Prices and SKU -
-
-
-
- Summary -
-
-
-
-
- You will create variants below -
-
-
-
-
- Variant -
-
- Price -
-
- Inventory -
-
- SKU -
-
-
-
- - 100g - - - Arabica - - - Round - -
-
-
-
- - USD - -
-
-
-
-
-
- - -
-
-
-
-
-
- - -
-
-
-
- -
-
-
-
- - 100g - - - Arabica - - - Polo - -
-
-
-
- - USD - -
-
-
-
-
-
- - -
-
-
-
-
-
- - -
-
-
-
- -
-
-
-
- - 500g - - - Arabica - - - Round - -
-
-
-
- - USD - -
-
-
-
-
-
- - -
-
-
-
-
-
- - -
-
-
-
- -
-
-
-
- - 500g - - - Arabica - - - Polo - -
-
-
-
- - USD - -
-
-
-
-
-
- - -
-
-
-
-
-
- - -
-

- SKUs must be unique -

-
-
-
- -
-
-
-
-
-
-
-
-`; - -exports[`Storyshots Views / Products / Create multiple variants choose values 1`] = ` -
-
-
-
-
-
-
- Select Values -
-
-
-
- Prices and SKU -
-
-
-
- Summary -
-
-
-
-
- Box Size -
-
-
- - - - -
-
- Coffee Genre -
-
-
- - -
-
- Collar -
-
-
- - - -
-
-
-
-
-
-`; - -exports[`Storyshots Views / Products / Create multiple variants interactive 1`] = ` -
-`; - -exports[`Storyshots Views / Products / Create multiple variants prices and SKU 1`] = ` -
-
-
-
-
-
-
- Select Values -
-
-
-
- Prices and SKU -
-
-
-
- Summary -
-
-
-
-
- Price -
-
-
- + + ​ + + + +
+
+
+
+
+
+
+
+ class="MuiTypography-root-id MuiTypography-body1-id" + > + 100g +
+
+
-
-
+
+
+
+
-
-
+
+
+
+ +
+ + USD +
-
-
+
+
+
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ Warehouses +
+
+ Based on your selections we will create 5 products. Use this step to customize price and stocks for your new products +
+
+ + + + +
+
+
+
+
+ Stock +
+
+ +
+
+
+ C our wares +
+
+ +
+
-
-
-
-
-
-
- Arabica -
-
+
+ Be stocked +
@@ -99144,19 +99789,60 @@ exports[`Storyshots Views / Products / Create multiple variants prices and SKU 1 class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-filled-id" data-shrink="true" > - Price + Stock
+ +
+
+
+
+
+ Darkwares +
+
+ +
+ - USD
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs apply to all when one warehouse 1`] = ` +
+
+
+
+ + Price + +
+
+
+
+
+
+ +
+
+
+ class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-formControl-id MuiInputBase-adornedEnd-id MuiOutlinedInput-adornedEnd-id" + > + + USD + +
+
+
+ +
+
+
+
+ Choose attribute +
+
+
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+
+ 100g +
+
+
+
+ USD
-
-
+
+
+
+
-
-
+
+
+
+ +
+ + USD +
-
-
+
+
+
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ Stock +
+
+
-
-
-
-
-
-
+ + -
- Arabica -
-
+ Apply single stock to all SKUs + + +
+
+ C our wares +
@@ -99435,7 +100445,852 @@ exports[`Storyshots Views / Products / Create multiple variants prices and SKU 1 + +
+
+
+
+
+ +
+ +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs apply to attribute 1`] = ` +
+
+
+
+ + Price + +
+
+
+
+
+
+ +
+
+ +
+ + USD + +
+
+
+ +
+
+
+
+
+ Choose attribute +
+
+
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+
+ 100g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ 500g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ Warehouses +
+
+ Based on your selections we will create 5 products. Use this step to customize price and stocks for your new products +
+
+ + + + +
+
+
+
+
+ Stock +
+
+ +
+ +
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+ C our wares +
+
+ Be stocked +
+
+ Darkwares +
+
+ 100g +
+
+ +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ 500g +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs apply to attribute when one warehouse 1`] = ` +
+
+
+
+ + Price + +
+
+
+
+
+
+ +
+
+ +
+ + USD + +
+
+
+ +
+
+
+
+
+ Choose attribute +
+
+
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+
+ 100g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ 500g +
+
+
+
+ +
+ + USD + +
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ Stock +
+
+ +
+ +
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+ C our wares +
+
+ 100g +
+
+ +
+ + +
+
+
+ 500g +
+
+ +
+ + +
+
+
+
+
+
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants / summary default 1`] = ` +
+
+
+
+ + Created Variants + +
+
+
+
+
+
+ Variant +
+
+ Price +
+
+ C our wares +
+
+ Be stocked +
+
+ Darkwares +
+
+ SKU +
+
+
+
+ + 100g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 100g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 500g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 500g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants / summary errors 1`] = ` +
+
+
+
+ + Created Variants + +
+
+
+
+
+
+ Variant +
+
+ Price +
+
+ C our wares +
+
+ Be stocked +
+
+ Darkwares +
+
+ SKU +
+
+
+
+ + 100g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 100g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 500g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+ + 500g + + + Arabica + +
+
+
+
+ + USD + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+

+ SKUs must be unique +

+
+
+
+ +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants choose values 1`] = ` +
+
+
+
+ + Box Size + +
+
+
+
+
+ + + + +
+
+
+
+
+ + Coffee Genre + +
+
+
+
+
+ + +
+
+
+
+
+`; + +exports[`Storyshots Views / Products / Create multiple variants interactive 1`] = ` +
+
+
+
+
+ Select Values +
+
+
+
+ Prices and SKU +
+
+
+
+ Summary +
+
+
+
+
+ Choose Values +
+ Selected values will be used to create variants for the configurable product. +
+
+
+
+ +
+
+
+
+
+
+ + Author + +
+
+
+
+
+ + +
+
+
+
+
+ + Box Size + +
+
+
+
+
+ + + + +
+
+
+
+
+ + Coffee Genre + +
+
+
+
+
+ + +
+
+
+
+
+ + Color + +
+
+
+
+
+ + +
+
+
`; @@ -99977,109 +105208,6 @@ Ctrl + K"
-
-
- - Inventory - -
-
-
-
-
-
-
- -
- - -
-
-
- -
- - -
-
-
-
-
-
@@ -101042,107 +106170,6 @@ Ctrl + K"
-
-
- - Inventory - -
-
-
-
-
-
-
- -
- - -
-
-
- -
- - -
-
-
-
-
-
@@ -102104,112 +107131,6 @@ Ctrl + K"
-
-
- - Inventory - -
-
-
-
-
-
-
- -
- - -
-

- Invalid value -

-
-
- -
- - -
-
-
-
-
-
@@ -103021,7 +107942,7 @@ exports[`Storyshots Views / Products / Create product variant add first variant - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
@@ -103534,7 +108519,7 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
@@ -103971,7 +109020,7 @@ exports[`Storyshots Views / Products / Create product variant when loading data - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
@@ -104499,7 +109613,7 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
@@ -104606,6 +109779,30 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] =
`; +exports[`Storyshots Views / Products / Edit warehouses default 1`] = ` +
+`; + +exports[`Storyshots Views / Products / Edit warehouses loading confirmation 1`] = ` +
+`; + +exports[`Storyshots Views / Products / Edit warehouses loading warehouses 1`] = ` +
+`; + +exports[`Storyshots Views / Products / Edit warehouses with error 1`] = ` +
+`; + exports[`Storyshots Views / Products / Product edit form errors 1`] = `
+
+
+ Available inventoty at: +
+ + + All Warehouses + + + +
@@ -105850,13 +111081,7 @@ Ctrl + K" class="MuiTableCell-root-id MuiTableCell-head-id ProductVariants-colName-id" scope="col" > - Name - - - Status + Variant SKU + + Inventory + Cordoba Oro - -
- Available -
- 87192-94370 + + 5 available at 2 locations + silver - -
- Available -
- 69055-15190 + + 13 available at 1 location + @@ -107355,6 +112576,40 @@ Ctrl + K"
+
+
+ Available inventoty at: +
+ + + All Warehouses + + + +
@@ -107387,13 +112642,7 @@ Ctrl + K" class="MuiTableCell-root-id MuiTableCell-head-id ProductVariants-colName-id" scope="col" > - Name - - - Status + Variant SKU + + Inventory + Cordoba Oro - -
- Available -
- 87192-94370 + + 5 available at 2 locations + silver - -
- Available -
- 69055-15190 + + 13 available at 1 location + @@ -107938,6 +113183,1786 @@ Ctrl + K"
`; +exports[`Storyshots Views / Products / Product edit no stock and no variants 1`] = ` +
+
+
+
+
+ Ergonomic Plastic Bacon +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ Description +
+
+ +
+
+
+
+
+
+ + + bold + + +
+
+
+
+ + + italic + + +
+
+
+
+ + + strikethrough + + +
+
+

+
+ + + h1 + + +
+

+

+
+ + + h2 + + +
+

+

+
+ + + h3 + + +
+

+
+
+ + + blockquote + + +
+
+
    +
  • +
    + + + ul + + +
    +
  • +
+
    +
  1. +
    + + + ol + + +
    +
  2. +
+
+
+ + + link + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Images + +
+ + +
+
+
+
+
+
+
+ + +
+ Drop here to upload +
+
+
+
+
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 2 Attributes +
+
+ +
+
+
+
+
+ Borders +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Legacy +
+
+
+
+
+ + +
+
+
+
+
+
+ Auto Loan Account +
+ +
+
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+ +
+
+
+
+
+ +
+ +
+
+ NZD +
+
+ +
+
+
+
+
+
+
+
+ + Inventory + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
+
+
+
+
+ + Search Engine Preview + +
+ +
+
+
+
+
+
+ Add search engine title and description to make this product easier to find +
+
+
+
+
+
+
+ + Organize Product + +
+
+
+
+
+
+ Product Type +
+
+ Versatile +
+
+
+ Product Type +
+
+ Simple +
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ + +

+ *Optional. Adding product to collection helps users find it. +

+
+
+
+
+
+
+ Winter sale +
+ +
+
+
+
+
+
+
+
+ + Visibility + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ +
+`; + exports[`Storyshots Views / Products / Product edit no variants 1`] = `
- Variants + Inventory
- -
+ />
- - - - - - - - - - + ​ + + + + + + +
+
+ + + Track Inventory +
-
- - - - - - - - - - - -
+
+ +
- Name - - Status - - SKU -
+ Active inventory tracking will automatically calculate changes of stock +
+ +
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ C our wares + +
+
+ + +
+
+
+ Be stocked + +
+
+ + +
+
+
- Configurable + Simple
+
+
+ Available inventoty at: +
+ + + All Warehouses + + + +
@@ -110957,13 +118079,7 @@ Ctrl + K" class="MuiTableCell-root-id MuiTableCell-head-id ProductVariants-colName-id" scope="col" > - Name - - - Status + Variant SKU + + Inventory + Cordoba Oro - -
- Available -
- 87192-94370 + + 5 available at 2 locations + silver - -
- Available -
- 69055-15190 + + 13 available at 1 location + @@ -112109,10 +119221,10 @@ Ctrl + K" class="MuiCardContent-root-id" >
-
+
+ +
+
+
+
+
+ + Quantity + +
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
+
+
+ Available inventoty at: +
+ + + All Warehouses + + + +
@@ -113744,13 +120959,7 @@ Ctrl + K" class="MuiTableCell-root-id MuiTableCell-head-id ProductVariants-colName-id" scope="col" > - Name - - - Status + Variant SKU + + Inventory + Cordoba Oro - -
- Available -
- 87192-94370 + + 5 available at 2 locations + silver - -
- Available -
- 69055-15190 + + 13 available at 1 location + @@ -115488,10 +122693,10 @@ Ctrl + K" class="MuiCardContent-root-id" >
-
+
+
+
+
+
+
+ + Quantity + +
+ + + + + + + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ C our wares + +
+
+ + +
+
+
+ Be stocked + +
+
+ + +
+
+
- Stock + Inventory
-
-
+
-
+
+
+ +
+
+
+
+
+ + Quantity + +
+ + + + + + + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ Warehouse 1 + +
+
+ + +
+
+
+ Warehouse 2 + +
+
+ + +
+
+
@@ -122385,7 +129848,7 @@ exports[`Storyshots Views / Products / Product variant details when loaded data - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ Warehouse 1 + +
+
+ + +
+
+
+ Warehouse 2 + +
+
+ + +
+
+
@@ -122833,7 +130423,7 @@ exports[`Storyshots Views / Products / Product variant details when loading data - Stock + Inventory
-
-
+
-
-
+ SKU (Stock Keeping Unit) +
-
+
+
+
+
+
+
+ + Quantity + + +
+
+
+ + + + + + + + + + + + +
+ Warehouse Name + + Quantity Available +
+ This product doesn't have any stock. You can add it + + here + + . +
@@ -131548,6 +139203,171 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
+
+
+
+ + Warehouse + +
+
+
+
+
+
+
+ + +

+ Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes. +

+
+
+
+
+
+
+ C our wares +
+ +
+
+
+
+
+ Be stocked +
+ +
+
+
+
+
+
@@ -132178,6 +139998,171 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
+
+
+
+ + Warehouse + +
+
+
+
+
+
+
+ + +

+ Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes. +

+
+
+
+
+
+
+ C our wares +
+ +
+
+
+
+
+ Be stocked +
+ +
+
+
+
+
+
@@ -132198,7 +140183,12 @@ exports[`Storyshots Views / Shipping / Shipping zone details loading 1`] = `
- ... + + ‌ +
+
+
+
+ + Warehouse + +
+
+
+
+
+
+
+ + +

+ Select warehouse from which you will ship products for this shipping zone. This warehouse address will also be used to calculate taxes. +

+
+
+
+
+
+
@@ -134935,7 +143021,7 @@ exports[`Storyshots Views / Site settings / Page default 1`] = `
- -
- - -
-
-
-
- -
- - -
-
-
-
- -
- - -
-
-
-
- City + Company
+
@@ -135116,7 +143092,7 @@ exports[`Storyshots Views / Site settings / Page default 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-filled-id" data-shrink="true" > - ZIP / Postal code + Address line 1
-
-
-
-
- - -
-
+ class="FormSpacer-spacer-id" + />
@@ -135217,7 +143130,7 @@ exports[`Storyshots Views / Site settings / Page default 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" data-shrink="false" > - Country area + Address line 2
-
-
-
-
+
- - + City + +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
@@ -135806,7 +143896,7 @@ exports[`Storyshots Views / Site settings / Page form errors 1`] = `
- -
- - -
-
-
-
- -
- - -
-
-
-
- -
- - -
-
-
-
- City + Company
+
@@ -135987,7 +143967,7 @@ exports[`Storyshots Views / Site settings / Page form errors 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id MuiFormLabel-filled-id" data-shrink="true" > - ZIP / Postal code + Address line 1
-
-
-
-
- - -
-
+ class="FormSpacer-spacer-id" + />
@@ -136088,7 +144005,7 @@ exports[`Storyshots Views / Site settings / Page form errors 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" data-shrink="false" > - Country area + Address line 2
-
-
-
-
+
- - + City + +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
@@ -136673,7 +144767,7 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = `
- -
- - -
-
-
-
- -
- - -
-
-
-
- -
- - -
-
-
-
- City + Company
@@ -136852,6 +144829,9 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = `
+
@@ -136859,7 +144839,7 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id" data-shrink="false" > - ZIP / Postal code + Address line 1
@@ -136888,15 +144868,50 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = `
-
-
-
+
+ +
+ + +
+
+
+
- Country + City +
+
+ +
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
@@ -136962,7 +145099,7 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id MuiFormLabel-disabled-id MuiInputLabel-disabled-id" data-shrink="false" > - Country area + Phone
@@ -136992,45 +145129,6 @@ exports[`Storyshots Views / Site settings / Page loading 1`] = `
-
-
- -
- - -
-

`; +exports[`Storyshots Views / Warehouses / Create warehouse default 1`] = ` +
+
+
+
+
+ Create Warehouse +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Create warehouse form errors 1`] = ` +
+
+
+
+
+ Create Warehouse +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+ + +

+ Invalid value +

+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Create warehouse loading 1`] = ` +
+
+
+
+
+ Create Warehouse +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Warehouse details default 1`] = ` +
+
+
+
+
+ C our wares +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
+ + Shipping Zones + +
+
+ +
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Warehouse details form errors 1`] = ` +
+
+
+
+
+ C our wares +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+ + +

+ Invalid value +

+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+

+ Invalid value +

+
+
+
+
+
+
+
+
+ + Shipping Zones + +
+
+ +
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Warehouse details loading 1`] = ` +
+
+
+
+
+ + ‌ + +
+
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Address Information + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
+ + Shipping Zones + +
+
+
+
+
+ + ‌ + +
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Warehouses / Warehouse list default 1`] = ` +
+
+
+
+ Warehouses +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Name +
+ +
+
+ Shipping Zones + + Actions +
+ C our wares + + Europe, Oceania, Asia, Americas, Africa + +
+ + +
+
+ Be stocked + + Europe, Oceania, Asia, Americas, Africa + +
+ + +
+
+ A Warehouse + + Europe, Oceania, Asia, Americas, Africa + +
+ + +
+
+ Darkwares + + Europe, Oceania, Asia, Americas, Africa + +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Views / Warehouses / Warehouse list loading 1`] = ` +
+
+
+
+ Warehouses +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ Name +
+ +
+
+ Shipping Zones + + Actions +
+ + ‌ + + + + ‌ + + +
+ + +
+
+
+
+
+
+`; + +exports[`Storyshots Views / Warehouses / Warehouse list no data 1`] = ` +
+
+
+
+ Warehouses +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ Name +
+ +
+
+ Shipping Zones + + Actions +
+ No warehouses found +
+
+
+
+
+`; + exports[`Storyshots Views / Webhooks / Create webhook default 1`] = `
undefined, onConfirm: () => undefined, - open: true + open: true, + warehouses: warehouseList }; storiesOf("Orders / OrderFulfillmentCancelDialog", module) diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index 306c5eca9..4d37c89ed 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -3,6 +3,7 @@ import React from "react"; import { fetchMoreProps } from "@saleor/fixtures"; import { ProductErrorCode } from "@saleor/types/globalTypes"; +import { warehouseList } from "@saleor/warehouses/fixtures"; import ProductCreatePage, { ProductCreatePageSubmitData } from "../../../products/components/ProductCreatePage"; @@ -32,6 +33,8 @@ storiesOf("Views / Products / Create product", module) onBack={() => undefined} onSubmit={() => undefined} saveButtonBarState="default" + onWarehouseEdit={() => undefined} + warehouses={warehouseList} /> )) .add("When loading", () => ( @@ -52,6 +55,8 @@ storiesOf("Views / Products / Create product", module) onBack={() => undefined} onSubmit={() => undefined} saveButtonBarState="default" + onWarehouseEdit={() => undefined} + warehouses={undefined} /> )) .add("form errors", () => ( @@ -78,5 +83,7 @@ storiesOf("Views / Products / Create product", module) onBack={() => undefined} onSubmit={() => undefined} saveButtonBarState="default" + onWarehouseEdit={() => undefined} + warehouses={warehouseList} /> )); diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index 4deb5d08e..acff0e2d3 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -30,11 +30,11 @@ const props: ProductUpdatePageProps = { onDelete: () => undefined, onImageDelete: () => undefined, onImageUpload: () => undefined, - onProductShow: () => undefined, onSubmit: () => undefined, onVariantAdd: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, + onWarehousesEdit: () => undefined, placeholderImage, product, saveButtonBarState: "default", @@ -73,7 +73,28 @@ storiesOf("Views / Products / Product edit", module) {...props} product={{ ...props.product, - variants: [] + productType: { + ...product.productType, + hasVariants: false + } + }} + /> + )) + .add("no stock and no variants", () => ( + )) diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index 9269ae5e2..2d83d76c5 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -3,6 +3,7 @@ import React from "react"; import placeholderImage from "@assets/images/placeholder255x255.png"; import { ProductErrorCode } from "@saleor/types/globalTypes"; +import { warehouseList } from "@saleor/warehouses/fixtures"; import ProductVariantCreatePage from "../../../products/components/ProductVariantCreatePage"; import { product as productFixture } from "../../../products/fixtures"; import Decorator from "../../Decorator"; @@ -14,19 +15,22 @@ storiesOf("Views / Products / Create product variant", module) .add("default", () => ( undefined} onSubmit={() => undefined} onVariantClick={undefined} saveButtonBarState="default" + warehouses={warehouseList} + onWarehouseEdit={() => undefined} /> )) .add("with errors", () => ( undefined} onSubmit={() => undefined} onVariantClick={undefined} saveButtonBarState="default" + warehouses={warehouseList} + onWarehouseEdit={() => undefined} /> )) .add("when loading data", () => ( undefined} onSubmit={() => undefined} onVariantClick={undefined} saveButtonBarState="default" + warehouses={warehouseList} + onWarehouseEdit={() => undefined} /> )) .add("add first variant", () => ( undefined} onVariantClick={undefined} saveButtonBarState="default" + warehouses={warehouseList} + onWarehouseEdit={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index e8413b3e4..4cd3853ce 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -23,6 +23,7 @@ storiesOf("Views / Products / Product variant details", module) onSubmit={() => undefined} onVariantClick={() => undefined} saveButtonBarState="default" + onWarehousesEdit={() => undefined} /> )) .add("when loading data", () => ( @@ -38,6 +39,7 @@ storiesOf("Views / Products / Product variant details", module) onSubmit={() => undefined} onVariantClick={() => undefined} saveButtonBarState="default" + onWarehousesEdit={() => undefined} /> )) .add("attribute errors", () => ( @@ -69,5 +71,6 @@ storiesOf("Views / Products / Product variant details", module) message: "Generic form error", ...error }))} + onWarehousesEdit={() => undefined} /> )); diff --git a/src/storybook/stories/shipping/ShippingZoneDetailsPage.tsx b/src/storybook/stories/shipping/ShippingZoneDetailsPage.tsx index 0bfc95c76..d510de60f 100644 --- a/src/storybook/stories/shipping/ShippingZoneDetailsPage.tsx +++ b/src/storybook/stories/shipping/ShippingZoneDetailsPage.tsx @@ -2,6 +2,8 @@ import { storiesOf } from "@storybook/react"; import React from "react"; import { ShippingErrorCode } from "@saleor/types/globalTypes"; +import { warehouseList } from "@saleor/warehouses/fixtures"; +import { fetchMoreProps, searchPageProps } from "@saleor/fixtures"; import ShippingZoneDetailsPage, { ShippingZoneDetailsPageProps } from "../../../shipping/components/ShippingZoneDetailsPage"; @@ -9,6 +11,8 @@ import { shippingZone } from "../../../shipping/fixtures"; import Decorator from "../../Decorator"; const props: ShippingZoneDetailsPageProps = { + ...fetchMoreProps, + ...searchPageProps, disabled: false, errors: [], onBack: () => undefined, @@ -19,10 +23,12 @@ const props: ShippingZoneDetailsPageProps = { onPriceRateEdit: () => undefined, onRateRemove: () => undefined, onSubmit: () => undefined, + onWarehouseAdd: () => undefined, onWeightRateAdd: () => undefined, onWeightRateEdit: () => undefined, saveButtonBarState: "default", - shippingZone + shippingZone, + warehouses: warehouseList }; storiesOf("Views / Shipping / Shipping zone details", module) diff --git a/src/types.ts b/src/types.ts index 3db6bcc62..0937e2d9b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -37,6 +37,7 @@ export enum ListViews { SHIPPING_METHODS_LIST = "SHIPPING_METHODS_LIST", STAFF_MEMBERS_LIST = "STAFF_MEMBERS_LIST", VOUCHER_LIST = "VOUCHER_LIST", + WAREHOUSE_LIST = "WAREHOUSE_LIST", WEBHOOK_LIST = "WEBHOOK_LIST" } @@ -87,10 +88,12 @@ export interface PageListProps onAdd: () => void; } -export interface SearchPageProps { - initialSearch: string; +export interface SearchProps { onSearchChange: (value: string) => void; } +export interface SearchPageProps extends SearchProps { + initialSearch: string; +} export interface FilterPageProps extends FilterProps, SearchPageProps, diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 72cec5cbe..37cfba865 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -24,6 +24,7 @@ export enum AccountErrorCode { NOT_FOUND = "NOT_FOUND", OUT_OF_SCOPE_GROUP = "OUT_OF_SCOPE_GROUP", OUT_OF_SCOPE_PERMISSION = "OUT_OF_SCOPE_PERMISSION", + OUT_OF_SCOPE_SERVICE_ACCOUNT = "OUT_OF_SCOPE_SERVICE_ACCOUNT", OUT_OF_SCOPE_USER = "OUT_OF_SCOPE_USER", PASSWORD_ENTIRELY_NUMERIC = "PASSWORD_ENTIRELY_NUMERIC", PASSWORD_TOO_COMMON = "PASSWORD_TOO_COMMON", @@ -456,6 +457,7 @@ export enum OrderErrorCode { CANNOT_DELETE = "CANNOT_DELETE", CANNOT_REFUND = "CANNOT_REFUND", CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT", + DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", FULFILL_ORDER_LINE = "FULFILL_ORDER_LINE", GRAPHQL_ERROR = "GRAPHQL_ERROR", INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK", @@ -689,6 +691,15 @@ export enum StockAvailability { OUT_OF_STOCK = "OUT_OF_STOCK", } +export enum StockErrorCode { + ALREADY_EXISTS = "ALREADY_EXISTS", + GRAPHQL_ERROR = "GRAPHQL_ERROR", + INVALID = "INVALID", + NOT_FOUND = "NOT_FOUND", + REQUIRED = "REQUIRED", + UNIQUE = "UNIQUE", +} + export enum TaxRateType { ACCOMMODATION = "ACCOMMODATION", ADMISSION_TO_CULTURAL_EVENTS = "ADMISSION_TO_CULTURAL_EVENTS", @@ -746,6 +757,19 @@ export enum VoucherTypeEnum { SPECIFIC_PRODUCT = "SPECIFIC_PRODUCT", } +export enum WarehouseErrorCode { + ALREADY_EXISTS = "ALREADY_EXISTS", + GRAPHQL_ERROR = "GRAPHQL_ERROR", + INVALID = "INVALID", + NOT_FOUND = "NOT_FOUND", + REQUIRED = "REQUIRED", + UNIQUE = "UNIQUE", +} + +export enum WarehouseSortField { + NAME = "NAME", +} + export enum WebhookErrorCode { GRAPHQL_ERROR = "GRAPHQL_ERROR", INVALID = "INVALID", @@ -768,6 +792,7 @@ export enum WebhookEventTypeEnum { } export enum WebhookSortField { + APP = "APP", NAME = "NAME", SERVICE_ACCOUNT = "SERVICE_ACCOUNT", TARGET_URL = "TARGET_URL", @@ -972,18 +997,7 @@ export interface DraftOrderInput { } export interface FulfillmentCancelInput { - restock?: boolean | null; -} - -export interface FulfillmentCreateInput { - trackingNumber?: string | null; - notifyCustomer?: boolean | null; - lines: (FulfillmentLineInput | null)[]; -} - -export interface FulfillmentLineInput { - orderLineId?: string | null; - quantity?: number | null; + warehouseId: string; } export interface FulfillmentUpdateTrackingInput { @@ -1052,6 +1066,21 @@ export interface OrderFilterInput { search?: string | null; } +export interface OrderFulfillInput { + lines: OrderFulfillLineInput[]; + notifyCustomer?: boolean | null; +} + +export interface OrderFulfillLineInput { + orderLineId?: string | null; + stocks: OrderFulfillStockInput[]; +} + +export interface OrderFulfillStockInput { + quantity?: number | null; + warehouse?: string | null; +} + export interface OrderLineCreateInput { quantity: number; variantId: string; @@ -1201,9 +1230,9 @@ export interface ProductVariantBulkCreateInput { costPrice?: any | null; priceOverride?: any | null; sku: string; - quantity?: number | null; trackInventory?: boolean | null; weight?: any | null; + stocks?: StockInput[] | null; } export interface ProductVariantCreateInput { @@ -1211,7 +1240,6 @@ export interface ProductVariantCreateInput { costPrice?: any | null; priceOverride?: any | null; sku?: string | null; - quantity?: number | null; trackInventory?: boolean | null; weight?: any | null; product: string; @@ -1223,7 +1251,6 @@ export interface ProductVariantInput { costPrice?: any | null; priceOverride?: any | null; sku?: string | null; - quantity?: number | null; trackInventory?: boolean | null; weight?: any | null; } @@ -1415,11 +1442,50 @@ export interface VoucherSortingInput { field: VoucherSortField; } +export interface WarehouseAddressInput { + streetAddress1: string; + streetAddress2?: string | null; + city: string; + cityArea?: string | null; + postalCode?: string | null; + country: CountryCode; + countryArea?: string | null; + phone?: string | null; +} + +export interface WarehouseCreateInput { + slug?: string | null; + companyName?: string | null; + email?: string | null; + name: string; + address: WarehouseAddressInput; + shippingZones?: (string | null)[] | null; +} + +export interface WarehouseFilterInput { + search?: string | null; + ids?: (string | null)[] | null; +} + +export interface WarehouseSortingInput { + direction: OrderDirection; + field: WarehouseSortField; +} + +export interface WarehouseUpdateInput { + slug?: string | null; + companyName?: string | null; + email?: string | null; + name?: string | null; + address?: WarehouseAddressInput | null; +} + export interface WebhookCreateInput { name?: string | null; targetUrl?: string | null; events?: (WebhookEventTypeEnum | null)[] | null; serviceAccount?: string | null; + app?: string | null; isActive?: boolean | null; secretKey?: string | null; } @@ -1439,6 +1505,7 @@ export interface WebhookUpdateInput { targetUrl?: string | null; events?: (WebhookEventTypeEnum | null)[] | null; serviceAccount?: string | null; + app?: string | null; isActive?: boolean | null; secretKey?: string | null; } diff --git a/src/utils/errors/stock.ts b/src/utils/errors/stock.ts new file mode 100644 index 000000000..6bf4d1da9 --- /dev/null +++ b/src/utils/errors/stock.ts @@ -0,0 +1,47 @@ +import { IntlShape, defineMessages } from "react-intl"; + +import { StockErrorFragment } from "@saleor/products/types/StockErrorFragment"; +import { StockErrorCode } from "@saleor/types/globalTypes"; +import { commonMessages } from "@saleor/intl"; +import { BulkStockErrorFragment } from "@saleor/products/types/BulkStockErrorFragment"; +import commonErrorMessages from "./common"; +import getProductErrorMessage from "./product"; + +const messages = defineMessages({ + slugUnique: { + defaultMessage: + "Stock for this warehouse already exists for this product variant", + description: "error message" + } +}); + +function getStockErrorMessage( + err: Omit | undefined, + intl: IntlShape +): string { + if (err) { + switch (err.code) { + case StockErrorCode.UNIQUE: + return intl.formatMessage(messages.slugUnique); + case StockErrorCode.GRAPHQL_ERROR: + return intl.formatMessage(commonErrorMessages.graphqlError); + case StockErrorCode.REQUIRED: + return intl.formatMessage(commonMessages.requiredField); + case StockErrorCode.INVALID: + return intl.formatMessage(commonErrorMessages.invalid); + default: + return intl.formatMessage(commonErrorMessages.unknownError); + } + } + + return undefined; +} + +export function getBulkStockErrorMessage( + err: Omit | undefined, + intl: IntlShape +): string { + return getProductErrorMessage(err, intl); +} + +export default getStockErrorMessage; diff --git a/src/utils/errors/warehouse.ts b/src/utils/errors/warehouse.ts new file mode 100644 index 000000000..ee45e04a3 --- /dev/null +++ b/src/utils/errors/warehouse.ts @@ -0,0 +1,51 @@ +import { IntlShape, defineMessages } from "react-intl"; + +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; +import { WarehouseErrorCode } from "@saleor/types/globalTypes"; +import { commonMessages } from "@saleor/intl"; +import commonErrorMessages from "./common"; + +const messages = defineMessages({ + slugUnique: { + defaultMessage: "Slug must be unique for each warehouse", + description: "error message" + } +}); + +function getWarehouseErrorMessage( + err: Omit | undefined, + intl: IntlShape +): string { + if (err) { + switch (err.code) { + case WarehouseErrorCode.GRAPHQL_ERROR: + return intl.formatMessage(commonErrorMessages.graphqlError); + case WarehouseErrorCode.REQUIRED: + return intl.formatMessage(commonMessages.requiredField); + case WarehouseErrorCode.INVALID: + return intl.formatMessage(commonErrorMessages.invalid); + default: + return intl.formatMessage(commonErrorMessages.unknownError); + } + } + + return undefined; +} + +export function getWarehouseSlugErrorMessage( + err: Omit | undefined, + intl: IntlShape +): string { + if (err) { + switch (err.code) { + case WarehouseErrorCode.UNIQUE: + return intl.formatMessage(messages.slugUnique); + default: + return getWarehouseErrorMessage(err, intl); + } + } + + return undefined; +} + +export default getWarehouseErrorMessage; diff --git a/src/utils/lists/__snapshots__/lists.test.ts.snap b/src/utils/lists/__snapshots__/lists.test.ts.snap index c849a90e0..f2580e552 100644 --- a/src/utils/lists/__snapshots__/lists.test.ts.snap +++ b/src/utils/lists/__snapshots__/lists.test.ts.snap @@ -67,13 +67,21 @@ Array [ "value": 1, }, Object { - "name": "dolor", - "value": 2, + "name": "lorem", + "value": 0, + }, + Object { + "name": "ipsum", + "value": 1, }, Object { "name": "amet", "value": 32, }, + Object { + "name": "dolor", + "value": 2, + }, ] `; diff --git a/src/utils/lists/lists.ts b/src/utils/lists/lists.ts index 067fc959c..5526b39b1 100644 --- a/src/utils/lists/lists.ts +++ b/src/utils/lists/lists.ts @@ -35,7 +35,9 @@ export function update( list: List, compare: Compare ) { - return add(data, remove(data, list, compare)); + const index = list.findIndex(element => compare(data, element)); + + return updateAtIndex(data, list, index); } export function updateAtIndex( @@ -43,6 +45,9 @@ export function updateAtIndex( list: List, index: number ) { + if (!index.toFixed) { + throw new Error("Index is not a number"); + } return addAtIndex(data, removeAtIndex(list, index), index); } diff --git a/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx new file mode 100644 index 000000000..2dcb9ca69 --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx @@ -0,0 +1,48 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import Decorator from "@saleor/storybook/Decorator"; +import { countries } from "@saleor/fixtures"; +import { WarehouseErrorCode } from "@saleor/types/globalTypes"; +import WarehouseCreatePage, { + WarehouseCreatePageProps, + WarehouseCreatePageFormData +} from "./WarehouseCreatePage"; + +const props: WarehouseCreatePageProps = { + countries: countries.map(c => ({ + __typename: "CountryDisplay", + code: c.code, + country: c.name + })), + disabled: false, + errors: [], + onBack: () => undefined, + onSubmit: () => undefined, + saveButtonBarState: "default" +}; +storiesOf("Views / Warehouses / Create warehouse", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ) + .add("form errors", () => ( + ).map(field => ({ + __typename: "WarehouseError", + code: WarehouseErrorCode.INVALID, + field + }))} + /> + )); diff --git a/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx new file mode 100644 index 000000000..b49e010f2 --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import { useIntl, FormattedMessage } from "react-intl"; + +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Grid from "@saleor/components/Grid"; +import CardSpacer from "@saleor/components/CardSpacer"; +import CompanyAddressInput from "@saleor/components/CompanyAddressInput"; +import { AddressTypeInput } from "@saleor/customers/types"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import { mapCountriesToChoices } from "@saleor/utils/maps"; +import useAddressValidation from "@saleor/hooks/useAddressValidation"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; +import AppHeader from "@saleor/components/AppHeader"; +import PageHeader from "@saleor/components/PageHeader"; +import { sectionNames } from "@saleor/intl"; +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; +import WarehouseInfo from "../WarehouseInfo"; + +export interface WarehouseCreatePageFormData extends AddressTypeInput { + name: string; +} +export interface WarehouseCreatePageProps { + countries: ShopInfo_shop_countries[]; + disabled: boolean; + errors: WarehouseErrorFragment[]; + saveButtonBarState: ConfirmButtonTransitionState; + onBack: () => void; + onSubmit: (data: WarehouseCreatePageFormData) => void; +} + +const initialForm: WarehouseCreatePageFormData = { + city: "", + companyName: "", + country: "", + countryArea: "", + name: "", + phone: "", + postalCode: "", + streetAddress1: "", + streetAddress2: "" +}; + +const WarehouseCreatePage: React.FC = ({ + countries, + disabled, + errors, + saveButtonBarState, + onBack, + onSubmit +}) => { + const intl = useIntl(); + const [displayCountry, setDisplayCountry] = useStateFromProps(""); + + const { + errors: validationErrors, + submit: handleSubmit + } = useAddressValidation(onSubmit); + + return ( +
+ {({ change, data, submit }) => { + const countryChoices = mapCountriesToChoices(countries); + const handleCountryChange = createSingleAutocompleteSelectHandler( + change, + setDisplayCountry, + countryChoices + ); + + return ( + + + + + + +
+ + + +
+
+ +
+ ); + }} +
+ ); +}; + +WarehouseCreatePage.displayName = "WarehouseCreatePage"; +export default WarehouseCreatePage; diff --git a/src/warehouses/components/WarehouseCreatePage/index.ts b/src/warehouses/components/WarehouseCreatePage/index.ts new file mode 100644 index 000000000..b9bd23b20 --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseCreatePage"; +export * from "./WarehouseCreatePage"; diff --git a/src/warehouses/components/WarehouseDeleteDialog/WarehouseDeleteDialog.tsx b/src/warehouses/components/WarehouseDeleteDialog/WarehouseDeleteDialog.tsx new file mode 100644 index 000000000..224a2c36e --- /dev/null +++ b/src/warehouses/components/WarehouseDeleteDialog/WarehouseDeleteDialog.tsx @@ -0,0 +1,51 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; + +export interface WarehouseDeleteDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + onConfirm: () => void; + onClose: () => void; + name: string; +} + +const WarehouseDeleteDialog: React.FC = ({ + name, + confirmButtonState, + onClose, + onConfirm, + open +}) => { + const intl = useIntl(); + + return ( + + + {name} + }} + /> + + + ); +}; + +WarehouseDeleteDialog.displayName = "WarehouseDeleteDialog"; +export default WarehouseDeleteDialog; diff --git a/src/warehouses/components/WarehouseDeleteDialog/index.ts b/src/warehouses/components/WarehouseDeleteDialog/index.ts new file mode 100644 index 000000000..f8f68537d --- /dev/null +++ b/src/warehouses/components/WarehouseDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseDeleteDialog"; +export * from "./WarehouseDeleteDialog"; diff --git a/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.stories.tsx b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.stories.tsx new file mode 100644 index 000000000..bf7e9ab4f --- /dev/null +++ b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.stories.tsx @@ -0,0 +1,57 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { address, countries } from "@saleor/fixtures"; +import Decorator from "@saleor/storybook/Decorator"; +import { WarehouseErrorCode } from "@saleor/types/globalTypes"; +import { warehouseList } from "../../fixtures"; +import WarehouseDetailsPage, { + WarehouseDetailsPageProps, + WarehouseDetailsPageFormData +} from "./WarehouseDetailsPage"; + +const props: WarehouseDetailsPageProps = { + countries: countries.map(c => ({ + __typename: "CountryDisplay", + code: c.code, + country: c.name + })), + disabled: false, + errors: [], + onBack: () => undefined, + onDelete: () => undefined, + onShippingZoneClick: () => undefined, + onSubmit: () => undefined, + saveButtonBarState: "default", + warehouse: { + ...warehouseList[0], + address + } +}; +storiesOf("Views / Warehouses / Warehouse details", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("form errors", () => ( + ).map(field => ({ + __typename: "WarehouseError", + code: WarehouseErrorCode.INVALID, + field + }))} + /> + )); diff --git a/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx new file mode 100644 index 000000000..c4c53c811 --- /dev/null +++ b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx @@ -0,0 +1,136 @@ +import React from "react"; +import { useIntl, FormattedMessage } from "react-intl"; + +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Grid from "@saleor/components/Grid"; +import CardSpacer from "@saleor/components/CardSpacer"; +import CompanyAddressInput from "@saleor/components/CompanyAddressInput"; +import { AddressTypeInput } from "@saleor/customers/types"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import { mapCountriesToChoices } from "@saleor/utils/maps"; +import useAddressValidation from "@saleor/hooks/useAddressValidation"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { maybe, findValueInEnum } from "@saleor/misc"; +import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; +import AppHeader from "@saleor/components/AppHeader"; +import PageHeader from "@saleor/components/PageHeader"; +import { sectionNames } from "@saleor/intl"; +import { CountryCode } from "@saleor/types/globalTypes"; +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; +import WarehouseInfo from "../WarehouseInfo"; +import WarehouseZones from "../WarehouseZones"; +import { WarehouseDetails_warehouse } from "../../types/WarehouseDetails"; + +export interface WarehouseDetailsPageFormData extends AddressTypeInput { + name: string; +} +export interface WarehouseDetailsPageProps { + countries: ShopInfo_shop_countries[]; + disabled: boolean; + errors: WarehouseErrorFragment[]; + saveButtonBarState: ConfirmButtonTransitionState; + warehouse: WarehouseDetails_warehouse; + onBack: () => void; + onDelete: () => void; + onShippingZoneClick: (id: string) => void; + onSubmit: (data: WarehouseDetailsPageFormData) => void; +} + +const WarehouseDetailsPage: React.FC = ({ + countries, + disabled, + errors, + saveButtonBarState, + warehouse, + onBack, + onDelete, + onShippingZoneClick, + onSubmit +}) => { + const intl = useIntl(); + const [displayCountry, setDisplayCountry] = useStateFromProps(""); + + const { + errors: validationErrors, + submit: handleSubmit + } = useAddressValidation(onSubmit); + + const initialForm: WarehouseDetailsPageFormData = { + city: maybe(() => warehouse.address.city, ""), + companyName: maybe(() => warehouse.address.companyName, ""), + country: maybe(() => + findValueInEnum(warehouse.address.country.code, CountryCode) + ), + countryArea: maybe(() => warehouse.address.countryArea, ""), + name: maybe(() => warehouse.name, ""), + phone: maybe(() => warehouse.address.phone, ""), + postalCode: maybe(() => warehouse.address.postalCode, ""), + streetAddress1: maybe(() => warehouse.address.streetAddress1, ""), + streetAddress2: maybe(() => warehouse.address.streetAddress2, "") + }; + + return ( +
+ {({ change, data, submit }) => { + const countryChoices = mapCountriesToChoices(countries); + const handleCountryChange = createSingleAutocompleteSelectHandler( + change, + setDisplayCountry, + countryChoices + ); + + return ( + + + + + warehouse.name)} /> + +
+ + + +
+
+ edge.node)} + onShippingZoneClick={onShippingZoneClick} + /> +
+
+ +
+ ); + }} +
+ ); +}; + +WarehouseDetailsPage.displayName = "WarehouseDetailsPage"; +export default WarehouseDetailsPage; diff --git a/src/warehouses/components/WarehouseDetailsPage/index.ts b/src/warehouses/components/WarehouseDetailsPage/index.ts new file mode 100644 index 000000000..c5fea07a8 --- /dev/null +++ b/src/warehouses/components/WarehouseDetailsPage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseDetailsPage"; +export * from "./WarehouseDetailsPage"; diff --git a/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx b/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx new file mode 100644 index 000000000..1dde8858a --- /dev/null +++ b/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import { useIntl } from "react-intl"; + +import CardTitle from "@saleor/components/CardTitle"; +import { commonMessages } from "@saleor/intl"; +import { FormChange } from "@saleor/hooks/useForm"; +import { getFormErrors } from "@saleor/utils/errors"; +import { WarehouseErrorFragment } from "@saleor/warehouses/types/WarehouseErrorFragment"; +import getWarehouseErrorMessage from "@saleor/utils/errors/warehouse"; + +export interface WarehouseInfoProps { + data: Record<"name", string>; + disabled: boolean; + errors: WarehouseErrorFragment[]; + onChange: FormChange; +} + +const WarehouseInfo: React.FC = ({ + data, + disabled, + errors, + onChange +}) => { + const intl = useIntl(); + + const formErrors = getFormErrors(["name"], errors); + + return ( + + + + + + + ); +}; + +WarehouseInfo.displayName = "WarehouseInfo"; +export default WarehouseInfo; diff --git a/src/warehouses/components/WarehouseInfo/index.ts b/src/warehouses/components/WarehouseInfo/index.ts new file mode 100644 index 000000000..3296772d7 --- /dev/null +++ b/src/warehouses/components/WarehouseInfo/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseInfo"; +export * from "./WarehouseInfo"; diff --git a/src/warehouses/components/WarehouseList/WarehouseList.tsx b/src/warehouses/components/WarehouseList/WarehouseList.tsx new file mode 100644 index 000000000..68602fb33 --- /dev/null +++ b/src/warehouses/components/WarehouseList/WarehouseList.tsx @@ -0,0 +1,178 @@ +import { makeStyles } from "@material-ui/core/styles"; +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 TableHead from "@material-ui/core/TableHead"; +import React from "react"; +import { FormattedMessage } from "react-intl"; +import IconButton from "@material-ui/core/IconButton"; +import DeleteIcon from "@material-ui/icons/Delete"; +import EditIcon from "@material-ui/icons/Edit"; + +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import TablePagination from "@saleor/components/TablePagination"; +import { maybe, renderCollection, stopPropagation } from "@saleor/misc"; +import { ListProps, SortPage } from "@saleor/types"; +import { WarehouseListUrlSortField } from "@saleor/warehouses/urls"; +import TableCellHeader from "@saleor/components/TableCellHeader"; +import { getArrowDirection } from "@saleor/utils/sort"; +import { WarehouseWithShippingFragment } from "@saleor/warehouses/types/WarehouseWithShippingFragment"; + +const useStyles = makeStyles( + theme => ({ + [theme.breakpoints.up("lg")]: { + colActions: { + width: 160 + }, + colName: { + width: 400 + }, + colZones: { + width: "auto" + } + }, + actions: { + alignItems: "center", + display: "flex", + justifyContent: "flex-end", + position: "relative", + right: -theme.spacing(2) + }, + colActions: { + textAlign: "right" + }, + colName: { + paddingLeft: 0 + }, + colZones: { + paddingLeft: 0 + }, + tableRow: { + cursor: "pointer" + } + }), + { name: "WarehouseList" } +); + +interface WarehouseListProps + extends ListProps, + SortPage { + warehouses: WarehouseWithShippingFragment[]; + onAdd: () => void; + onRemove: (id: string) => void; +} + +const numberOfColumns = 3; + +const WarehouseList: React.FC = props => { + const { + warehouses, + disabled, + settings, + sort, + pageInfo, + onNextPage, + onPreviousPage, + onUpdateListSettings, + onRemove, + onRowClick, + onSort + } = props; + + const classes = useStyles(props); + + return ( + + + + onSort(WarehouseListUrlSortField.name)} + > + + + + + + + + + + + + + + + + + {renderCollection( + warehouses, + warehouse => ( + warehouse.id)} + > + + {maybe(() => warehouse.name, )} + + + {maybe( + () => + warehouse.shippingZones.edges + .map(edge => edge.node.name) + .join(", "), + + )} + + +
+ + + + onRemove(warehouse.id))} + > + + +
+
+
+ ), + () => ( + + + + + + ) + )} +
+
+ ); +}; + +WarehouseList.displayName = "WarehouseList"; +export default WarehouseList; diff --git a/src/warehouses/components/WarehouseList/index.ts b/src/warehouses/components/WarehouseList/index.ts new file mode 100644 index 000000000..4c64e0c2c --- /dev/null +++ b/src/warehouses/components/WarehouseList/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseList"; +export * from "./WarehouseList"; diff --git a/src/warehouses/components/WarehouseListPage/WarehouseListPage.stories.tsx b/src/warehouses/components/WarehouseListPage/WarehouseListPage.stories.tsx new file mode 100644 index 000000000..6fbaaeac5 --- /dev/null +++ b/src/warehouses/components/WarehouseListPage/WarehouseListPage.stories.tsx @@ -0,0 +1,37 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { + pageListProps, + tabPageProps, + sortPageProps, + searchPageProps +} from "@saleor/fixtures"; +import WarehouseListPage, { + WarehouseListPageProps +} from "@saleor/warehouses/components/WarehouseListPage"; +import Decorator from "@saleor/storybook/Decorator"; +import { WarehouseListUrlSortField } from "@saleor/warehouses/urls"; +import { warehouseList } from "../../fixtures"; + +const props: WarehouseListPageProps = { + ...pageListProps.default, + ...searchPageProps, + ...sortPageProps, + ...tabPageProps, + onBack: () => undefined, + onRemove: () => undefined, + sort: { + ...sortPageProps.sort, + sort: WarehouseListUrlSortField.name + }, + warehouses: warehouseList +}; + +storiesOf("Views / Warehouses / Warehouse list", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("no data", () => ); diff --git a/src/warehouses/components/WarehouseListPage/WarehouseListPage.tsx b/src/warehouses/components/WarehouseListPage/WarehouseListPage.tsx new file mode 100644 index 000000000..dc3d0b257 --- /dev/null +++ b/src/warehouses/components/WarehouseListPage/WarehouseListPage.tsx @@ -0,0 +1,104 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import SearchBar from "@saleor/components/SearchBar"; +import { sectionNames } from "@saleor/intl"; +import { + PageListProps, + SearchPageProps, + TabPageProps, + SortPage +} from "@saleor/types"; +import { WarehouseListUrlSortField } from "@saleor/warehouses/urls"; +import AppHeader from "@saleor/components/AppHeader"; +import { WarehouseWithShippingFragment } from "@saleor/warehouses/types/WarehouseWithShippingFragment"; +import WarehouseList from "../WarehouseList"; + +export interface WarehouseListPageProps + extends PageListProps, + SearchPageProps, + SortPage, + TabPageProps { + warehouses: WarehouseWithShippingFragment[]; + onBack: () => void; + onRemove: (id: string) => void; +} + +export const WarehouseListPage: React.FC = ({ + warehouses, + currentTab, + disabled, + initialSearch, + pageInfo, + settings, + tabs, + onAdd, + onAll, + onBack, + onNextPage, + onPreviousPage, + onRemove, + onRowClick, + onSearchChange, + onTabChange, + onTabDelete, + onTabSave, + onUpdateListSettings, + ...listProps +}) => { + const intl = useIntl(); + + return ( + + + + + + + + + + + + + ); +}; +WarehouseListPage.displayName = "WarehouseListPage"; +export default WarehouseListPage; diff --git a/src/warehouses/components/WarehouseListPage/index.ts b/src/warehouses/components/WarehouseListPage/index.ts new file mode 100644 index 000000000..1bce1dd38 --- /dev/null +++ b/src/warehouses/components/WarehouseListPage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseListPage"; +export * from "./WarehouseListPage"; diff --git a/src/warehouses/components/WarehouseZones/WarehouseZones.tsx b/src/warehouses/components/WarehouseZones/WarehouseZones.tsx new file mode 100644 index 000000000..b4fe1cb2d --- /dev/null +++ b/src/warehouses/components/WarehouseZones/WarehouseZones.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import { useIntl, FormattedMessage } from "react-intl"; +import makeStyles from "@material-ui/core/styles/makeStyles"; + +import CardTitle from "@saleor/components/CardTitle"; +import { WarehouseDetails_warehouse_shippingZones_edges_node } from "@saleor/warehouses/types/WarehouseDetails"; +import { renderCollection, maybe } from "@saleor/misc"; +import Link from "@saleor/components/Link"; +import Skeleton from "@saleor/components/Skeleton"; + +export interface WarehouseInfoProps { + zones: WarehouseDetails_warehouse_shippingZones_edges_node[]; + onShippingZoneClick: (id: string) => void; +} + +const useStyles = makeStyles( + theme => ({ + link: { + "&:not(:last-of-type)": { + marginBottom: theme.spacing() + } + } + }), + { + name: "WarehouseInfoProps" + } +); + +const WarehouseInfo: React.FC = ({ + zones, + onShippingZoneClick +}) => { + const classes = useStyles({}); + const intl = useIntl(); + + return ( + + + + {renderCollection( + zones, + zone => + maybe( + () => ( +
+ onShippingZoneClick(zone.id)}> + {zone.name} + +
+ ), + + ), + () => ( + + + + ) + )} +
+
+ ); +}; + +WarehouseInfo.displayName = "WarehouseInfo"; +export default WarehouseInfo; diff --git a/src/warehouses/components/WarehouseZones/index.ts b/src/warehouses/components/WarehouseZones/index.ts new file mode 100644 index 000000000..c0a280252 --- /dev/null +++ b/src/warehouses/components/WarehouseZones/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseZones"; +export * from "./WarehouseZones"; diff --git a/src/warehouses/fixtures.ts b/src/warehouses/fixtures.ts new file mode 100644 index 000000000..e393c9c11 --- /dev/null +++ b/src/warehouses/fixtures.ts @@ -0,0 +1,53 @@ +import { shippingZones } from "../shipping/fixtures"; +import { WarehouseList_warehouses_edges_node } from "./types/WarehouseList"; + +export const warehouseList: WarehouseList_warehouses_edges_node[] = [ + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOmEzMThmMGZlLTcwMmYtNDNjYy1hYmFjLWZmZmMzN2Y3ZTliYw==", + name: "C our wares", + shippingZones: { + __typename: "ShippingZoneCountableConnection", + edges: shippingZones.map(node => ({ + __typename: "ShippingZoneCountableEdge", + node + })) + } + }, + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOjJmN2UyOTlmLWEwMzMtNDhjZS1iYmM5LTFkZDM4NjU2ZjMwYw==", + name: "Be stocked", + shippingZones: { + __typename: "ShippingZoneCountableConnection", + edges: shippingZones.map(node => ({ + __typename: "ShippingZoneCountableEdge", + node + })) + } + }, + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOmM0ZmQ3Nzc0LWZlMjYtNDE1YS1hYjk1LWFlYTFjMjI0NTgwNg==", + name: "A Warehouse", + shippingZones: { + __typename: "ShippingZoneCountableConnection", + edges: shippingZones.map(node => ({ + __typename: "ShippingZoneCountableEdge", + node + })) + } + }, + { + __typename: "Warehouse", + id: "V2FyZWhvdXNlOmNlMmNiZDhhLWRkYmQtNDhiNS1hM2UxLTNmZGVkZGI5MWZkMg==", + name: "Darkwares", + shippingZones: { + __typename: "ShippingZoneCountableConnection", + edges: shippingZones.map(node => ({ + __typename: "ShippingZoneCountableEdge", + node + })) + } + } +]; diff --git a/src/warehouses/index.tsx b/src/warehouses/index.tsx new file mode 100644 index 000000000..b94af8c1a --- /dev/null +++ b/src/warehouses/index.tsx @@ -0,0 +1,59 @@ +import { parse as parseQs } from "qs"; +import React from "react"; +import { Route, RouteComponentProps, Switch } from "react-router-dom"; + +import { sectionNames } from "@saleor/intl"; +import { useIntl } from "react-intl"; +import { asSortParams } from "@saleor/utils/sort"; +import { WindowTitle } from "../components/WindowTitle"; +import { + warehouseListPath, + WarehouseListUrlQueryParams, + warehousePath, + WarehouseUrlQueryParams, + WarehouseListUrlSortField, + warehouseAddPath +} from "./urls"; +import WarehouseDetailsComponent from "./views/WarehouseDetails"; +import WarehouseListComponent from "./views/WarehouseList"; +import WarehouseCreate from "./views/WarehouseCreate"; + +const WarehouseList: React.FC = ({ location }) => { + const qs = parseQs(location.search.substr(1)); + const params: WarehouseListUrlQueryParams = asSortParams( + qs, + WarehouseListUrlSortField + ); + + return ; +}; + +const WarehouseDetails: React.FC> = ({ + location, + match +}) => { + const qs = parseQs(location.search.substr(1)); + const params: WarehouseUrlQueryParams = qs; + return ( + + ); +}; + +export const WarehouseSection: React.FC = () => { + const intl = useIntl(); + + return ( + <> + + + + + + + + ); +}; +export default WarehouseSection; diff --git a/src/warehouses/mutations.ts b/src/warehouses/mutations.ts new file mode 100644 index 000000000..343f1bb50 --- /dev/null +++ b/src/warehouses/mutations.ts @@ -0,0 +1,76 @@ +import gql from "graphql-tag"; + +import makeMutation from "@saleor/hooks/makeMutation"; +import { + WarehouseCreate, + WarehouseCreateVariables +} from "./types/WarehouseCreate"; +import { + WarehouseUpdate, + WarehouseUpdateVariables +} from "./types/WarehouseUpdate"; +import { + WarehouseDelete, + WarehouseDeleteVariables +} from "./types/WarehouseDelete"; +import { warehouseDetailsFragment } from "./queries"; + +export const warehouseErrorFragment = gql` + fragment WarehouseErrorFragment on WarehouseError { + code + field + } +`; + +const deleteWarehouse = gql` + ${warehouseErrorFragment} + mutation WarehouseDelete($id: ID!) { + deleteWarehouse(id: $id) { + errors: warehouseErrors { + ...WarehouseErrorFragment + } + } + } +`; +export const useWarehouseDelete = makeMutation< + WarehouseDelete, + WarehouseDeleteVariables +>(deleteWarehouse); + +const createWarehouse = gql` + ${warehouseDetailsFragment} + ${warehouseErrorFragment} + mutation WarehouseCreate($input: WarehouseCreateInput!) { + createWarehouse(input: $input) { + errors: warehouseErrors { + ...WarehouseErrorFragment + } + warehouse { + ...WarehouseDetailsFragment + } + } + } +`; +export const useWarehouseCreate = makeMutation< + WarehouseCreate, + WarehouseCreateVariables +>(createWarehouse); + +const updateWarehouse = gql` + ${warehouseDetailsFragment} + ${warehouseErrorFragment} + mutation WarehouseUpdate($id: ID!, $input: WarehouseUpdateInput!) { + updateWarehouse(id: $id, input: $input) { + errors: warehouseErrors { + ...WarehouseErrorFragment + } + warehouse { + ...WarehouseDetailsFragment + } + } + } +`; +export const useWarehouseUpdate = makeMutation< + WarehouseUpdate, + WarehouseUpdateVariables +>(updateWarehouse); diff --git a/src/warehouses/queries.ts b/src/warehouses/queries.ts new file mode 100644 index 000000000..8075c0ee0 --- /dev/null +++ b/src/warehouses/queries.ts @@ -0,0 +1,90 @@ +import gql from "graphql-tag"; + +import makeQuery from "@saleor/hooks/makeQuery"; +import { pageInfoFragment } from "@saleor/queries"; +import { fragmentAddress } from "@saleor/orders/queries"; +import { WarehouseList, WarehouseListVariables } from "./types/WarehouseList"; +import { + WarehouseDetails, + WarehouseDetailsVariables +} from "./types/WarehouseDetails"; + +export const warehouseFragment = gql` + fragment WarehouseFragment on Warehouse { + id + name + } +`; +export const warehouseWithShippingFragment = gql` + ${warehouseFragment} + fragment WarehouseWithShippingFragment on Warehouse { + ...WarehouseFragment + shippingZones(first: 100) { + edges { + node { + id + name + } + } + } + } +`; + +export const warehouseDetailsFragment = gql` + ${fragmentAddress} + ${warehouseWithShippingFragment} + fragment WarehouseDetailsFragment on Warehouse { + ...WarehouseWithShippingFragment + address { + ...AddressFragment + } + } +`; + +const warehouseList = gql` + ${warehouseWithShippingFragment} + ${pageInfoFragment} + query WarehouseList( + $first: Int + $after: String + $last: Int + $before: String + $filter: WarehouseFilterInput + $sort: WarehouseSortingInput + ) { + warehouses( + before: $before + after: $after + first: $first + last: $last + filter: $filter + sortBy: $sort + ) { + edges { + node { + ...WarehouseWithShippingFragment + } + } + pageInfo { + ...PageInfoFragment + } + } + } +`; +export const useWarehouseList = makeQuery< + WarehouseList, + WarehouseListVariables +>(warehouseList); + +const warehouseDetails = gql` + ${warehouseDetailsFragment} + query WarehouseDetails($id: ID!) { + warehouse(id: $id) { + ...WarehouseDetailsFragment + } + } +`; +export const useWarehouseDetails = makeQuery< + WarehouseDetails, + WarehouseDetailsVariables +>(warehouseDetails); diff --git a/src/warehouses/types/WarehouseCreate.ts b/src/warehouses/types/WarehouseCreate.ts new file mode 100644 index 000000000..0cceb22dc --- /dev/null +++ b/src/warehouses/types/WarehouseCreate.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseCreateInput, WarehouseErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: WarehouseCreate +// ==================================================== + +export interface WarehouseCreate_createWarehouse_errors { + __typename: "WarehouseError"; + code: WarehouseErrorCode; + field: string | null; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseCreate_createWarehouse_warehouse_shippingZones_edges_node; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseCreate_createWarehouse_warehouse_shippingZones_edges[]; +} + +export interface WarehouseCreate_createWarehouse_warehouse_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseCreate_createWarehouse_warehouse_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseCreate_createWarehouse_warehouse_shippingZones; + address: WarehouseCreate_createWarehouse_warehouse_address; +} + +export interface WarehouseCreate_createWarehouse { + __typename: "WarehouseCreate"; + errors: WarehouseCreate_createWarehouse_errors[]; + warehouse: WarehouseCreate_createWarehouse_warehouse | null; +} + +export interface WarehouseCreate { + createWarehouse: WarehouseCreate_createWarehouse | null; +} + +export interface WarehouseCreateVariables { + input: WarehouseCreateInput; +} diff --git a/src/warehouses/types/WarehouseDelete.ts b/src/warehouses/types/WarehouseDelete.ts new file mode 100644 index 000000000..b1ad9cf46 --- /dev/null +++ b/src/warehouses/types/WarehouseDelete.ts @@ -0,0 +1,28 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: WarehouseDelete +// ==================================================== + +export interface WarehouseDelete_deleteWarehouse_errors { + __typename: "WarehouseError"; + code: WarehouseErrorCode; + field: string | null; +} + +export interface WarehouseDelete_deleteWarehouse { + __typename: "WarehouseDelete"; + errors: WarehouseDelete_deleteWarehouse_errors[]; +} + +export interface WarehouseDelete { + deleteWarehouse: WarehouseDelete_deleteWarehouse | null; +} + +export interface WarehouseDeleteVariables { + id: string; +} diff --git a/src/warehouses/types/WarehouseDetails.ts b/src/warehouses/types/WarehouseDetails.ts new file mode 100644 index 000000000..b48eb255a --- /dev/null +++ b/src/warehouses/types/WarehouseDetails.ts @@ -0,0 +1,61 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: WarehouseDetails +// ==================================================== + +export interface WarehouseDetails_warehouse_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseDetails_warehouse_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseDetails_warehouse_shippingZones_edges_node; +} + +export interface WarehouseDetails_warehouse_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseDetails_warehouse_shippingZones_edges[]; +} + +export interface WarehouseDetails_warehouse_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseDetails_warehouse_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseDetails_warehouse_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseDetails_warehouse { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseDetails_warehouse_shippingZones; + address: WarehouseDetails_warehouse_address; +} + +export interface WarehouseDetails { + warehouse: WarehouseDetails_warehouse | null; +} + +export interface WarehouseDetailsVariables { + id: string; +} diff --git a/src/warehouses/types/WarehouseDetailsFragment.ts b/src/warehouses/types/WarehouseDetailsFragment.ts new file mode 100644 index 000000000..fae00ac31 --- /dev/null +++ b/src/warehouses/types/WarehouseDetailsFragment.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: WarehouseDetailsFragment +// ==================================================== + +export interface WarehouseDetailsFragment_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseDetailsFragment_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseDetailsFragment_shippingZones_edges_node; +} + +export interface WarehouseDetailsFragment_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseDetailsFragment_shippingZones_edges[]; +} + +export interface WarehouseDetailsFragment_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseDetailsFragment_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseDetailsFragment_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseDetailsFragment { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseDetailsFragment_shippingZones; + address: WarehouseDetailsFragment_address; +} diff --git a/src/warehouses/types/WarehouseErrorFragment.ts b/src/warehouses/types/WarehouseErrorFragment.ts new file mode 100644 index 000000000..efb04bb45 --- /dev/null +++ b/src/warehouses/types/WarehouseErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: WarehouseErrorFragment +// ==================================================== + +export interface WarehouseErrorFragment { + __typename: "WarehouseError"; + code: WarehouseErrorCode; + field: string | null; +} diff --git a/src/warehouses/types/WarehouseFragment.ts b/src/warehouses/types/WarehouseFragment.ts new file mode 100644 index 000000000..0f91a3997 --- /dev/null +++ b/src/warehouses/types/WarehouseFragment.ts @@ -0,0 +1,13 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: WarehouseFragment +// ==================================================== + +export interface WarehouseFragment { + __typename: "Warehouse"; + id: string; + name: string; +} diff --git a/src/warehouses/types/WarehouseList.ts b/src/warehouses/types/WarehouseList.ts new file mode 100644 index 000000000..00e3b29e4 --- /dev/null +++ b/src/warehouses/types/WarehouseList.ts @@ -0,0 +1,64 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseFilterInput, WarehouseSortingInput } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL query operation: WarehouseList +// ==================================================== + +export interface WarehouseList_warehouses_edges_node_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseList_warehouses_edges_node_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseList_warehouses_edges_node_shippingZones_edges_node; +} + +export interface WarehouseList_warehouses_edges_node_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseList_warehouses_edges_node_shippingZones_edges[]; +} + +export interface WarehouseList_warehouses_edges_node { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseList_warehouses_edges_node_shippingZones; +} + +export interface WarehouseList_warehouses_edges { + __typename: "WarehouseCountableEdge"; + node: WarehouseList_warehouses_edges_node; +} + +export interface WarehouseList_warehouses_pageInfo { + __typename: "PageInfo"; + endCursor: string | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; +} + +export interface WarehouseList_warehouses { + __typename: "WarehouseCountableConnection"; + edges: WarehouseList_warehouses_edges[]; + pageInfo: WarehouseList_warehouses_pageInfo; +} + +export interface WarehouseList { + warehouses: WarehouseList_warehouses | null; +} + +export interface WarehouseListVariables { + first?: number | null; + after?: string | null; + last?: number | null; + before?: string | null; + filter?: WarehouseFilterInput | null; + sort?: WarehouseSortingInput | null; +} diff --git a/src/warehouses/types/WarehouseUpdate.ts b/src/warehouses/types/WarehouseUpdate.ts new file mode 100644 index 000000000..a44d12b2a --- /dev/null +++ b/src/warehouses/types/WarehouseUpdate.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseUpdateInput, WarehouseErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: WarehouseUpdate +// ==================================================== + +export interface WarehouseUpdate_updateWarehouse_errors { + __typename: "WarehouseError"; + code: WarehouseErrorCode; + field: string | null; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseUpdate_updateWarehouse_warehouse_shippingZones_edges_node; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseUpdate_updateWarehouse_warehouse_shippingZones_edges[]; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseUpdate_updateWarehouse_warehouse_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseUpdate_updateWarehouse_warehouse { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseUpdate_updateWarehouse_warehouse_shippingZones; + address: WarehouseUpdate_updateWarehouse_warehouse_address; +} + +export interface WarehouseUpdate_updateWarehouse { + __typename: "WarehouseUpdate"; + errors: WarehouseUpdate_updateWarehouse_errors[]; + warehouse: WarehouseUpdate_updateWarehouse_warehouse | null; +} + +export interface WarehouseUpdate { + updateWarehouse: WarehouseUpdate_updateWarehouse | null; +} + +export interface WarehouseUpdateVariables { + id: string; + input: WarehouseUpdateInput; +} diff --git a/src/warehouses/types/WarehouseWithShippingFragment.ts b/src/warehouses/types/WarehouseWithShippingFragment.ts new file mode 100644 index 000000000..85f1ab270 --- /dev/null +++ b/src/warehouses/types/WarehouseWithShippingFragment.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: WarehouseWithShippingFragment +// ==================================================== + +export interface WarehouseWithShippingFragment_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseWithShippingFragment_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseWithShippingFragment_shippingZones_edges_node; +} + +export interface WarehouseWithShippingFragment_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseWithShippingFragment_shippingZones_edges[]; +} + +export interface WarehouseWithShippingFragment { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseWithShippingFragment_shippingZones; +} diff --git a/src/warehouses/urls.ts b/src/warehouses/urls.ts new file mode 100644 index 000000000..d84d2266a --- /dev/null +++ b/src/warehouses/urls.ts @@ -0,0 +1,42 @@ +import { stringify as stringifyQs } from "qs"; +import urlJoin from "url-join"; + +import { + ActiveTab, + Dialog, + Filters, + Pagination, + SingleAction, + TabActionDialog, + Sort +} from "../types"; + +export const warehouseSection = "/warehouses/"; + +export const warehouseListPath = warehouseSection; +export enum WarehouseListUrlFiltersEnum { + query = "query" +} +export type WarehouseListUrlFilters = Filters; +export type WarehouseListUrlDialog = "delete" | TabActionDialog; +export enum WarehouseListUrlSortField { + name = "name" +} +export type WarehouseListUrlSort = Sort; +export type WarehouseListUrlQueryParams = ActiveTab & + Dialog & + Pagination & + WarehouseListUrlFilters & + WarehouseListUrlSort & + SingleAction; +export const warehouseListUrl = (params?: WarehouseListUrlQueryParams) => + warehouseListPath + "?" + stringifyQs(params); + +export const warehousePath = (id: string) => urlJoin(warehouseSection, id); +export type WarehouseUrlDialog = "delete"; +export type WarehouseUrlQueryParams = Dialog & SingleAction; +export const warehouseUrl = (id: string, params?: WarehouseUrlQueryParams) => + warehousePath(encodeURIComponent(id)) + "?" + stringifyQs(params); + +export const warehouseAddPath = urlJoin(warehouseSection, "add"); +export const warehouseAddUrl = warehouseAddPath; diff --git a/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx b/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx new file mode 100644 index 000000000..4698713ce --- /dev/null +++ b/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import WarehouseCreatePage from "@saleor/warehouses/components/WarehouseCreatePage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import { warehouseListUrl, warehouseUrl } from "@saleor/warehouses/urls"; +import { useWarehouseCreate } from "@saleor/warehouses/mutations"; +import { commonMessages } from "@saleor/intl"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { maybe, findValueInEnum, getMutationStatus } from "@saleor/misc"; +import { CountryCode } from "@saleor/types/globalTypes"; +import useShop from "@saleor/hooks/useShop"; +import { WindowTitle } from "@saleor/components/WindowTitle"; + +const WarehouseCreate: React.FC = () => { + const intl = useIntl(); + const navigate = useNavigator(); + const notify = useNotifier(); + const shop = useShop(); + const [createWarehouse, createWarehouseOpts] = useWarehouseCreate({ + onCompleted: data => { + if (data.createWarehouse.errors.length === 0) { + navigate(warehouseUrl(data.createWarehouse.warehouse.id)); + notify({ text: intl.formatMessage(commonMessages.savedChanges) }); + } + } + }); + const createWarehouseTransitionState = getMutationStatus(createWarehouseOpts); + + return ( + <> + + shop.countries, [])} + disabled={createWarehouseOpts.loading} + errors={maybe( + () => createWarehouseOpts.data.createWarehouse.errors, + [] + )} + saveButtonBarState={createWarehouseTransitionState} + onBack={() => navigate(warehouseListUrl())} + onSubmit={data => + createWarehouse({ + variables: { + input: { + address: { + city: data.city, + cityArea: data.cityArea, + country: findValueInEnum(data.country, CountryCode), + countryArea: data.countryArea, + phone: data.phone, + postalCode: data.postalCode, + streetAddress1: data.streetAddress1, + streetAddress2: data.streetAddress2 + }, + name: data.name + } + } + }) + } + /> + + ); +}; + +WarehouseCreate.displayName = "WarehouseCreate"; +export default WarehouseCreate; diff --git a/src/warehouses/views/WarehouseCreate/index.ts b/src/warehouses/views/WarehouseCreate/index.ts new file mode 100644 index 000000000..d73de272c --- /dev/null +++ b/src/warehouses/views/WarehouseCreate/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseCreate"; +export * from "./WarehouseCreate"; diff --git a/src/warehouses/views/WarehouseDetails/WarehouseDetails.tsx b/src/warehouses/views/WarehouseDetails/WarehouseDetails.tsx new file mode 100644 index 000000000..b8655ef00 --- /dev/null +++ b/src/warehouses/views/WarehouseDetails/WarehouseDetails.tsx @@ -0,0 +1,125 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import WarehouseDetailsPage from "@saleor/warehouses/components/WarehouseDetailsPage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import { + warehouseListUrl, + WarehouseUrlQueryParams, + warehouseUrl +} from "@saleor/warehouses/urls"; +import { useWarehouseDetails } from "@saleor/warehouses/queries"; +import { commonMessages } from "@saleor/intl"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { + findValueInEnum, + getMutationStatus, + getStringOrPlaceholder +} from "@saleor/misc"; +import { CountryCode } from "@saleor/types/globalTypes"; +import useShop from "@saleor/hooks/useShop"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { + useWarehouseUpdate, + useWarehouseDelete +} from "@saleor/warehouses/mutations"; +import { shippingZoneUrl } from "@saleor/shipping/urls"; +import WarehouseDeleteDialog from "@saleor/warehouses/components/WarehouseDeleteDialog"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import NotFoundPage from "@saleor/components/NotFoundPage"; + +export interface WarehouseDetailsProps { + id: string; + params: WarehouseUrlQueryParams; +} + +const WarehouseDetails: React.FC = ({ id, params }) => { + const intl = useIntl(); + const navigate = useNavigator(); + const notify = useNotifier(); + const shop = useShop(); + const { data, loading } = useWarehouseDetails({ + displayLoader: true, + variables: { id } + }); + const [updateWarehouse, updateWarehouseOpts] = useWarehouseUpdate({ + onCompleted: data => { + if (data.updateWarehouse.errors.length === 0) { + notify({ text: intl.formatMessage(commonMessages.savedChanges) }); + } + } + }); + const updateWarehouseTransitionState = getMutationStatus(updateWarehouseOpts); + + const [deleteWarehouse, deleteWarehouseOpts] = useWarehouseDelete({ + onCompleted: data => { + if (data.deleteWarehouse.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + navigate(warehouseListUrl()); + } + } + }); + const deleteWarehouseTransitionState = getMutationStatus(deleteWarehouseOpts); + + const [openModal, closeModal] = createDialogActionHandlers( + navigate, + params => warehouseUrl(id, params), + params + ); + + if (data?.warehouse === null) { + return navigate(warehouseListUrl())} />; + } + + return ( + <> + + navigate(warehouseListUrl())} + onDelete={() => openModal("delete")} + onShippingZoneClick={id => navigate(shippingZoneUrl(id))} + onSubmit={data => + updateWarehouse({ + variables: { + id, + input: { + address: { + city: data.city, + cityArea: data.cityArea, + country: findValueInEnum(data.country, CountryCode), + countryArea: data.countryArea, + phone: data.phone, + postalCode: data.postalCode, + streetAddress1: data.streetAddress1, + streetAddress2: data.streetAddress2 + }, + name: data.name + } + } + }) + } + /> + + deleteWarehouse({ + variables: { id } + }) + } + open={params.action === "delete"} + /> + + ); +}; + +WarehouseDetails.displayName = "WarehouseDetails"; +export default WarehouseDetails; diff --git a/src/warehouses/views/WarehouseDetails/index.ts b/src/warehouses/views/WarehouseDetails/index.ts new file mode 100644 index 000000000..a8e1f03bc --- /dev/null +++ b/src/warehouses/views/WarehouseDetails/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseDetails"; +export * from "./WarehouseDetails"; diff --git a/src/warehouses/views/WarehouseList/WarehouseList.tsx b/src/warehouses/views/WarehouseList/WarehouseList.tsx new file mode 100644 index 000000000..4609456a6 --- /dev/null +++ b/src/warehouses/views/WarehouseList/WarehouseList.tsx @@ -0,0 +1,192 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import { + WarehouseListUrlQueryParams, + warehouseUrl, + WarehouseListUrlDialog, + warehouseListUrl, + warehouseAddUrl +} from "@saleor/warehouses/urls"; +import useNavigator from "@saleor/hooks/useNavigator"; +import { useWarehouseList } from "@saleor/warehouses/queries"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import useListSettings from "@saleor/hooks/useListSettings"; +import { ListViews } from "@saleor/types"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { sectionNames, commonMessages } from "@saleor/intl"; +import WarehouseListPage from "@saleor/warehouses/components/WarehouseListPage"; +import SaveFilterTabDialog, { + SaveFilterTabDialogFormData +} from "@saleor/components/SaveFilterTabDialog"; +import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; +import { maybe, getMutationStatus } from "@saleor/misc"; +import { getSortParams } from "@saleor/utils/sort"; +import createSortHandler from "@saleor/utils/handlers/sortHandler"; +import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { configurationMenuUrl } from "@saleor/configuration"; +import WarehouseDeleteDialog from "@saleor/warehouses/components/WarehouseDeleteDialog"; +import { useWarehouseDelete } from "@saleor/warehouses/mutations"; +import { getSortQueryVariables } from "./sort"; +import { + getFilterVariables, + getFilterTabs, + areFiltersApplied, + deleteFilterTab, + saveFilterTab, + getActiveFilters +} from "./filters"; + +export interface WarehouseListProps { + params: WarehouseListUrlQueryParams; +} + +const WarehouseList: React.FC = ({ params }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const paginate = usePaginator(); + const { updateListSettings, settings } = useListSettings( + ListViews.SALES_LIST + ); + const intl = useIntl(); + + const paginationState = createPaginationState(settings.rowNumber, params); + const queryVariables = React.useMemo( + () => ({ + ...paginationState, + filter: getFilterVariables(params), + sort: getSortQueryVariables(params) + }), + [params] + ); + const { data, loading, refetch } = useWarehouseList({ + displayLoader: true, + variables: queryVariables + }); + const [deleteWarehouse, deleteWarehouseOpts] = useWarehouseDelete({ + onCompleted: data => { + if (data.deleteWarehouse.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + refetch(); + closeModal(); + } + } + }); + + const tabs = getFilterTabs(); + + const currentTab = + params.activeTab === undefined + ? areFiltersApplied(params) + ? tabs.length + 1 + : 0 + : parseInt(params.activeTab, 0); + + const [, resetFilters, handleSearchChange] = createFilterHandlers({ + createUrl: warehouseListUrl, + getFilterQueryParam: () => undefined, + navigate, + params + }); + + const [openModal, closeModal] = createDialogActionHandlers< + WarehouseListUrlDialog, + WarehouseListUrlQueryParams + >(navigate, warehouseListUrl, params); + + const handleTabChange = (tab: number) => + navigate( + warehouseListUrl({ + activeTab: tab.toString(), + ...getFilterTabs()[tab - 1].data + }) + ); + + const handleTabDelete = () => { + deleteFilterTab(currentTab); + navigate(warehouseListUrl()); + }; + + const handleTabSave = (data: SaveFilterTabDialogFormData) => { + saveFilterTab(data.name, getActiveFilters(params)); + handleTabChange(tabs.length + 1); + }; + + const { loadNextPage, loadPreviousPage, pageInfo } = paginate( + maybe(() => data.warehouses.pageInfo), + paginationState, + params + ); + + const handleSort = createSortHandler(navigate, warehouseListUrl, params); + + const deleteTransitionState = getMutationStatus(deleteWarehouseOpts); + + return ( + <> + + navigate(configurationMenuUrl)} + onTabChange={handleTabChange} + onTabDelete={() => openModal("delete-search")} + onTabSave={() => openModal("save-search")} + tabs={tabs.map(tab => tab.name)} + warehouses={maybe(() => data.warehouses.edges.map(edge => edge.node))} + settings={settings} + disabled={loading} + pageInfo={pageInfo} + onAdd={() => navigate(warehouseAddUrl)} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onRemove={id => openModal("delete", { id })} + onSort={handleSort} + onUpdateListSettings={updateListSettings} + onRowClick={id => () => navigate(warehouseUrl(id))} + sort={getSortParams(params)} + /> + + data.warehouses.edges.find(edge => edge.node.id === params.id).node + .name + )} + open={params.action === "delete"} + onClose={closeModal} + onConfirm={() => + deleteWarehouse({ + variables: { + id: params.id + } + }) + } + /> + + tabs[currentTab - 1].name, "...")} + /> + + ); +}; + +WarehouseList.displayName = "WarehouseList"; +export default WarehouseList; diff --git a/src/warehouses/views/WarehouseList/filters.ts b/src/warehouses/views/WarehouseList/filters.ts new file mode 100644 index 000000000..f0e6a19f3 --- /dev/null +++ b/src/warehouses/views/WarehouseList/filters.ts @@ -0,0 +1,31 @@ +import { WarehouseFilterInput } from "@saleor/types/globalTypes"; +import { + createFilterTabUtils, + createFilterUtils +} from "../../../utils/filters"; +import { + WarehouseListUrlFilters, + WarehouseListUrlFiltersEnum, + WarehouseListUrlQueryParams +} from "../../urls"; + +export const WAREHOUSE_FILTERS_KEY = "warehouseFilters"; + +export function getFilterVariables( + params: WarehouseListUrlFilters +): WarehouseFilterInput { + return { + search: params.query + }; +} + +export const { + deleteFilterTab, + getFilterTabs, + saveFilterTab +} = createFilterTabUtils(WAREHOUSE_FILTERS_KEY); + +export const { areFiltersApplied, getActiveFilters } = createFilterUtils< + WarehouseListUrlQueryParams, + WarehouseListUrlFilters +>(WarehouseListUrlFiltersEnum); diff --git a/src/warehouses/views/WarehouseList/index.ts b/src/warehouses/views/WarehouseList/index.ts new file mode 100644 index 000000000..4c64e0c2c --- /dev/null +++ b/src/warehouses/views/WarehouseList/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseList"; +export * from "./WarehouseList"; diff --git a/src/warehouses/views/WarehouseList/sort.ts b/src/warehouses/views/WarehouseList/sort.ts new file mode 100644 index 000000000..ccf74b0fb --- /dev/null +++ b/src/warehouses/views/WarehouseList/sort.ts @@ -0,0 +1,18 @@ +import { WarehouseListUrlSortField } from "@saleor/warehouses/urls"; +import { WarehouseSortField } from "@saleor/types/globalTypes"; +import { createGetSortQueryVariables } from "@saleor/utils/sort"; + +export function getSortQueryField( + sort: WarehouseListUrlSortField +): WarehouseSortField { + switch (sort) { + case WarehouseListUrlSortField.name: + return WarehouseSortField.NAME; + default: + return undefined; + } +} + +export const getSortQueryVariables = createGetSortQueryVariables( + getSortQueryField +);