commit
40b3791f0e
228 changed files with 31311 additions and 12121 deletions
|
@ -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
|
- Filter column ids before send it to GridAttributes operation - #476 by @gabmartinez
|
||||||
- Display Is Published column correctly in main Product Listing - #475 by @gabmartinez
|
- Display Is Published column correctly in main Product Listing - #475 by @gabmartinez
|
||||||
- Add Permission Groups section - #406 by @krzysztofwolski
|
- Add Permission Groups section - #406 by @krzysztofwolski
|
||||||
|
- Add warehouse management - #390 by @dominik-zeglen
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
{
|
{
|
||||||
"OrderCancelDialogButton": {
|
|
||||||
"context": "button",
|
|
||||||
"string": "Cancel Order"
|
|
||||||
},
|
|
||||||
"OrderCancelDialogHeader": {
|
"OrderCancelDialogHeader": {
|
||||||
"context": "dialog header",
|
"context": "dialog header",
|
||||||
"string": "Cancel Order"
|
"string": "Cancel Order"
|
||||||
|
@ -34,6 +30,9 @@
|
||||||
"configurationMenuTaxes": {
|
"configurationMenuTaxes": {
|
||||||
"string": "Manage how your store charges tax"
|
"string": "Manage how your store charges tax"
|
||||||
},
|
},
|
||||||
|
"configurationMenuWarehouses": {
|
||||||
|
"string": "Manage and update your warehouse information"
|
||||||
|
},
|
||||||
"configurationPluginsPages": {
|
"configurationPluginsPages": {
|
||||||
"string": "View and update your plugins and their settings."
|
"string": "View and update your plugins and their settings."
|
||||||
},
|
},
|
||||||
|
@ -206,10 +205,6 @@
|
||||||
"context": "vat not included in order price",
|
"context": "vat not included in order price",
|
||||||
"string": "does not apply"
|
"string": "does not apply"
|
||||||
},
|
},
|
||||||
"prodictStockInventoryLabel": {
|
|
||||||
"context": "product stock",
|
|
||||||
"string": "Inventory"
|
|
||||||
},
|
|
||||||
"productStatusLabel": {
|
"productStatusLabel": {
|
||||||
"context": "product",
|
"context": "product",
|
||||||
"string": "Published"
|
"string": "Published"
|
||||||
|
@ -240,13 +235,17 @@
|
||||||
"context": "variant price",
|
"context": "variant price",
|
||||||
"string": "Price"
|
"string": "Price"
|
||||||
},
|
},
|
||||||
"productVariantCreatePricesSetStockPlaceholder": {
|
|
||||||
"context": "variant stock",
|
|
||||||
"string": "Stock"
|
|
||||||
},
|
|
||||||
"productVariantCreatePricesStockInputLabel": {
|
"productVariantCreatePricesStockInputLabel": {
|
||||||
"string": "Stock"
|
"string": "Stock"
|
||||||
},
|
},
|
||||||
|
"productVariantCreatorStockSectionHeader": {
|
||||||
|
"context": "variant stock, header",
|
||||||
|
"string": "Stock"
|
||||||
|
},
|
||||||
|
"productVariantCreatorWarehouseSectionHeader": {
|
||||||
|
"context": "header",
|
||||||
|
"string": "Warehouses"
|
||||||
|
},
|
||||||
"productVariantPriceOptionalCostPriceField": {
|
"productVariantPriceOptionalCostPriceField": {
|
||||||
"context": "optional field",
|
"context": "optional field",
|
||||||
"string": "Optional"
|
"string": "Optional"
|
||||||
|
@ -287,10 +286,18 @@
|
||||||
"context": "delete shipping zone",
|
"context": "delete shipping zone",
|
||||||
"string": "Are you sure you want to delete {name}?"
|
"string": "Are you sure you want to delete {name}?"
|
||||||
},
|
},
|
||||||
|
"shippingZoneWarehouses_dot_autocomplete_dot_label": {
|
||||||
|
"context": "autocomplete select label",
|
||||||
|
"string": "Warehouse"
|
||||||
|
},
|
||||||
"siteSettingsMailingHelperText": {
|
"siteSettingsMailingHelperText": {
|
||||||
"context": "helper text",
|
"context": "helper text",
|
||||||
"string": "Mailing Configuration"
|
"string": "Mailing Configuration"
|
||||||
},
|
},
|
||||||
|
"src_dot_accept": {
|
||||||
|
"context": "button",
|
||||||
|
"string": "Accept"
|
||||||
|
},
|
||||||
"src_dot_attributes": {
|
"src_dot_attributes": {
|
||||||
"context": "attributes section name",
|
"context": "attributes section name",
|
||||||
"string": "Attributes"
|
"string": "Attributes"
|
||||||
|
@ -1020,6 +1027,30 @@
|
||||||
"context": "pick columns to display",
|
"context": "pick columns to display",
|
||||||
"string": "{numberOfSelected} columns selected out of {numberOfTotal}"
|
"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": {
|
"src_dot_components_dot_ConfirmButton_dot_2845142593": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Error"
|
"string": "Error"
|
||||||
|
@ -2191,6 +2222,10 @@
|
||||||
"src_dot_no": {
|
"src_dot_no": {
|
||||||
"string": "No"
|
"string": "No"
|
||||||
},
|
},
|
||||||
|
"src_dot_ok": {
|
||||||
|
"context": "button",
|
||||||
|
"string": "OK"
|
||||||
|
},
|
||||||
"src_dot_optionalField": {
|
"src_dot_optionalField": {
|
||||||
"context": "field is optional",
|
"context": "field is optional",
|
||||||
"string": "Optional"
|
"string": "Optional"
|
||||||
|
@ -2211,19 +2246,18 @@
|
||||||
"context": "dialog header",
|
"context": "dialog header",
|
||||||
"string": "Cancel Orders"
|
"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": {
|
"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?}}"
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderCancelDialog_dot_4059738695": {
|
||||||
"string": "Are you sure you want to cancel order #{orderNumber}?"
|
"string": "Cancelling this order will release unfulfilled stocks, so they can be bought by other customers. <b>Order will not be refunded when cancelling order - You need to do it manually.</b> Are you sure you want to cancel this order?"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderCancelDialog_dot_944150063": {
|
"src_dot_orders_dot_components_dot_OrderCannotCancelOrderDialog_dot_1561587911": {
|
||||||
"context": "switch button",
|
"context": "dialog header",
|
||||||
"string": "Release all stock allocated to this order"
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderCustomerNote_dot_1505053535": {
|
||||||
"string": "No notes from customer"
|
"string": "No notes from customer"
|
||||||
|
@ -2392,16 +2426,51 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Finalize"
|
"string": "Finalize"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_2569854889": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_1608534452": {
|
||||||
"string": "Are you sure you want to cancel this fulfillment?"
|
"context": "page header",
|
||||||
|
"string": "Order no. {orderNumber} - Add Fulfillment"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_3515223857": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_2094985970": {
|
||||||
"context": "switch button",
|
"context": "quantity of fulfilled products",
|
||||||
"string": "Restock items?"
|
"string": "Quantity to fulfill"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_675709443": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_2095687440": {
|
||||||
"context": "button",
|
"context": "fulfill order, button",
|
||||||
"string": "Cancel fulfillment"
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderFulfillmentCancelDialog_dot_732594284": {
|
||||||
"context": "dialog header",
|
"context": "dialog header",
|
||||||
|
@ -2456,9 +2525,17 @@
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Fulfilled ({quantity})"
|
"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",
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderFulfillment_dot_878013594": {
|
||||||
"context": "order line total price",
|
"context": "order line total price",
|
||||||
|
@ -2766,9 +2843,6 @@
|
||||||
"src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": {
|
"src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": {
|
||||||
"string": "Order successfully updated"
|
"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": {
|
"src_dot_orders_dot_views_dot_OrderDetails_dot_580490159": {
|
||||||
"context": "window title",
|
"context": "window title",
|
||||||
"string": "Order #{orderNumber}"
|
"string": "Order #{orderNumber}"
|
||||||
|
@ -2800,16 +2874,21 @@
|
||||||
"context": "dialog content",
|
"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?}}"
|
"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": {
|
"src_dot_orders_dot_views_dot_OrderFulfill_dot_1186021594": {
|
||||||
"string": "Orders cancelled"
|
"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": {
|
"src_dot_orders_dot_views_dot_OrderList_dot_1738939038": {
|
||||||
"string": "Order draft successfully created"
|
"string": "Order draft successfully created"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_views_dot_OrderList_dot_3528672691": {
|
|
||||||
"context": "cancel orders, button",
|
|
||||||
"string": "Cancel"
|
|
||||||
},
|
|
||||||
"src_dot_pages": {
|
"src_dot_pages": {
|
||||||
"context": "pages section name",
|
"context": "pages section name",
|
||||||
"string": "Pages"
|
"string": "Pages"
|
||||||
|
@ -3520,13 +3599,35 @@
|
||||||
"src_dot_products_dot_components_dot_ProductPricing_dot_3015886868": {
|
"src_dot_products_dot_components_dot_ProductPricing_dot_3015886868": {
|
||||||
"string": "Charge taxes for this item"
|
"string": "Charge taxes for this item"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductStock_dot_1680952454": {
|
"src_dot_products_dot_components_dot_ProductStocks_dot_2585918415": {
|
||||||
"context": "allocated product stock",
|
|
||||||
"string": "Allocated: {quantity}"
|
|
||||||
},
|
|
||||||
"src_dot_products_dot_components_dot_ProductStock_dot_2585918415": {
|
|
||||||
"string": "SKU (Stock Keeping Unit)"
|
"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 <l>here</l>."
|
||||||
|
},
|
||||||
|
"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": {
|
"src_dot_products_dot_components_dot_ProductUpdatePage_dot_1815688500": {
|
||||||
"context": "product",
|
"context": "product",
|
||||||
"string": "since {date}"
|
"string": "since {date}"
|
||||||
|
@ -3546,81 +3647,6 @@
|
||||||
"context": "product attribute error",
|
"context": "product attribute error",
|
||||||
"string": "This variant already exists"
|
"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": {
|
"src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_2853608829": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Save variant"
|
"string": "Save variant"
|
||||||
|
@ -3629,6 +3655,96 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Delete Variant"
|
"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": {
|
"src_dot_products_dot_components_dot_ProductVariantDeleteDialog_dot_1583616500": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Delete variant"
|
"string": "Delete variant"
|
||||||
|
@ -3678,23 +3794,12 @@
|
||||||
"src_dot_products_dot_components_dot_ProductVariantPrice_dot_2238565650": {
|
"src_dot_products_dot_components_dot_ProductVariantPrice_dot_2238565650": {
|
||||||
"string": "Selling price override"
|
"string": "Selling price override"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariantStock_dot_1680952454": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_1001303107": {
|
||||||
"context": "variant allocated stock",
|
"context": "product variant inventory",
|
||||||
"string": "Allocated: {quantity}"
|
"string": "Unavailable in all locations"
|
||||||
},
|
|
||||||
"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_1033175132": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_1033175132": {
|
||||||
"context": "product variant status",
|
"context": "product variant inventory",
|
||||||
"string": "Unavailable"
|
"string": "Unavailable"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariants_dot_1134347598": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_1134347598": {
|
||||||
|
@ -3705,32 +3810,55 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Create variants"
|
"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": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_2153006789": {
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Variants"
|
"string": "Variants"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariants_dot_2157131639": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_2496096212": {
|
||||||
"context": "product variant status",
|
"context": "product variant inventory",
|
||||||
"string": "Available"
|
"string": "{numLocations,plural,one{{numAvailable} available at {numLocations} location} other{{numAvailable} available at {numLocations} locations}}"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariants_dot_277989856": {
|
"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"
|
"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": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_3989383405": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Create variant"
|
"string": "Create variant"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariants_dot_636461959": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_4017567521": {
|
||||||
"context": "product variant name",
|
"context": "product variant inventory",
|
||||||
"string": "Name"
|
"string": "Not stocked"
|
||||||
},
|
},
|
||||||
"src_dot_products_dot_components_dot_ProductVariants_dot_693960049": {
|
"src_dot_products_dot_components_dot_ProductVariants_dot_693960049": {
|
||||||
"string": "SKU"
|
"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": {
|
"src_dot_products_dot_views_dot_1542417144": {
|
||||||
"context": "window title",
|
"context": "window title",
|
||||||
"string": "Create Product"
|
"string": "Create Product"
|
||||||
|
@ -3811,6 +3939,14 @@
|
||||||
"context": "dialog header",
|
"context": "dialog header",
|
||||||
"string": "Delete Product"
|
"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": {
|
"src_dot_properties": {
|
||||||
"string": "Properties"
|
"string": "Properties"
|
||||||
},
|
},
|
||||||
|
@ -4014,6 +4150,13 @@
|
||||||
"src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_549146363": {
|
"src_dot_shipping_dot_components_dot_ShippingWeightUnitForm_dot_549146363": {
|
||||||
"string": "Shipping Weight Unit"
|
"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": {
|
"src_dot_shipping_dot_components_dot_ShippingZoneCountriesAssignDialog_dot_1003092716": {
|
||||||
"string": "Rest of the World"
|
"string": "Rest of the World"
|
||||||
},
|
},
|
||||||
|
@ -4184,6 +4327,21 @@
|
||||||
"context": "shipping method name",
|
"context": "shipping method name",
|
||||||
"string": "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": {
|
"src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1325966144": {
|
||||||
"context": "header",
|
"context": "header",
|
||||||
"string": "Shipping"
|
"string": "Shipping"
|
||||||
|
@ -4245,34 +4403,6 @@
|
||||||
"context": "site settings section name",
|
"context": "site settings section name",
|
||||||
"string": "Site Settings"
|
"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": {
|
"src_dot_siteSettings_dot_components_dot_SiteSettingsDetails_dot_1008586926": {
|
||||||
"string": "Name of your store is shown on tab in web browser"
|
"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": {
|
"src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_1214877701": {
|
||||||
"string": "Authentication method defines additional ways that customers can log in to your ecommerce."
|
"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": {
|
"src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_2768400497": {
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Company Information"
|
"string": "Company Information"
|
||||||
|
@ -4980,6 +5114,10 @@
|
||||||
"context": "bulk variant create error",
|
"context": "bulk variant create error",
|
||||||
"string": "SKUs must be unique"
|
"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": {
|
"src_dot_utils_dot_errors_dot_tooCommon": {
|
||||||
"string": "This password is too commonly used"
|
"string": "This password is too commonly used"
|
||||||
},
|
},
|
||||||
|
@ -5002,6 +5140,68 @@
|
||||||
"context": "vouchers section name",
|
"context": "vouchers section name",
|
||||||
"string": "Vouchers"
|
"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": {
|
"src_dot_webhooks": {
|
||||||
"context": "webhooks section name",
|
"context": "webhooks section name",
|
||||||
"string": "Webhooks"
|
"string": "Webhooks"
|
||||||
|
|
60
package-lock.json
generated
60
package-lock.json
generated
|
@ -16,56 +16,56 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@apollo/react-common": {
|
"@apollo/react-common": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.4.tgz",
|
||||||
"integrity": "sha512-Q7ZjDOeqjJf/AOGxUMdGxKF+JVClRXrYBGVq+SuVFqANRpd68MxtVV2OjCWavsFAN0eqYnRqRUrl7vtUCiJqeg==",
|
"integrity": "sha512-X5Kyro73bthWSCBJUC5XYQqMnG0dLWuDZmVkzog9dynovhfiVCV4kPSdgSIkqnb++cwCzOVuQ4rDKVwo2XRzQA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ts-invariant": "^0.4.4",
|
"ts-invariant": "^0.4.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@apollo/react-components": {
|
"@apollo/react-components": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/react-components/-/react-components-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/react-components/-/react-components-3.1.5.tgz",
|
||||||
"integrity": "sha512-H0l2JKDQMz+LkM93QK7j3ThbNXkWQCduN3s3eKxFN3Rdg7rXsrikJWvx2wQ868jmqy0VhwJbS1vYdRLdh114uQ==",
|
"integrity": "sha512-c82VyUuE9VBnJB7bnX+3dmwpIPMhyjMwyoSLyQWPHxz8jK4ak30XszJtqFf4eC4hwvvLYa+Ou6X73Q8V8e2/jg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@apollo/react-common": "^3.1.3",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.5",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"ts-invariant": "^0.4.4",
|
"ts-invariant": "^0.4.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@apollo/react-hoc": {
|
"@apollo/react-hoc": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/react-hoc/-/react-hoc-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/react-hoc/-/react-hoc-3.1.5.tgz",
|
||||||
"integrity": "sha512-oCPma0uBVPTcYTR5sOvtMbpaWll4xDBvYfKr6YkDorUcQVeNzFu1LK1kmQjJP64bKsaziKYji5ibFaeCnVptmA==",
|
"integrity": "sha512-jlZ2pvEnRevLa54H563BU0/xrYSgWQ72GksarxUzCHQW85nmn9wQln0kLBX7Ua7SBt9WgiuYQXQVechaaCulfQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@apollo/react-common": "^3.1.3",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@apollo/react-components": "^3.1.3",
|
"@apollo/react-components": "^3.1.5",
|
||||||
"hoist-non-react-statics": "^3.3.0",
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
"ts-invariant": "^0.4.4",
|
"ts-invariant": "^0.4.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@apollo/react-hooks": {
|
"@apollo/react-hooks": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.5.tgz",
|
||||||
"integrity": "sha512-reIRO9xKdfi+B4gT/o/hnXuopUnm7WED/ru8VQydPw+C/KG/05Ssg1ZdxFKHa3oxwiTUIDnevtccIH35POanbA==",
|
"integrity": "sha512-y0CJ393DLxIIkksRup4nt+vSjxalbZBXnnXxYbviq/woj+zKa431zy0yT4LqyRKpFy9ahMIwxBnBwfwIoupqLQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@apollo/react-common": "^3.1.3",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@wry/equality": "^0.1.9",
|
"@wry/equality": "^0.1.9",
|
||||||
"ts-invariant": "^0.4.4",
|
"ts-invariant": "^0.4.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@apollo/react-ssr": {
|
"@apollo/react-ssr": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.5.tgz",
|
||||||
"integrity": "sha512-fUTmEYHxSTX1GA43B8vICxXXplpcEBnDwn0IgdAc3eG0p2YK97ZrJDRFCJ5vD7fyDZsrYhMf+rAI3sd+H2SS+A==",
|
"integrity": "sha512-wuLPkKlctNn3u8EU8rlECyktpOUCeekFfb0KhIKknpGY6Lza2Qu0bThx7D9MIbVEzhKadNNrzLcpk0Y8/5UuWg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@apollo/react-common": "^3.1.3",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.5",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -17593,15 +17593,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-apollo": {
|
"react-apollo": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-3.1.5.tgz",
|
||||||
"integrity": "sha512-orCZNoAkgveaK5b75y7fw1MSqSHOU/Wuu9rRFOGmRQBSQVZjvV4DI+hj604rHmuN9+WDABxb5W48wTa0F/xNZQ==",
|
"integrity": "sha512-xOxMqxORps+WHrUYbjVHPliviomefOpu5Sh35oO3osuOyPTxvrljdfTLGCggMhcXBsDljtS5Oy4g+ijWg3D4JQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@apollo/react-common": "^3.1.3",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@apollo/react-components": "^3.1.3",
|
"@apollo/react-components": "^3.1.5",
|
||||||
"@apollo/react-hoc": "^3.1.3",
|
"@apollo/react-hoc": "^3.1.5",
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.5",
|
||||||
"@apollo/react-ssr": "^3.1.3"
|
"@apollo/react-ssr": "^3.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-clientside-effect": {
|
"react-clientside-effect": {
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"moment-timezone": "^0.5.26",
|
"moment-timezone": "^0.5.26",
|
||||||
"qs": "^6.9.0",
|
"qs": "^6.9.0",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-apollo": "^3.1.3",
|
"react-apollo": "^3.1.4",
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
"react-dropzone": "^8.2.0",
|
"react-dropzone": "^8.2.0",
|
||||||
"react-error-boundary": "^1.2.5",
|
"react-error-boundary": "^1.2.5",
|
||||||
|
|
2594
schema.graphql
2594
schema.graphql
File diff suppressed because it is too large
Load diff
|
@ -172,7 +172,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
errors={updateResult.data?.categoryUpdate.errors || []}
|
errors={updateResult.data?.categoryUpdate.errors || []}
|
||||||
onAddCategory={() => navigate(categoryAddUrl(id))}
|
onAddCategory={() => navigate(categoryAddUrl(id))}
|
||||||
onAddProduct={() => navigate(productAddUrl)}
|
onAddProduct={() => navigate(productAddUrl())}
|
||||||
onBack={() =>
|
onBack={() =>
|
||||||
navigate(
|
navigate(
|
||||||
maybe(
|
maybe(
|
||||||
|
|
197
src/components/CompanyAddressInput/CompanyAddressForm.tsx
Normal file
197
src/components/CompanyAddressInput/CompanyAddressForm.tsx
Normal file
|
@ -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<CompanyAddressFormProps> = 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 (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.companyName}
|
||||||
|
helperText={getErrorMessage(formErrors.companyName, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Company"
|
||||||
|
})}
|
||||||
|
name={"companyName" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.companyName}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.streetAddress1}
|
||||||
|
helperText={getErrorMessage(formErrors.streetAddress1, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Address line 1"
|
||||||
|
})}
|
||||||
|
name={"streetAddress1" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.streetAddress1}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.streetAddress2}
|
||||||
|
helperText={getErrorMessage(formErrors.streetAddress2, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Address line 2"
|
||||||
|
})}
|
||||||
|
name={"streetAddress2" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.streetAddress2}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.city}
|
||||||
|
helperText={getErrorMessage(formErrors.city, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "City"
|
||||||
|
})}
|
||||||
|
name={"city" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.city}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.postalCode}
|
||||||
|
helperText={getErrorMessage(formErrors.postalCode, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "ZIP / Postal code"
|
||||||
|
})}
|
||||||
|
name={"postalCode" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.postalCode}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<FormSpacer />
|
||||||
|
<Grid>
|
||||||
|
<SingleAutocompleteSelectField
|
||||||
|
disabled={disabled}
|
||||||
|
displayValue={displayCountry}
|
||||||
|
error={!!formErrors.country}
|
||||||
|
helperText={getErrorMessage(formErrors.country, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Country"
|
||||||
|
})}
|
||||||
|
name={"country" as keyof AddressTypeInput}
|
||||||
|
onChange={onCountryChange}
|
||||||
|
value={data.country}
|
||||||
|
choices={countries}
|
||||||
|
InputProps={{
|
||||||
|
inputProps: {
|
||||||
|
autocomplete: "plsdontautocomplete" // Somehow it shuts it down
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.countryArea}
|
||||||
|
helperText={getErrorMessage(formErrors.countryArea, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Country area"
|
||||||
|
})}
|
||||||
|
name={"countryArea" as keyof AddressTypeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
value={data.countryArea}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<FormSpacer />
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!formErrors.phone}
|
||||||
|
fullWidth
|
||||||
|
helperText={getErrorMessage(formErrors.phone, intl)}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Phone"
|
||||||
|
})}
|
||||||
|
name={"phone" as keyof AddressTypeInput}
|
||||||
|
value={data.phone}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
CompanyAddressForm.displayName = "CompanyAddressForm";
|
||||||
|
export default CompanyAddressForm;
|
40
src/components/CompanyAddressInput/CompanyAddressInput.tsx
Normal file
40
src/components/CompanyAddressInput/CompanyAddressInput.tsx
Normal file
|
@ -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<CompanyAddressInputProps> = props => {
|
||||||
|
const { className, header, ...formProps } = props;
|
||||||
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={classNames(classes.root, className)}>
|
||||||
|
<CardTitle title={header} />
|
||||||
|
<CardContent>
|
||||||
|
<CompanyAddressForm {...formProps} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
CompanyAddressInput.displayName = "CompanyAddressInput";
|
||||||
|
export default CompanyAddressInput;
|
2
src/components/CompanyAddressInput/index.ts
Normal file
2
src/components/CompanyAddressInput/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./CompanyAddressInput";
|
||||||
|
export * from "./CompanyAddressInput";
|
|
@ -25,6 +25,7 @@ export const ControlledCheckbox: React.FC<ControlledCheckboxProps> = ({
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checked}
|
checked={checked}
|
||||||
|
disabled={disabled}
|
||||||
name={name}
|
name={name}
|
||||||
disableClickPropagation
|
disableClickPropagation
|
||||||
onChange={() => onChange({ target: { name, value: !checked } })}
|
onChange={() => onChange({ target: { name, value: !checked } })}
|
||||||
|
|
26
src/components/LinkChoice/LinkChoice.stories.tsx
Normal file
26
src/components/LinkChoice/LinkChoice.stories.tsx
Normal file
|
@ -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<LinkChoiceProps, "value" | "onChange"> = {
|
||||||
|
choices: suggestions.slice(0, 10),
|
||||||
|
name: "country"
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("Generics / Link with choices", module)
|
||||||
|
.addDecorator(CardDecorator)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("default", () => (
|
||||||
|
<Form initial={{ country: suggestions[1].value }}>
|
||||||
|
{({ change, data }) => (
|
||||||
|
<LinkChoice {...props} value={data.country} onChange={change} />
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
));
|
159
src/components/LinkChoice/LinkChoice.tsx
Normal file
159
src/components/LinkChoice/LinkChoice.tsx
Normal file
|
@ -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<LinkChoiceProps> = ({
|
||||||
|
className,
|
||||||
|
choices,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({});
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const anchor = React.useRef<HTMLInputElement>(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<HTMLSpanElement>) => {
|
||||||
|
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 (
|
||||||
|
<span
|
||||||
|
className={classNames(classes.root, className)}
|
||||||
|
ref={anchor}
|
||||||
|
onKeyDown={handleKeyPress}
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<Link onClick={() => setOpen(open => !open)}>{current.label}</Link>
|
||||||
|
<ArrowDropdown
|
||||||
|
className={classNames(classes.arrow, {
|
||||||
|
[classes.rotate]: open
|
||||||
|
})}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Popper
|
||||||
|
className={classes.popper}
|
||||||
|
open={open}
|
||||||
|
anchorEl={anchor.current}
|
||||||
|
transition
|
||||||
|
disablePortal
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<ClickAwayListener
|
||||||
|
onClickAway={() => setOpen(false)}
|
||||||
|
mouseEvent="onClick"
|
||||||
|
>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
{choices.map((choice, choiceIndex) => (
|
||||||
|
<MenuItem
|
||||||
|
className={classNames(classes.menuItem, {
|
||||||
|
[classes.highlighted]: highlightedIndex === choiceIndex
|
||||||
|
})}
|
||||||
|
selected={choice.value === value}
|
||||||
|
key={choice.value}
|
||||||
|
onClick={() => handleChange(choice.value)}
|
||||||
|
data-tc="select-option"
|
||||||
|
>
|
||||||
|
{choice.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Paper>
|
||||||
|
</ClickAwayListener>
|
||||||
|
</Popper>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
LinkChoice.displayName = "LinkChoice";
|
||||||
|
export default LinkChoice;
|
2
src/components/LinkChoice/index.ts
Normal file
2
src/components/LinkChoice/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./LinkChoice";
|
||||||
|
export * from "./LinkChoice";
|
|
@ -12,7 +12,8 @@ import Debounce, { DebounceProps } from "@saleor/components/Debounce";
|
||||||
import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown";
|
import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown";
|
||||||
import { FetchMoreProps } from "@saleor/types";
|
import { FetchMoreProps } from "@saleor/types";
|
||||||
import MultiAutocompleteSelectFieldContent, {
|
import MultiAutocompleteSelectFieldContent, {
|
||||||
MultiAutocompleteChoiceType
|
MultiAutocompleteChoiceType,
|
||||||
|
MultiAutocompleteActionType
|
||||||
} from "./MultiAutocompleteSelectFieldContent";
|
} from "./MultiAutocompleteSelectFieldContent";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -71,6 +72,7 @@ const useStyles = makeStyles(
|
||||||
|
|
||||||
export interface MultiAutocompleteSelectFieldProps
|
export interface MultiAutocompleteSelectFieldProps
|
||||||
extends Partial<FetchMoreProps> {
|
extends Partial<FetchMoreProps> {
|
||||||
|
add?: MultiAutocompleteActionType;
|
||||||
allowCustomValues?: boolean;
|
allowCustomValues?: boolean;
|
||||||
displayValues: MultiAutocompleteChoiceType[];
|
displayValues: MultiAutocompleteChoiceType[];
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
|
@ -91,6 +93,7 @@ const DebounceAutocomplete: React.ComponentType<DebounceProps<
|
||||||
|
|
||||||
const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFieldProps> = props => {
|
const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFieldProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
add,
|
||||||
allowCustomValues,
|
allowCustomValues,
|
||||||
choices,
|
choices,
|
||||||
displayValues,
|
displayValues,
|
||||||
|
@ -129,6 +132,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
|
||||||
itemToString={() => ""}
|
itemToString={() => ""}
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
|
closeMenu,
|
||||||
getInputProps,
|
getInputProps,
|
||||||
getItemProps,
|
getItemProps,
|
||||||
isOpen,
|
isOpen,
|
||||||
|
@ -166,6 +170,13 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
|
||||||
/>
|
/>
|
||||||
{isOpen && (!!inputValue || !!choices.length) && (
|
{isOpen && (!!inputValue || !!choices.length) && (
|
||||||
<MultiAutocompleteSelectFieldContent
|
<MultiAutocompleteSelectFieldContent
|
||||||
|
add={{
|
||||||
|
...add,
|
||||||
|
onClick: () => {
|
||||||
|
add.onClick();
|
||||||
|
closeMenu();
|
||||||
|
}
|
||||||
|
}}
|
||||||
choices={choices.filter(
|
choices={choices.filter(
|
||||||
choice => !value.includes(choice.value)
|
choice => !value.includes(choice.value)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl";
|
||||||
import chevronDown from "@assets/images/ChevronDown.svg";
|
import chevronDown from "@assets/images/ChevronDown.svg";
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import AddIcon from "@material-ui/icons/Add";
|
import AddIcon from "@material-ui/icons/Add";
|
||||||
|
@ -22,6 +23,10 @@ const menuItemHeight = 46;
|
||||||
const maxMenuItems = 5;
|
const maxMenuItems = 5;
|
||||||
const offset = 24;
|
const offset = 24;
|
||||||
|
|
||||||
|
export interface MultiAutocompleteActionType {
|
||||||
|
label: string;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
export interface MultiAutocompleteChoiceType {
|
export interface MultiAutocompleteChoiceType {
|
||||||
label: string;
|
label: string;
|
||||||
value: any;
|
value: any;
|
||||||
|
@ -29,6 +34,7 @@ export interface MultiAutocompleteChoiceType {
|
||||||
}
|
}
|
||||||
export interface MultiAutocompleteSelectFieldContentProps
|
export interface MultiAutocompleteSelectFieldContentProps
|
||||||
extends Partial<FetchMoreProps> {
|
extends Partial<FetchMoreProps> {
|
||||||
|
add?: MultiAutocompleteActionType;
|
||||||
choices: MultiAutocompleteChoiceType[];
|
choices: MultiAutocompleteChoiceType[];
|
||||||
displayCustomValue: boolean;
|
displayCustomValue: boolean;
|
||||||
displayValues: MultiAutocompleteChoiceType[];
|
displayValues: MultiAutocompleteChoiceType[];
|
||||||
|
@ -39,6 +45,14 @@ export interface MultiAutocompleteSelectFieldContentProps
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
add: {
|
||||||
|
background: theme.palette.background.default,
|
||||||
|
border: `1px solid ${theme.palette.divider}`,
|
||||||
|
borderRadius: "100%",
|
||||||
|
height: 24,
|
||||||
|
margin: theme.spacing(),
|
||||||
|
width: 24
|
||||||
|
},
|
||||||
addIcon: {
|
addIcon: {
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: 9,
|
margin: 9,
|
||||||
|
@ -100,6 +114,7 @@ const useStyles = makeStyles(
|
||||||
gridColumnGap: theme.spacing(1),
|
gridColumnGap: theme.spacing(1),
|
||||||
gridTemplateColumns: "30px 1fr",
|
gridTemplateColumns: "30px 1fr",
|
||||||
height: "auto",
|
height: "auto",
|
||||||
|
marginBottom: theme.spacing(0.5),
|
||||||
padding: 0,
|
padding: 0,
|
||||||
whiteSpace: "normal"
|
whiteSpace: "normal"
|
||||||
},
|
},
|
||||||
|
@ -130,10 +145,11 @@ const useStyles = makeStyles(
|
||||||
function getChoiceIndex(
|
function getChoiceIndex(
|
||||||
index: number,
|
index: number,
|
||||||
displayValues: MultiAutocompleteChoiceType[],
|
displayValues: MultiAutocompleteChoiceType[],
|
||||||
displayCustomValue: boolean
|
displayCustomValue: boolean,
|
||||||
|
add: boolean
|
||||||
) {
|
) {
|
||||||
let choiceIndex = index;
|
let choiceIndex = index;
|
||||||
if (displayCustomValue) {
|
if (add || displayCustomValue) {
|
||||||
choiceIndex += 2;
|
choiceIndex += 2;
|
||||||
}
|
}
|
||||||
if (displayValues.length > 0) {
|
if (displayValues.length > 0) {
|
||||||
|
@ -145,6 +161,7 @@ function getChoiceIndex(
|
||||||
|
|
||||||
const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectFieldContentProps> = props => {
|
const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectFieldContentProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
add,
|
||||||
choices,
|
choices,
|
||||||
displayCustomValue,
|
displayCustomValue,
|
||||||
displayValues,
|
displayValues,
|
||||||
|
@ -156,6 +173,10 @@ const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectField
|
||||||
onFetchMore
|
onFetchMore
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
if (!!add && !!displayCustomValue) {
|
||||||
|
throw new Error("Add and custom value cannot be displayed simultaneously");
|
||||||
|
}
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const anchor = React.useRef<HTMLDivElement>();
|
const anchor = React.useRef<HTMLDivElement>();
|
||||||
const scrollPosition = useElementScroll(anchor);
|
const scrollPosition = useElementScroll(anchor);
|
||||||
|
@ -183,6 +204,20 @@ const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectField
|
||||||
displayValues.length > 0 ||
|
displayValues.length > 0 ||
|
||||||
displayCustomValue ? (
|
displayCustomValue ? (
|
||||||
<>
|
<>
|
||||||
|
{add && (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
item: inputValue
|
||||||
|
})}
|
||||||
|
data-tc="multiautocomplete-select-option-add"
|
||||||
|
onClick={add.onClick}
|
||||||
|
>
|
||||||
|
<AddIcon color="primary" className={classes.addIcon} />
|
||||||
|
<Typography color="primary">{add.label}</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
{displayCustomValue && (
|
{displayCustomValue && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={classes.menuItem}
|
className={classes.menuItem}
|
||||||
|
@ -233,7 +268,8 @@ const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectField
|
||||||
const choiceIndex = getChoiceIndex(
|
const choiceIndex = getChoiceIndex(
|
||||||
index,
|
index,
|
||||||
displayValues,
|
displayValues,
|
||||||
displayCustomValue
|
displayCustomValue,
|
||||||
|
!!add
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -46,7 +46,7 @@ export function searchInCommands(
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(messages.createProduct),
|
label: intl.formatMessage(messages.createProduct),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(productAddUrl);
|
navigate(productAddUrl());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { staffListUrl } from "@saleor/staff/urls";
|
||||||
import { countryListUrl } from "@saleor/taxes/urls";
|
import { countryListUrl } from "@saleor/taxes/urls";
|
||||||
import { languageListUrl } from "@saleor/translations/urls";
|
import { languageListUrl } from "@saleor/translations/urls";
|
||||||
import { webhookListUrl } from "@saleor/webhooks/urls";
|
import { webhookListUrl } from "@saleor/webhooks/urls";
|
||||||
|
import { warehouseListUrl } from "@saleor/warehouses/urls";
|
||||||
import { QuickSearchActionInput } from "../../types";
|
import { QuickSearchActionInput } from "../../types";
|
||||||
|
|
||||||
interface View {
|
interface View {
|
||||||
|
@ -116,6 +117,10 @@ function searchInViews(
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(sectionNames.webhooks),
|
label: intl.formatMessage(sectionNames.webhooks),
|
||||||
url: webhookListUrl()
|
url: webhookListUrl()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage(sectionNames.warehouses),
|
||||||
|
url: warehouseListUrl()
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ interface PageHeaderProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
title?: string;
|
title?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageHeader: React.FC<PageHeaderProps> = props => {
|
const PageHeader: React.FC<PageHeaderProps> = props => {
|
||||||
|
|
|
@ -69,6 +69,7 @@ const Story: React.FC<Partial<
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentProps: SingleAutocompleteSelectFieldContentProps = {
|
const contentProps: SingleAutocompleteSelectFieldContentProps = {
|
||||||
|
add: undefined,
|
||||||
choices: suggestions.slice(0, 10),
|
choices: suggestions.slice(0, 10),
|
||||||
displayCustomValue: false,
|
displayCustomValue: false,
|
||||||
emptyOption: false,
|
emptyOption: false,
|
||||||
|
@ -88,6 +89,15 @@ storiesOf("Generics / Select with autocomplete", module)
|
||||||
.add("default", () => (
|
.add("default", () => (
|
||||||
<SingleAutocompleteSelectFieldContent {...contentProps} />
|
<SingleAutocompleteSelectFieldContent {...contentProps} />
|
||||||
))
|
))
|
||||||
|
.add("with add", () => (
|
||||||
|
<SingleAutocompleteSelectFieldContent
|
||||||
|
{...contentProps}
|
||||||
|
add={{
|
||||||
|
label: "Add New Collection",
|
||||||
|
onClick: () => undefined
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
.add("can load more", () => (
|
.add("can load more", () => (
|
||||||
<SingleAutocompleteSelectFieldContent {...contentProps} hasMore={true} />
|
<SingleAutocompleteSelectFieldContent {...contentProps} hasMore={true} />
|
||||||
))
|
))
|
||||||
|
|
|
@ -10,7 +10,8 @@ import { FetchMoreProps } from "@saleor/types";
|
||||||
import ArrowDropdownIcon from "../../icons/ArrowDropdown";
|
import ArrowDropdownIcon from "../../icons/ArrowDropdown";
|
||||||
import Debounce, { DebounceProps } from "../Debounce";
|
import Debounce, { DebounceProps } from "../Debounce";
|
||||||
import SingleAutocompleteSelectFieldContent, {
|
import SingleAutocompleteSelectFieldContent, {
|
||||||
SingleAutocompleteChoiceType
|
SingleAutocompleteChoiceType,
|
||||||
|
SingleAutocompleteActionType
|
||||||
} from "./SingleAutocompleteSelectFieldContent";
|
} from "./SingleAutocompleteSelectFieldContent";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -25,6 +26,7 @@ const useStyles = makeStyles(
|
||||||
|
|
||||||
export interface SingleAutocompleteSelectFieldProps
|
export interface SingleAutocompleteSelectFieldProps
|
||||||
extends Partial<FetchMoreProps> {
|
extends Partial<FetchMoreProps> {
|
||||||
|
add?: SingleAutocompleteActionType;
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
displayValue: string;
|
displayValue: string;
|
||||||
|
@ -47,6 +49,7 @@ const DebounceAutocomplete: React.ComponentType<DebounceProps<
|
||||||
|
|
||||||
const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectFieldProps> = props => {
|
const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectFieldProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
add,
|
||||||
allowCustomValues,
|
allowCustomValues,
|
||||||
choices,
|
choices,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -144,6 +147,15 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
|
||||||
/>
|
/>
|
||||||
{isOpen && (!!inputValue || !!choices.length) && (
|
{isOpen && (!!inputValue || !!choices.length) && (
|
||||||
<SingleAutocompleteSelectFieldContent
|
<SingleAutocompleteSelectFieldContent
|
||||||
|
add={
|
||||||
|
!!add && {
|
||||||
|
...add,
|
||||||
|
onClick: () => {
|
||||||
|
add.onClick();
|
||||||
|
closeMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
choices={choices}
|
choices={choices}
|
||||||
displayCustomValue={displayCustomValue}
|
displayCustomValue={displayCustomValue}
|
||||||
emptyOption={emptyOption}
|
emptyOption={emptyOption}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
|
import Add from "@material-ui/icons/Add";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
@ -24,8 +25,13 @@ export interface SingleAutocompleteChoiceType {
|
||||||
label: string;
|
label: string;
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
export interface SingleAutocompleteActionType {
|
||||||
|
label: string;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
export interface SingleAutocompleteSelectFieldContentProps
|
export interface SingleAutocompleteSelectFieldContentProps
|
||||||
extends Partial<FetchMoreProps> {
|
extends Partial<FetchMoreProps> {
|
||||||
|
add?: SingleAutocompleteActionType;
|
||||||
choices: SingleAutocompleteChoiceType[];
|
choices: SingleAutocompleteChoiceType[];
|
||||||
displayCustomValue: boolean;
|
displayCustomValue: boolean;
|
||||||
emptyOption: boolean;
|
emptyOption: boolean;
|
||||||
|
@ -38,6 +44,14 @@ export interface SingleAutocompleteSelectFieldContentProps
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
add: {
|
||||||
|
background: theme.palette.background.default,
|
||||||
|
border: `1px solid ${theme.palette.divider}`,
|
||||||
|
borderRadius: "100%",
|
||||||
|
height: 24,
|
||||||
|
marginRight: theme.spacing(),
|
||||||
|
width: 24
|
||||||
|
},
|
||||||
arrowContainer: {
|
arrowContainer: {
|
||||||
position: "relative"
|
position: "relative"
|
||||||
},
|
},
|
||||||
|
@ -96,23 +110,23 @@ const useStyles = makeStyles(
|
||||||
function getChoiceIndex(
|
function getChoiceIndex(
|
||||||
index: number,
|
index: number,
|
||||||
emptyValue: boolean,
|
emptyValue: boolean,
|
||||||
customValue: boolean
|
customValue: boolean,
|
||||||
|
add: boolean
|
||||||
) {
|
) {
|
||||||
let choiceIndex = index;
|
let choiceIndex = index;
|
||||||
if (emptyValue) {
|
if (emptyValue) {
|
||||||
choiceIndex += 1;
|
choiceIndex += 1;
|
||||||
}
|
}
|
||||||
if (customValue) {
|
if (customValue || add) {
|
||||||
choiceIndex += 2;
|
choiceIndex += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return choiceIndex;
|
return choiceIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SingleAutocompleteSelectFieldContent: React.FC<
|
const SingleAutocompleteSelectFieldContent: React.FC<SingleAutocompleteSelectFieldContentProps> = props => {
|
||||||
SingleAutocompleteSelectFieldContentProps
|
|
||||||
> = props => {
|
|
||||||
const {
|
const {
|
||||||
|
add,
|
||||||
choices,
|
choices,
|
||||||
displayCustomValue,
|
displayCustomValue,
|
||||||
emptyOption,
|
emptyOption,
|
||||||
|
@ -125,6 +139,10 @@ const SingleAutocompleteSelectFieldContent: React.FC<
|
||||||
onFetchMore
|
onFetchMore
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
if (!!add && !!displayCustomValue) {
|
||||||
|
throw new Error("Add and custom value cannot be displayed simultaneously");
|
||||||
|
}
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const anchor = React.useRef<HTMLDivElement>();
|
const anchor = React.useRef<HTMLDivElement>();
|
||||||
const scrollPosition = useElementScroll(anchor);
|
const scrollPosition = useElementScroll(anchor);
|
||||||
|
@ -164,6 +182,20 @@ const SingleAutocompleteSelectFieldContent: React.FC<
|
||||||
</Typography>
|
</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{add && (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
item: inputValue
|
||||||
|
})}
|
||||||
|
data-tc="singleautocomplete-select-option-add"
|
||||||
|
onClick={add.onClick}
|
||||||
|
>
|
||||||
|
<Add color="primary" className={classes.add} />
|
||||||
|
<Typography color="primary">{add.label}</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
{displayCustomValue && (
|
{displayCustomValue && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={classes.menuItem}
|
className={classes.menuItem}
|
||||||
|
@ -184,14 +216,15 @@ const SingleAutocompleteSelectFieldContent: React.FC<
|
||||||
/>
|
/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{choices.length > 0 && displayCustomValue && (
|
{choices.length > 0 && (!!add || displayCustomValue) && (
|
||||||
<Hr className={classes.hr} />
|
<Hr className={classes.hr} />
|
||||||
)}
|
)}
|
||||||
{choices.map((suggestion, index) => {
|
{choices.map((suggestion, index) => {
|
||||||
const choiceIndex = getChoiceIndex(
|
const choiceIndex = getChoiceIndex(
|
||||||
index,
|
index,
|
||||||
emptyOption,
|
emptyOption,
|
||||||
displayCustomValue
|
displayCustomValue,
|
||||||
|
!!add
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -34,6 +34,7 @@ export interface AppListViewSettings {
|
||||||
[ListViews.STAFF_MEMBERS_LIST]: ListSettings;
|
[ListViews.STAFF_MEMBERS_LIST]: ListSettings;
|
||||||
[ListViews.PERMISSION_GROUP_LIST]: ListSettings;
|
[ListViews.PERMISSION_GROUP_LIST]: ListSettings;
|
||||||
[ListViews.VOUCHER_LIST]: ListSettings;
|
[ListViews.VOUCHER_LIST]: ListSettings;
|
||||||
|
[ListViews.WAREHOUSE_LIST]: ListSettings;
|
||||||
[ListViews.WEBHOOK_LIST]: ListSettings;
|
[ListViews.WEBHOOK_LIST]: ListSettings;
|
||||||
}
|
}
|
||||||
export const defaultListSettings: AppListViewSettings = {
|
export const defaultListSettings: AppListViewSettings = {
|
||||||
|
@ -80,6 +81,9 @@ export const defaultListSettings: AppListViewSettings = {
|
||||||
[ListViews.VOUCHER_LIST]: {
|
[ListViews.VOUCHER_LIST]: {
|
||||||
rowNumber: PAGINATE_BY
|
rowNumber: PAGINATE_BY
|
||||||
},
|
},
|
||||||
|
[ListViews.WAREHOUSE_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
[ListViews.WEBHOOK_LIST]: {
|
[ListViews.WEBHOOK_LIST]: {
|
||||||
rowNumber: PAGINATE_BY
|
rowNumber: PAGINATE_BY
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import { permissionGroupListUrl } from "@saleor/permissionGroups/urls";
|
||||||
import { taxSection } from "@saleor/taxes/urls";
|
import { taxSection } from "@saleor/taxes/urls";
|
||||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
import { webhookListUrl } from "@saleor/webhooks/urls";
|
import { webhookListUrl } from "@saleor/webhooks/urls";
|
||||||
|
import Warehouses from "@saleor/icons/Warehouses";
|
||||||
|
import { warehouseSection } from "@saleor/warehouses/urls";
|
||||||
import ConfigurationPage, { MenuSection } from "./ConfigurationPage";
|
import ConfigurationPage, { MenuSection } from "./ConfigurationPage";
|
||||||
|
|
||||||
export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||||
|
@ -67,16 +69,6 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||||
defaultMessage: "Product Settings"
|
defaultMessage: "Product Settings"
|
||||||
}),
|
}),
|
||||||
menuItems: [
|
menuItems: [
|
||||||
{
|
|
||||||
description: intl.formatMessage({
|
|
||||||
defaultMessage: "Manage how you ship out orders",
|
|
||||||
id: "configurationMenuShipping"
|
|
||||||
}),
|
|
||||||
icon: <ShippingMethods fontSize="inherit" viewBox="0 0 44 44" />,
|
|
||||||
permission: PermissionEnum.MANAGE_SHIPPING,
|
|
||||||
title: intl.formatMessage(sectionNames.shipping),
|
|
||||||
url: shippingZonesListUrl()
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: intl.formatMessage({
|
description: intl.formatMessage({
|
||||||
defaultMessage: "Manage how your store charges tax",
|
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: <ShippingMethods fontSize="inherit" viewBox="0 0 44 44" />,
|
||||||
|
permission: PermissionEnum.MANAGE_SHIPPING,
|
||||||
|
title: intl.formatMessage(sectionNames.shipping),
|
||||||
|
url: shippingZonesListUrl()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: intl.formatMessage({
|
||||||
|
defaultMessage: "Manage and update your warehouse information",
|
||||||
|
id: "configurationMenuWarehouses"
|
||||||
|
}),
|
||||||
|
icon: <Warehouses fontSize="inherit" viewBox="0 0 44 44" />,
|
||||||
|
permission: PermissionEnum.MANAGE_PRODUCTS,
|
||||||
|
title: intl.formatMessage(sectionNames.warehouses),
|
||||||
|
url: warehouseSection
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: intl.formatMessage({
|
||||||
defaultMessage: "Miscellaneous"
|
defaultMessage: "Miscellaneous"
|
||||||
|
|
|
@ -485,3 +485,23 @@ export const adminUserPermissions: User_userPermissions[] = [
|
||||||
name: "Manage customers."
|
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"
|
||||||
|
};
|
||||||
|
|
|
@ -18,8 +18,8 @@ export interface UseFormsetOutput<TData = object, TValue = any> {
|
||||||
set: (data: FormsetData<TData, TValue>) => void;
|
set: (data: FormsetData<TData, TValue>) => void;
|
||||||
}
|
}
|
||||||
function useFormset<TData = object, TValue = any>(
|
function useFormset<TData = object, TValue = any>(
|
||||||
initial: FormsetData<TData>
|
initial: FormsetData<TData, TValue>
|
||||||
): UseFormsetOutput<TData> {
|
): UseFormsetOutput<TData, TValue> {
|
||||||
const [data, setData] = useStateFromProps<FormsetData<TData, TValue>>(
|
const [data, setData] = useStateFromProps<FormsetData<TData, TValue>>(
|
||||||
initial || []
|
initial || []
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
import { AppListViewSettings, defaultListSettings } from "./../config";
|
import { AppListViewSettings, defaultListSettings } from "./../config";
|
||||||
import { ListSettings, ListViews } from "./../types";
|
import { ListSettings, ListViews } from "./../types";
|
||||||
|
@ -14,6 +15,15 @@ export default function useListSettings<TColumns extends string = string>(
|
||||||
defaultListSettings
|
defaultListSettings
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (settings[listName] === undefined) {
|
||||||
|
setListSettings(settings => ({
|
||||||
|
...settings,
|
||||||
|
[listName]: defaultListSettings[listName]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const updateListSettings = (key: keyof ListSettings, value: any) =>
|
const updateListSettings = (key: keyof ListSettings, value: any) =>
|
||||||
setListSettings(settings => ({
|
setListSettings(settings => ({
|
||||||
...settings,
|
...settings,
|
||||||
|
|
61
src/hooks/useWizard.ts
Normal file
61
src/hooks/useWizard.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export interface UseWizardActions<T> {
|
||||||
|
next: () => void;
|
||||||
|
prev: () => void;
|
||||||
|
set: (step: T) => void;
|
||||||
|
}
|
||||||
|
export interface UseWizardOpts<T> {
|
||||||
|
onTransition: (prevStep: T, nextStep: T) => void;
|
||||||
|
}
|
||||||
|
export type UseWizard<T> = [T, UseWizardActions<T>];
|
||||||
|
function useWizard<T>(
|
||||||
|
initial: T,
|
||||||
|
steps: T[],
|
||||||
|
opts?: UseWizardOpts<T>
|
||||||
|
): UseWizard<T> {
|
||||||
|
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;
|
16
src/icons/Warehouses.tsx
Normal file
16
src/icons/Warehouses.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Warehouses = createSvgIcon(
|
||||||
|
<>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M5.23438 15.2344H34.7656V38.4375H38.2812V12.3121L19.9219 1.80048L1.5625 12.3121V38.4375H5.23438V15.2344ZM6.79688 40H0V11.4062L19.9219 0L39.8438 11.4062V40H33.2031V16.7969H6.79688V40ZM23.8281 9.84375H16.1719V11.7188H23.8281V9.84375ZM14.6094 8.28125V13.2812H25.3906V8.28125H14.6094ZM15.8594 32.3438H21.6406V38.4375H15.8594V32.3438ZM21.6406 30.7812V23.125H30.5469V30.7812V32.3438V40H23.2031H21.6406H14.2969V30.7812H21.6406ZM28.9844 32.3438H23.2031V38.4375H28.9844V32.3438ZM28.9844 30.7812V24.6875H23.2031V30.7812H28.9844Z"
|
||||||
|
fill="#06847B"
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
"Warehouses"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Warehouses;
|
|
@ -55,6 +55,8 @@ import TaxesSection from "./taxes";
|
||||||
import TranslationsSection from "./translations";
|
import TranslationsSection from "./translations";
|
||||||
import { PermissionEnum } from "./types/globalTypes";
|
import { PermissionEnum } from "./types/globalTypes";
|
||||||
import WebhooksSection from "./webhooks";
|
import WebhooksSection from "./webhooks";
|
||||||
|
import { warehouseSection } from "./warehouses/urls";
|
||||||
|
import WarehouseSection from "./warehouses";
|
||||||
|
|
||||||
interface ResponseError extends ErrorResponse {
|
interface ResponseError extends ErrorResponse {
|
||||||
networkError?: Error & {
|
networkError?: Error & {
|
||||||
|
@ -263,6 +265,11 @@ const Routes: React.FC = () => {
|
||||||
path={serviceSection}
|
path={serviceSection}
|
||||||
component={ServiceSection}
|
component={ServiceSection}
|
||||||
/>
|
/>
|
||||||
|
<SectionRoute
|
||||||
|
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||||
|
path={warehouseSection}
|
||||||
|
component={WarehouseSection}
|
||||||
|
/>
|
||||||
{createConfigurationMenu(intl).filter(menu =>
|
{createConfigurationMenu(intl).filter(menu =>
|
||||||
menu.menuItems.map(item =>
|
menu.menuItems.map(item =>
|
||||||
hasPermission(item.permission, user)
|
hasPermission(item.permission, user)
|
||||||
|
|
12
src/intl.ts
12
src/intl.ts
|
@ -81,6 +81,10 @@ export const commonMessages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const buttonMessages = defineMessages({
|
export const buttonMessages = defineMessages({
|
||||||
|
accept: {
|
||||||
|
defaultMessage: "Accept",
|
||||||
|
description: "button"
|
||||||
|
},
|
||||||
back: {
|
back: {
|
||||||
defaultMessage: "Back",
|
defaultMessage: "Back",
|
||||||
description: "button"
|
description: "button"
|
||||||
|
@ -117,6 +121,10 @@ export const buttonMessages = defineMessages({
|
||||||
defaultMessage: "Manage",
|
defaultMessage: "Manage",
|
||||||
description: "button"
|
description: "button"
|
||||||
},
|
},
|
||||||
|
ok: {
|
||||||
|
defaultMessage: "OK",
|
||||||
|
description: "button"
|
||||||
|
},
|
||||||
remove: {
|
remove: {
|
||||||
defaultMessage: "Remove",
|
defaultMessage: "Remove",
|
||||||
description: "button"
|
description: "button"
|
||||||
|
@ -224,6 +232,10 @@ export const sectionNames = defineMessages({
|
||||||
defaultMessage: "Vouchers",
|
defaultMessage: "Vouchers",
|
||||||
description: "vouchers section name"
|
description: "vouchers section name"
|
||||||
},
|
},
|
||||||
|
warehouses: {
|
||||||
|
defaultMessage: "Warehouses",
|
||||||
|
description: "warehouses section name"
|
||||||
|
},
|
||||||
webhooks: {
|
webhooks: {
|
||||||
defaultMessage: "Webhooks",
|
defaultMessage: "Webhooks",
|
||||||
description: "webhooks section name"
|
description: "webhooks section name"
|
||||||
|
|
|
@ -4,14 +4,13 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
|
||||||
|
|
||||||
export interface OrderBulkCancelDialogProps {
|
export interface OrderBulkCancelDialogProps {
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
numberOfOrders: string;
|
numberOfOrders: string;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onConfirm: (restock: boolean) => void;
|
onConfirm: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
|
const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
|
||||||
|
@ -22,7 +21,6 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
|
||||||
onConfirm
|
onConfirm
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [restock, setRestock] = React.useState(true);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
|
@ -34,7 +32,7 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
|
||||||
description: "dialog header"
|
description: "dialog header"
|
||||||
})}
|
})}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onConfirm={() => onConfirm(restock)}
|
onConfirm={onConfirm}
|
||||||
>
|
>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -45,15 +43,6 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<ControlledCheckbox
|
|
||||||
checked={restock}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Release all stock allocated to these orders",
|
|
||||||
description: "switch button"
|
|
||||||
})}
|
|
||||||
name="restock"
|
|
||||||
onChange={event => setRestock(event.target.value)}
|
|
||||||
/>
|
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,45 +4,25 @@ import DialogActions from "@material-ui/core/DialogActions";
|
||||||
import DialogContent from "@material-ui/core/DialogContent";
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
|
||||||
import Form from "@saleor/components/Form";
|
|
||||||
import { buttonMessages } from "@saleor/intl";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
|
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import getOrderErrorMessage from "@saleor/utils/errors/order";
|
import getOrderErrorMessage from "@saleor/utils/errors/order";
|
||||||
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
|
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 {
|
export interface OrderCancelDialogProps {
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
errors: OrderErrorFragment[];
|
errors: OrderErrorFragment[];
|
||||||
number: string;
|
number: string;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose?();
|
onClose: () => void;
|
||||||
onSubmit(data: FormData);
|
onSubmit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
|
const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
|
||||||
|
@ -55,20 +35,11 @@ const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
|
||||||
onClose
|
onClose
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const errors = useModalDialogErrors(apiErrors, open);
|
const errors = useModalDialogErrors(apiErrors, open);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog onClose={onClose} open={open}>
|
<Dialog onClose={onClose} open={open} maxWidth="sm">
|
||||||
<Form
|
|
||||||
initial={{
|
|
||||||
restock: true
|
|
||||||
}}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
>
|
|
||||||
{({ data, change }) => (
|
|
||||||
<>
|
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Cancel Order"
|
defaultMessage="Cancel Order"
|
||||||
|
@ -79,20 +50,12 @@ const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Are you sure you want to cancel order #{orderNumber}?"
|
defaultMessage="Cancelling this order will release unfulfilled stocks, so they can be bought by other customers. <b>Order will not be refunded when cancelling order - You need to do it manually.</b> Are you sure you want to cancel this order?"
|
||||||
values={{
|
values={{
|
||||||
|
b: (...chunks) => <b>{chunks}</b>,
|
||||||
orderNumber
|
orderNumber
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ControlledCheckbox
|
|
||||||
checked={data.restock}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Release all stock allocated to this order",
|
|
||||||
description: "switch button"
|
|
||||||
})}
|
|
||||||
name="restock"
|
|
||||||
onChange={change}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
{errors.length > 0 && (
|
{errors.length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
@ -110,21 +73,14 @@ const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
|
||||||
<FormattedMessage {...buttonMessages.back} />
|
<FormattedMessage {...buttonMessages.back} />
|
||||||
</Button>
|
</Button>
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
|
onClick={onSubmit}
|
||||||
transitionState={confirmButtonState}
|
transitionState={confirmButtonState}
|
||||||
className={classes.deleteButton}
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage {...buttonMessages.accept} />
|
||||||
defaultMessage="Cancel Order"
|
|
||||||
description="button"
|
|
||||||
id="OrderCancelDialogButton"
|
|
||||||
/>
|
|
||||||
</ConfirmButton>
|
</ConfirmButton>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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<DialogProps> = ({
|
||||||
|
open,
|
||||||
|
onClose
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog onClose={onClose} open={open} maxWidth="sm">
|
||||||
|
<DialogTitle>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Saleor couldn’t cancel order"
|
||||||
|
description="dialog header"
|
||||||
|
/>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage defaultMessage="There are still fulfillments created for this order. Cancel the fulfillments first before you cancel the order." />
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
className={classes.button}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<FormattedMessage {...buttonMessages.ok} />
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
OrderCannotCancelOrderDialog.displayName = "OrderCannotCancelOrderDialog";
|
||||||
|
export default OrderCannotCancelOrderDialog;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderCannotCancelOrderDialog";
|
||||||
|
export * from "./OrderCannotCancelOrderDialog";
|
|
@ -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", () => <OrderFulfillPage {...props} />)
|
||||||
|
.add("loading", () => (
|
||||||
|
<OrderFulfillPage
|
||||||
|
{...props}
|
||||||
|
disabled={true}
|
||||||
|
order={undefined}
|
||||||
|
warehouses={undefined}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
.add("error", () => (
|
||||||
|
<OrderFulfillPage
|
||||||
|
{...props}
|
||||||
|
errors={[
|
||||||
|
{
|
||||||
|
__typename: "OrderError",
|
||||||
|
code: OrderErrorCode.INSUFFICIENT_STOCK,
|
||||||
|
field: null,
|
||||||
|
orderLine: orderToFulfill.lines[0].id,
|
||||||
|
warehouse: warehouseList[0].id
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
));
|
413
src/orders/components/OrderFulfillPage/OrderFulfillPage.tsx
Normal file
413
src/orders/components/OrderFulfillPage/OrderFulfillPage.tsx
Normal file
|
@ -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<null, OrderFulfillStockInput[]>;
|
||||||
|
}
|
||||||
|
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<OrderFulfillPageProps> = ({
|
||||||
|
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 (
|
||||||
|
<Container>
|
||||||
|
<AppHeader onBack={onBack}>
|
||||||
|
{order?.number
|
||||||
|
? intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "Order #{orderNumber}",
|
||||||
|
description: "page header with order number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
orderNumber: order.number
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Order",
|
||||||
|
description: "page header"
|
||||||
|
})}
|
||||||
|
</AppHeader>
|
||||||
|
<PageHeader
|
||||||
|
title={intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "Order no. {orderNumber} - Add Fulfillment",
|
||||||
|
description: "page header"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
orderNumber: order?.number
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Form initial={initialFormData} onSubmit={handleSubmit}>
|
||||||
|
{({ change, data, submit }) => (
|
||||||
|
<>
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Items ready to ship",
|
||||||
|
description: "header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<ResponsiveTable className={classes.table}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell className={classes.colName}>
|
||||||
|
<FormattedMessage defaultMessage="Product name" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colSku}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="SKU"
|
||||||
|
description="product's sku"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
{warehouses?.map(warehouse => (
|
||||||
|
<TableCell
|
||||||
|
key={warehouse.id}
|
||||||
|
className={classNames(
|
||||||
|
classes.colQuantity,
|
||||||
|
classes.colQuantityHeader
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{warehouse.name}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
<TableCell className={classes.colQuantityTotal}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Quantity to fulfill"
|
||||||
|
description="quantity of fulfilled products"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{renderCollection(
|
||||||
|
order?.lines.filter(line => getRemainingQuantity(line) > 0),
|
||||||
|
(line, lineIndex) => {
|
||||||
|
if (!line) {
|
||||||
|
return (
|
||||||
|
<TableRow>
|
||||||
|
<TableCellAvatar className={classes.colName}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCellAvatar>
|
||||||
|
<TableCell className={classes.colSku}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
{warehouses?.map(warehouse => (
|
||||||
|
<TableCell
|
||||||
|
className={classes.colQuantity}
|
||||||
|
key={warehouse.id}
|
||||||
|
>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
<TableCell className={classes.colQuantityTotal}>
|
||||||
|
{" "}
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remainingQuantity = getRemainingQuantity(line);
|
||||||
|
const quantityToFulfill = formsetData[
|
||||||
|
lineIndex
|
||||||
|
].value.reduce(
|
||||||
|
(quantityToFulfill, lineInput) =>
|
||||||
|
quantityToFulfill + (lineInput.quantity || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const overfulfill = remainingQuantity < quantityToFulfill;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={line.id}>
|
||||||
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
|
thumbnail={line?.thumbnail?.url}
|
||||||
|
>
|
||||||
|
{line.productName}
|
||||||
|
<Typography color="textSecondary" variant="caption">
|
||||||
|
{line.variant.attributes
|
||||||
|
.map(attribute =>
|
||||||
|
attribute.values
|
||||||
|
.map(attributeValue => attributeValue.name)
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
.join(" / ")}
|
||||||
|
</Typography>
|
||||||
|
</TableCellAvatar>
|
||||||
|
<TableCell className={classes.colSku}>
|
||||||
|
{line.variant.sku}
|
||||||
|
</TableCell>
|
||||||
|
{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 (
|
||||||
|
<TableCell
|
||||||
|
className={classNames(
|
||||||
|
classes.colQuantity,
|
||||||
|
classes.error
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="No Stock"
|
||||||
|
description="no variant stock in warehouse"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const availableQuantity =
|
||||||
|
warehouseStock.quantity -
|
||||||
|
warehouseStock.quantityAllocated;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
<div className={classes.colQuantityContent}>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
inputProps={{
|
||||||
|
className: classes.quantityInnerInput,
|
||||||
|
max: warehouseStock.quantity,
|
||||||
|
min: 0,
|
||||||
|
style: { textAlign: "right" }
|
||||||
|
}}
|
||||||
|
className={classes.quantityInput}
|
||||||
|
value={formsetStock.quantity}
|
||||||
|
onChange={event =>
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className={classes.remainingQuantity}>
|
||||||
|
/ {availableQuantity}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<TableCell className={classes.colQuantityTotal}>
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
[classes.error]: overfulfill,
|
||||||
|
[classes.full]:
|
||||||
|
remainingQuantity <= quantityToFulfill
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{quantityToFulfill}
|
||||||
|
</span>{" "}
|
||||||
|
/ {remainingQuantity}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</ResponsiveTable>
|
||||||
|
<CardActions className={classes.actionBar}>
|
||||||
|
<ControlledCheckbox
|
||||||
|
checked={data.sendInfo}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Send shipment details to customer",
|
||||||
|
description: "checkbox"
|
||||||
|
})}
|
||||||
|
name="sendInfo"
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
<SaveButtonBar
|
||||||
|
disabled={disabled}
|
||||||
|
labels={{
|
||||||
|
save: intl.formatMessage({
|
||||||
|
defaultMessage: "Fulfill",
|
||||||
|
description: "fulfill order, button"
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
state={saveButtonBar}
|
||||||
|
onSave={submit}
|
||||||
|
onCancel={onBack}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OrderFulfillPage.displayName = "OrderFulfillPage";
|
||||||
|
export default OrderFulfillPage;
|
172
src/orders/components/OrderFulfillPage/fixtures.ts
Normal file
172
src/orders/components/OrderFulfillPage/fixtures.ts
Normal file
|
@ -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"
|
||||||
|
};
|
2
src/orders/components/OrderFulfillPage/index.ts
Normal file
2
src/orders/components/OrderFulfillPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./OrderFulfillPage";
|
||||||
|
export { default } from "./OrderFulfillPage";
|
|
@ -9,6 +9,7 @@ import TableRow from "@material-ui/core/TableRow";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
import CardMenu from "@saleor/components/CardMenu";
|
import CardMenu from "@saleor/components/CardMenu";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
|
@ -19,7 +20,7 @@ import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableCellAvatar, {
|
import TableCellAvatar, {
|
||||||
AVATAR_MARGIN
|
AVATAR_MARGIN
|
||||||
} from "@saleor/components/TableCellAvatar";
|
} from "@saleor/components/TableCellAvatar";
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection, getStringOrPlaceholder } from "../../../misc";
|
||||||
import { FulfillmentStatus } from "../../../types/globalTypes";
|
import { FulfillmentStatus } from "../../../types/globalTypes";
|
||||||
import { OrderDetails_order_fulfillments } from "../../types/OrderDetails";
|
import { OrderDetails_order_fulfillments } from "../../types/OrderDetails";
|
||||||
|
|
||||||
|
@ -46,7 +47,15 @@ const useStyles = makeStyles(
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
|
infoLabel: {
|
||||||
|
display: "inline-block"
|
||||||
|
},
|
||||||
|
infoLabelWithMargin: {
|
||||||
|
marginBottom: theme.spacing()
|
||||||
|
},
|
||||||
|
infoRow: {
|
||||||
|
padding: theme.spacing(2, 3)
|
||||||
|
},
|
||||||
orderNumber: {
|
orderNumber: {
|
||||||
display: "inline",
|
display: "inline",
|
||||||
marginLeft: theme.spacing(1)
|
marginLeft: theme.spacing(1)
|
||||||
|
@ -68,7 +77,7 @@ interface OrderFulfillmentProps {
|
||||||
onTrackingCodeAdd: () => void;
|
onTrackingCodeAdd: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberOfColumns = 3;
|
const numberOfColumns = 4;
|
||||||
|
|
||||||
const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
|
const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
@ -107,8 +116,8 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
|
||||||
)
|
)
|
||||||
: intl.formatMessage(
|
: intl.formatMessage(
|
||||||
{
|
{
|
||||||
defaultMessage: "Fulfilled ({quantity})",
|
defaultMessage: "Cancelled ({quantity})",
|
||||||
description: "section header"
|
description: "cancelled fulfillment, section header"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
quantity
|
quantity
|
||||||
|
@ -135,7 +144,7 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
|
||||||
menuItems={[
|
menuItems={[
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: intl.formatMessage({
|
||||||
defaultMessage: "Cancel shipment",
|
defaultMessage: "Cancel Fulfillment",
|
||||||
description: "button"
|
description: "button"
|
||||||
}),
|
}),
|
||||||
onSelect: onOrderFulfillmentCancel
|
onSelect: onOrderFulfillmentCancel
|
||||||
|
@ -216,18 +225,47 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
{maybe(() => fulfillment.trackingNumber) && (
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={numberOfColumns}>
|
<TableCell className={classes.infoRow} colSpan={numberOfColumns}>
|
||||||
|
<Typography color="textSecondary" variant="body2">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Fulfilled from: {warehouseName}"
|
||||||
|
description="fulfillment group"
|
||||||
|
values={{
|
||||||
|
warehouseName: (
|
||||||
|
<Typography
|
||||||
|
className={classNames(classes.infoLabel, {
|
||||||
|
[classes.infoLabelWithMargin]: !!fulfillment?.trackingNumber
|
||||||
|
})}
|
||||||
|
color="textPrimary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{getStringOrPlaceholder(fulfillment?.warehouse?.name)}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<Typography color="textSecondary" variant="body2">
|
||||||
|
{fulfillment?.trackingNumber && (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Tracking Number: {trackingNumber}"
|
defaultMessage="Tracking Number: {trackingNumber}"
|
||||||
values={{
|
values={{
|
||||||
trackingNumber: fulfillment.trackingNumber
|
trackingNumber: (
|
||||||
|
<Typography
|
||||||
|
className={classes.infoLabel}
|
||||||
|
color="textPrimary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{fulfillment.trackingNumber}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
{status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && (
|
{status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && (
|
||||||
|
|
|
@ -11,25 +11,29 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
|
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import { buttonMessages } from "@saleor/intl";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
|
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import getOrderErrorMessage from "@saleor/utils/errors/order";
|
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 {
|
export interface OrderFulfillmentCancelDialogFormData {
|
||||||
restock: boolean;
|
warehouseId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
deleteButton: {
|
enableOverflow: {
|
||||||
"&:hover": {
|
overflow: "visible"
|
||||||
backgroundColor: theme.palette.error.main
|
|
||||||
},
|
},
|
||||||
backgroundColor: theme.palette.error.main,
|
paragraph: {
|
||||||
color: theme.palette.error.contrastText
|
marginBottom: theme.spacing(2)
|
||||||
|
},
|
||||||
|
selectCcontainer: {
|
||||||
|
width: "60%"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ name: "OrderFulfillmentCancelDialog" }
|
{ name: "OrderFulfillmentCancelDialog" }
|
||||||
|
@ -39,20 +43,48 @@ export interface OrderFulfillmentCancelDialogProps {
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
errors: OrderErrorFragment[];
|
errors: OrderErrorFragment[];
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
warehouses: WarehouseFragment[];
|
||||||
onClose();
|
onClose();
|
||||||
onConfirm(data: FormData);
|
onConfirm(data: OrderFulfillmentCancelDialogFormData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderFulfillmentCancelDialog: React.FC<OrderFulfillmentCancelDialogProps> = props => {
|
const OrderFulfillmentCancelDialog: React.FC<OrderFulfillmentCancelDialogProps> = props => {
|
||||||
const { confirmButtonState, errors, open, onConfirm, onClose } = props;
|
const {
|
||||||
|
confirmButtonState,
|
||||||
|
errors,
|
||||||
|
open,
|
||||||
|
warehouses,
|
||||||
|
onConfirm,
|
||||||
|
onClose
|
||||||
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const [displayValue, setDisplayValue] = React.useState("");
|
||||||
|
|
||||||
|
const choices = warehouses?.map(warehouse => ({
|
||||||
|
label: warehouse.name,
|
||||||
|
value: warehouse.id
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog onClose={onClose} open={open} fullWidth maxWidth="xs">
|
<Dialog
|
||||||
<Form initial={{ restock: true }} onSubmit={onConfirm}>
|
classes={{
|
||||||
{({ change, data, submit }) => (
|
paper: classes.enableOverflow
|
||||||
|
}}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
>
|
||||||
|
<Form initial={{ warehouseId: null }} onSubmit={onConfirm}>
|
||||||
|
{({ change, data: formData, submit }) => {
|
||||||
|
const handleChange = createSingleAutocompleteSelectHandler(
|
||||||
|
change,
|
||||||
|
setDisplayValue,
|
||||||
|
choices
|
||||||
|
);
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -60,19 +92,23 @@ const OrderFulfillmentCancelDialog: React.FC<OrderFulfillmentCancelDialogProps>
|
||||||
description="dialog header"
|
description="dialog header"
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent className={classes.enableOverflow}>
|
||||||
<DialogContentText>
|
<DialogContentText className={classes.paragraph}>
|
||||||
<FormattedMessage defaultMessage="Are you sure you want to cancel this fulfillment?" />
|
<FormattedMessage defaultMessage="Are you sure you want to cancel fulfillment? Canceling a fulfillment will restock products at a selected warehouse." />
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<ControlledCheckbox
|
<div className={classes.selectCcontainer}>
|
||||||
checked={data.restock}
|
<SingleAutocompleteSelectField
|
||||||
|
choices={choices}
|
||||||
|
displayValue={displayValue}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
defaultMessage: "Restock items?",
|
defaultMessage: "Select Warehouse",
|
||||||
description: "switch button"
|
description: "select warehouse to restock items"
|
||||||
})}
|
})}
|
||||||
name="restock"
|
name="warehouseId"
|
||||||
onChange={change}
|
value={formData.warehouseId}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{errors.length > 0 && (
|
{errors.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<FormSpacer />
|
<FormSpacer />
|
||||||
|
@ -89,19 +125,17 @@ const OrderFulfillmentCancelDialog: React.FC<OrderFulfillmentCancelDialogProps>
|
||||||
<FormattedMessage {...buttonMessages.back} />
|
<FormattedMessage {...buttonMessages.back} />
|
||||||
</Button>
|
</Button>
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
|
disabled={formData.warehouseId === null}
|
||||||
transitionState={confirmButtonState}
|
transitionState={confirmButtonState}
|
||||||
className={classes.deleteButton}
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage {...buttonMessages.accept} />
|
||||||
defaultMessage="Cancel fulfillment"
|
|
||||||
description="button"
|
|
||||||
/>
|
|
||||||
</ConfirmButton>
|
</ConfirmButton>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</>
|
</>
|
||||||
)}
|
);
|
||||||
|
}}
|
||||||
</Form>
|
</Form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -54,7 +54,7 @@ const useStyles = makeStyles(
|
||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
quantityInput: {
|
quantityInput: {
|
||||||
width: "4rem"
|
width: 100
|
||||||
},
|
},
|
||||||
remainingQuantity: {
|
remainingQuantity: {
|
||||||
marginLeft: theme.spacing(),
|
marginLeft: theme.spacing(),
|
||||||
|
|
|
@ -3,16 +3,15 @@ import TableBody from "@material-ui/core/TableBody";
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableFooter from "@material-ui/core/TableFooter";
|
import TableFooter from "@material-ui/core/TableFooter";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
|
import TableHead from "@material-ui/core/TableHead";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
|
||||||
import { DateTime } from "@saleor/components/Date";
|
import { DateTime } from "@saleor/components/Date";
|
||||||
import Money from "@saleor/components/Money";
|
import Money from "@saleor/components/Money";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import StatusLabel from "@saleor/components/StatusLabel";
|
import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import {
|
import {
|
||||||
maybe,
|
maybe,
|
||||||
|
@ -20,7 +19,7 @@ import {
|
||||||
transformOrderStatus,
|
transformOrderStatus,
|
||||||
transformPaymentStatus
|
transformPaymentStatus
|
||||||
} from "@saleor/misc";
|
} from "@saleor/misc";
|
||||||
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
import { ListProps, SortPage } from "@saleor/types";
|
||||||
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
import TableCellHeader from "@saleor/components/TableCellHeader";
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
import { getArrowDirection } from "@saleor/utils/sort";
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
|
@ -59,14 +58,11 @@ const useStyles = makeStyles(
|
||||||
{ name: "OrderList" }
|
{ name: "OrderList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface OrderListProps
|
interface OrderListProps extends ListProps, SortPage<OrderListUrlSortField> {
|
||||||
extends ListProps,
|
|
||||||
ListActions,
|
|
||||||
SortPage<OrderListUrlSortField> {
|
|
||||||
orders: OrderList_orders_edges_node[];
|
orders: OrderList_orders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberOfColumns = 7;
|
const numberOfColumns = 6;
|
||||||
|
|
||||||
export const OrderList: React.FC<OrderListProps> = props => {
|
export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
@ -79,12 +75,7 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onSort,
|
onSort,
|
||||||
isChecked,
|
sort
|
||||||
selected,
|
|
||||||
sort,
|
|
||||||
toggle,
|
|
||||||
toggleAll,
|
|
||||||
toolbar
|
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
@ -99,14 +90,7 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
: undefined;
|
: undefined;
|
||||||
return (
|
return (
|
||||||
<ResponsiveTable>
|
<ResponsiveTable>
|
||||||
<TableHead
|
<TableHead>
|
||||||
colSpan={numberOfColumns}
|
|
||||||
selected={selected}
|
|
||||||
disabled={disabled}
|
|
||||||
items={orders}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={toolbar}
|
|
||||||
>
|
|
||||||
<TableCellHeader
|
<TableCellHeader
|
||||||
direction={
|
direction={
|
||||||
sort.sort === OrderListUrlSortField.number
|
sort.sort === OrderListUrlSortField.number
|
||||||
|
@ -206,31 +190,15 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(
|
{renderCollection(
|
||||||
orderList,
|
orderList,
|
||||||
order => {
|
order => (
|
||||||
const isSelected = order ? isChecked(order.id) : false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow
|
<TableRow
|
||||||
hover={!!order}
|
hover={!!order}
|
||||||
className={!!order ? classes.link : undefined}
|
className={!!order ? classes.link : undefined}
|
||||||
onClick={order ? onRowClick(order.id) : undefined}
|
onClick={order ? onRowClick(order.id) : undefined}
|
||||||
key={order ? order.id : "skeleton"}
|
key={order ? order.id : "skeleton"}
|
||||||
selected={isSelected}
|
|
||||||
>
|
>
|
||||||
<TableCell padding="checkbox">
|
|
||||||
<Checkbox
|
|
||||||
checked={isSelected}
|
|
||||||
disabled={disabled}
|
|
||||||
disableClickPropagation
|
|
||||||
onChange={() => toggle(order.id)}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colNumber}>
|
<TableCell className={classes.colNumber}>
|
||||||
{maybe(() => order.number) ? (
|
{maybe(() => order.number) ? "#" + order.number : <Skeleton />}
|
||||||
"#" + order.number
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colDate}>
|
<TableCell className={classes.colDate}>
|
||||||
{maybe(() => order.created) ? (
|
{maybe(() => order.created) ? (
|
||||||
|
@ -282,8 +250,7 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
),
|
||||||
},
|
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={numberOfColumns}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
|
|
@ -7,12 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import { FilterPageProps, PageListProps, SortPage } from "@saleor/types";
|
||||||
FilterPageProps,
|
|
||||||
ListActions,
|
|
||||||
PageListProps,
|
|
||||||
SortPage
|
|
||||||
} from "@saleor/types";
|
|
||||||
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
||||||
|
@ -25,7 +20,6 @@ import {
|
||||||
|
|
||||||
export interface OrderListPageProps
|
export interface OrderListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
|
||||||
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
||||||
SortPage<OrderListUrlSortField> {
|
SortPage<OrderListUrlSortField> {
|
||||||
orders: OrderList_orders_edges_node[];
|
orders: OrderList_orders_edges_node[];
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
TypedOrderAddNoteMutation,
|
TypedOrderAddNoteMutation,
|
||||||
TypedOrderCancelMutation,
|
TypedOrderCancelMutation,
|
||||||
TypedOrderCaptureMutation,
|
TypedOrderCaptureMutation,
|
||||||
TypedOrderCreateFulfillmentMutation,
|
|
||||||
TypedOrderDraftCancelMutation,
|
TypedOrderDraftCancelMutation,
|
||||||
TypedOrderDraftFinalizeMutation,
|
TypedOrderDraftFinalizeMutation,
|
||||||
TypedOrderDraftUpdateMutation,
|
TypedOrderDraftUpdateMutation,
|
||||||
|
@ -24,10 +23,6 @@ import {
|
||||||
import { OrderAddNote, OrderAddNoteVariables } from "../types/OrderAddNote";
|
import { OrderAddNote, OrderAddNoteVariables } from "../types/OrderAddNote";
|
||||||
import { OrderCancel, OrderCancelVariables } from "../types/OrderCancel";
|
import { OrderCancel, OrderCancelVariables } from "../types/OrderCancel";
|
||||||
import { OrderCapture, OrderCaptureVariables } from "../types/OrderCapture";
|
import { OrderCapture, OrderCaptureVariables } from "../types/OrderCapture";
|
||||||
import {
|
|
||||||
OrderCreateFulfillment,
|
|
||||||
OrderCreateFulfillmentVariables
|
|
||||||
} from "../types/OrderCreateFulfillment";
|
|
||||||
import {
|
import {
|
||||||
OrderDraftCancel,
|
OrderDraftCancel,
|
||||||
OrderDraftCancelVariables
|
OrderDraftCancelVariables
|
||||||
|
@ -80,10 +75,6 @@ interface OrderOperationsProps {
|
||||||
OrderCancel,
|
OrderCancel,
|
||||||
OrderCancelVariables
|
OrderCancelVariables
|
||||||
>;
|
>;
|
||||||
orderCreateFulfillment: PartialMutationProviderOutput<
|
|
||||||
OrderCreateFulfillment,
|
|
||||||
OrderCreateFulfillmentVariables
|
|
||||||
>;
|
|
||||||
orderFulfillmentCancel: PartialMutationProviderOutput<
|
orderFulfillmentCancel: PartialMutationProviderOutput<
|
||||||
OrderFulfillmentCancel,
|
OrderFulfillmentCancel,
|
||||||
OrderFulfillmentCancelVariables
|
OrderFulfillmentCancelVariables
|
||||||
|
@ -139,7 +130,6 @@ interface OrderOperationsProps {
|
||||||
>;
|
>;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
onOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
|
onOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
|
||||||
onOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void;
|
|
||||||
onOrderFulfillmentUpdate: (data: OrderFulfillmentUpdateTracking) => void;
|
onOrderFulfillmentUpdate: (data: OrderFulfillmentUpdateTracking) => void;
|
||||||
onOrderCancel: (data: OrderCancel) => void;
|
onOrderCancel: (data: OrderCancel) => void;
|
||||||
onOrderVoid: (data: OrderVoid) => void;
|
onOrderVoid: (data: OrderVoid) => void;
|
||||||
|
@ -160,7 +150,6 @@ interface OrderOperationsProps {
|
||||||
const OrderOperations: React.FC<OrderOperationsProps> = ({
|
const OrderOperations: React.FC<OrderOperationsProps> = ({
|
||||||
children,
|
children,
|
||||||
onDraftUpdate,
|
onDraftUpdate,
|
||||||
onOrderFulfillmentCreate,
|
|
||||||
onNoteAdd,
|
onNoteAdd,
|
||||||
onOrderCancel,
|
onOrderCancel,
|
||||||
onOrderLinesAdd,
|
onOrderLinesAdd,
|
||||||
|
@ -185,10 +174,6 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
|
||||||
{(...paymentCapture) => (
|
{(...paymentCapture) => (
|
||||||
<TypedOrderRefundMutation onCompleted={onPaymentRefund}>
|
<TypedOrderRefundMutation onCompleted={onPaymentRefund}>
|
||||||
{(...paymentRefund) => (
|
{(...paymentRefund) => (
|
||||||
<TypedOrderCreateFulfillmentMutation
|
|
||||||
onCompleted={onOrderFulfillmentCreate}
|
|
||||||
>
|
|
||||||
{(...createFulfillment) => (
|
|
||||||
<TypedOrderAddNoteMutation onCompleted={onNoteAdd}>
|
<TypedOrderAddNoteMutation onCompleted={onNoteAdd}>
|
||||||
{(...addNote) => (
|
{(...addNote) => (
|
||||||
<TypedOrderUpdateMutation onCompleted={onUpdate}>
|
<TypedOrderUpdateMutation onCompleted={onUpdate}>
|
||||||
|
@ -232,9 +217,7 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
|
||||||
onDraftFinalize
|
onDraftFinalize
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(
|
{(...finalizeDraft) => (
|
||||||
...finalizeDraft
|
|
||||||
) => (
|
|
||||||
<TypedOrderDraftCancelMutation
|
<TypedOrderDraftCancelMutation
|
||||||
onCompleted={
|
onCompleted={
|
||||||
onDraftCancel
|
onDraftCancel
|
||||||
|
@ -258,9 +241,6 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
|
||||||
orderCancel: getMutationProviderData(
|
orderCancel: getMutationProviderData(
|
||||||
...orderCancel
|
...orderCancel
|
||||||
),
|
),
|
||||||
orderCreateFulfillment: getMutationProviderData(
|
|
||||||
...createFulfillment
|
|
||||||
),
|
|
||||||
orderDraftCancel: getMutationProviderData(
|
orderDraftCancel: getMutationProviderData(
|
||||||
...cancelDraft
|
...cancelDraft
|
||||||
),
|
),
|
||||||
|
@ -329,8 +309,6 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
|
||||||
)}
|
)}
|
||||||
</TypedOrderAddNoteMutation>
|
</TypedOrderAddNoteMutation>
|
||||||
)}
|
)}
|
||||||
</TypedOrderCreateFulfillmentMutation>
|
|
||||||
)}
|
|
||||||
</TypedOrderRefundMutation>
|
</TypedOrderRefundMutation>
|
||||||
)}
|
)}
|
||||||
</TypedOrderCaptureMutation>
|
</TypedOrderCaptureMutation>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
||||||
import { MessageDescriptor } from "react-intl";
|
import { MessageDescriptor } from "react-intl";
|
||||||
|
import { warehouseList } from "@saleor/warehouses/fixtures";
|
||||||
import { transformOrderStatus, transformPaymentStatus } from "../misc";
|
import { transformOrderStatus, transformPaymentStatus } from "../misc";
|
||||||
import {
|
import {
|
||||||
FulfillmentStatus,
|
FulfillmentStatus,
|
||||||
|
@ -865,7 +866,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
status: FulfillmentStatus.FULFILLED,
|
status: FulfillmentStatus.FULFILLED,
|
||||||
trackingNumber: ""
|
trackingNumber: "",
|
||||||
|
warehouse: warehouseList[1]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: "Fulfillment",
|
__typename: "Fulfillment",
|
||||||
|
@ -905,7 +907,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
status: FulfillmentStatus.FULFILLED,
|
status: FulfillmentStatus.FULFILLED,
|
||||||
trackingNumber: ""
|
trackingNumber: "01nn12399su12nndfsy",
|
||||||
|
warehouse: warehouseList[0]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
id: "T3JkZXI6OQ==",
|
id: "T3JkZXI6OQ==",
|
||||||
|
|
|
@ -14,9 +14,11 @@ import {
|
||||||
orderPath,
|
orderPath,
|
||||||
OrderUrlQueryParams,
|
OrderUrlQueryParams,
|
||||||
OrderDraftListUrlSortField,
|
OrderDraftListUrlSortField,
|
||||||
OrderListUrlSortField
|
OrderListUrlSortField,
|
||||||
|
orderFulfillPath
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import OrderDetailsComponent from "./views/OrderDetails";
|
import OrderDetailsComponent from "./views/OrderDetails";
|
||||||
|
import OrderFulfillComponent from "./views/OrderFulfill";
|
||||||
import OrderDraftListComponent from "./views/OrderDraftList";
|
import OrderDraftListComponent from "./views/OrderDraftList";
|
||||||
import OrderListComponent from "./views/OrderList";
|
import OrderListComponent from "./views/OrderList";
|
||||||
|
|
||||||
|
@ -57,6 +59,10 @@ const OrderDetails: React.FC<RouteComponentProps<any>> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OrderFulfill: React.FC<RouteComponentProps<any>> = ({ match }) => (
|
||||||
|
<OrderFulfillComponent orderId={decodeURIComponent(match.params.id)} />
|
||||||
|
);
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -66,6 +72,7 @@ const Component = () => {
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={orderDraftListPath} component={OrderDraftList} />
|
<Route exact path={orderDraftListPath} component={OrderDraftList} />
|
||||||
<Route exact path={orderListPath} component={OrderList} />
|
<Route exact path={orderListPath} component={OrderList} />
|
||||||
|
<Route path={orderFulfillPath(":id")} component={OrderFulfill} />
|
||||||
<Route path={orderPath(":id")} component={OrderDetails} />
|
<Route path={orderPath(":id")} component={OrderDetails} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -8,20 +8,13 @@ import {
|
||||||
fragmentOrderEvent
|
fragmentOrderEvent
|
||||||
} from "./queries";
|
} from "./queries";
|
||||||
import { OrderAddNote, OrderAddNoteVariables } from "./types/OrderAddNote";
|
import { OrderAddNote, OrderAddNoteVariables } from "./types/OrderAddNote";
|
||||||
import {
|
|
||||||
OrderBulkCancel,
|
|
||||||
OrderBulkCancelVariables
|
|
||||||
} from "./types/OrderBulkCancel";
|
|
||||||
import { OrderCancel, OrderCancelVariables } from "./types/OrderCancel";
|
import { OrderCancel, OrderCancelVariables } from "./types/OrderCancel";
|
||||||
import { OrderCapture, OrderCaptureVariables } from "./types/OrderCapture";
|
import { OrderCapture, OrderCaptureVariables } from "./types/OrderCapture";
|
||||||
import {
|
|
||||||
OrderCreateFulfillment,
|
|
||||||
OrderCreateFulfillmentVariables
|
|
||||||
} from "./types/OrderCreateFulfillment";
|
|
||||||
import {
|
import {
|
||||||
OrderDraftBulkCancel,
|
OrderDraftBulkCancel,
|
||||||
OrderDraftBulkCancelVariables
|
OrderDraftBulkCancelVariables
|
||||||
} from "./types/OrderDraftBulkCancel";
|
} from "./types/OrderDraftBulkCancel";
|
||||||
|
import { FulfillOrder, FulfillOrderVariables } from "./types/FulfillOrder";
|
||||||
import {
|
import {
|
||||||
OrderDraftCancel,
|
OrderDraftCancel,
|
||||||
OrderDraftCancelVariables
|
OrderDraftCancelVariables
|
||||||
|
@ -74,8 +67,8 @@ export const orderErrorFragment = gql`
|
||||||
const orderCancelMutation = gql`
|
const orderCancelMutation = gql`
|
||||||
${fragmentOrderDetails}
|
${fragmentOrderDetails}
|
||||||
${orderErrorFragment}
|
${orderErrorFragment}
|
||||||
mutation OrderCancel($id: ID!, $restock: Boolean!) {
|
mutation OrderCancel($id: ID!) {
|
||||||
orderCancel(id: $id, restock: $restock) {
|
orderCancel(id: $id) {
|
||||||
errors: orderErrors {
|
errors: orderErrors {
|
||||||
...OrderErrorFragment
|
...OrderErrorFragment
|
||||||
}
|
}
|
||||||
|
@ -90,21 +83,6 @@ export const TypedOrderCancelMutation = TypedMutation<
|
||||||
OrderCancelVariables
|
OrderCancelVariables
|
||||||
>(orderCancelMutation);
|
>(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`
|
const orderDraftCancelMutation = gql`
|
||||||
${fragmentOrderDetails}
|
${fragmentOrderDetails}
|
||||||
${orderErrorFragment}
|
${orderErrorFragment}
|
||||||
|
@ -234,28 +212,6 @@ export const TypedOrderCaptureMutation = TypedMutation<
|
||||||
OrderCaptureVariables
|
OrderCaptureVariables
|
||||||
>(orderCaptureMutation);
|
>(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`
|
const orderFulfillmentUpdateTrackingMutation = gql`
|
||||||
${fragmentOrderDetails}
|
${fragmentOrderDetails}
|
||||||
${orderErrorFragment}
|
${orderErrorFragment}
|
||||||
|
@ -477,3 +433,24 @@ export const TypedOrderLineUpdateMutation = TypedMutation<
|
||||||
OrderLineUpdate,
|
OrderLineUpdate,
|
||||||
OrderLineUpdateVariables
|
OrderLineUpdateVariables
|
||||||
>(orderLineUpdateMutation);
|
>(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);
|
||||||
|
|
|
@ -13,6 +13,10 @@ import {
|
||||||
SearchOrderVariant as SearchOrderVariantType,
|
SearchOrderVariant as SearchOrderVariantType,
|
||||||
SearchOrderVariantVariables
|
SearchOrderVariantVariables
|
||||||
} from "./types/SearchOrderVariant";
|
} from "./types/SearchOrderVariant";
|
||||||
|
import {
|
||||||
|
OrderFulfillData,
|
||||||
|
OrderFulfillDataVariables
|
||||||
|
} from "./types/OrderFulfillData";
|
||||||
|
|
||||||
export const fragmentOrderEvent = gql`
|
export const fragmentOrderEvent = gql`
|
||||||
fragment OrderEventFragment on OrderEvent {
|
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`
|
export const fragmentOrderDetails = gql`
|
||||||
${fragmentAddress}
|
${fragmentAddress}
|
||||||
${fragmentOrderEvent}
|
${fragmentOrderEvent}
|
||||||
${fragmentOrderLine}
|
${fragmentOrderLine}
|
||||||
|
${fulfillmentFragment}
|
||||||
fragment OrderDetailsFragment on Order {
|
fragment OrderDetailsFragment on Order {
|
||||||
id
|
id
|
||||||
billingAddress {
|
billingAddress {
|
||||||
|
@ -90,17 +115,7 @@ export const fragmentOrderDetails = gql`
|
||||||
...OrderEventFragment
|
...OrderEventFragment
|
||||||
}
|
}
|
||||||
fulfillments {
|
fulfillments {
|
||||||
id
|
...FulfillmentFragment
|
||||||
lines {
|
|
||||||
id
|
|
||||||
quantity
|
|
||||||
orderLine {
|
|
||||||
...OrderLineFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fulfillmentOrder
|
|
||||||
status
|
|
||||||
trackingNumber
|
|
||||||
}
|
}
|
||||||
lines {
|
lines {
|
||||||
...OrderLineFragment
|
...OrderLineFragment
|
||||||
|
@ -327,3 +342,45 @@ export const useOrderVariantSearch = makeTopLevelSearch<
|
||||||
SearchOrderVariantType,
|
SearchOrderVariantType,
|
||||||
SearchOrderVariantVariables
|
SearchOrderVariantVariables
|
||||||
>(searchOrderVariant);
|
>(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);
|
||||||
|
|
291
src/orders/types/FulfillOrder.ts
Normal file
291
src/orders/types/FulfillOrder.ts
Normal file
|
@ -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;
|
||||||
|
}
|
67
src/orders/types/FulfillmentFragment.ts
Normal file
67
src/orders/types/FulfillmentFragment.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -97,6 +97,12 @@ export interface OrderCancel_orderCancel_order_fulfillments_lines {
|
||||||
orderLine: OrderCancel_orderCancel_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderCancel_orderCancel_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderCancel_orderCancel_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderCancel_orderCancel_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderCancel_orderCancel_order_lines_unitPrice_gross {
|
export interface OrderCancel_orderCancel_order_lines_unitPrice_gross {
|
||||||
|
@ -278,5 +285,4 @@ export interface OrderCancel {
|
||||||
|
|
||||||
export interface OrderCancelVariables {
|
export interface OrderCancelVariables {
|
||||||
id: string;
|
id: string;
|
||||||
restock: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderCapture_orderCapture_order_fulfillments_lines {
|
||||||
orderLine: OrderCapture_orderCapture_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderCapture_orderCapture_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderCapture_orderCapture_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderCapture_orderCapture_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderCapture_orderCapture_order_lines_unitPrice_gross {
|
export interface OrderCapture_orderCapture_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -91,6 +91,12 @@ export interface OrderDetails_order_fulfillments_lines {
|
||||||
orderLine: OrderDetails_order_fulfillments_lines_orderLine | null;
|
orderLine: OrderDetails_order_fulfillments_lines_orderLine | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OrderDetails_order_fulfillments_warehouse {
|
||||||
|
__typename: "Warehouse";
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OrderDetails_order_fulfillments {
|
export interface OrderDetails_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -98,6 +104,7 @@ export interface OrderDetails_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderDetails_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDetails_order_lines_unitPrice_gross {
|
export interface OrderDetails_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -91,6 +91,12 @@ export interface OrderDetailsFragment_fulfillments_lines {
|
||||||
orderLine: OrderDetailsFragment_fulfillments_lines_orderLine | null;
|
orderLine: OrderDetailsFragment_fulfillments_lines_orderLine | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OrderDetailsFragment_fulfillments_warehouse {
|
||||||
|
__typename: "Warehouse";
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OrderDetailsFragment_fulfillments {
|
export interface OrderDetailsFragment_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -98,6 +104,7 @@ export interface OrderDetailsFragment_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderDetailsFragment_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDetailsFragment_lines_unitPrice_gross {
|
export interface OrderDetailsFragment_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines {
|
||||||
orderLine: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDraftCancel_draftOrderDelete_order_lines_unitPrice_gross {
|
export interface OrderDraftCancel_draftOrderDelete_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines
|
||||||
orderLine: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice_gross {
|
export interface OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines {
|
||||||
orderLine: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice_gross {
|
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice_gross {
|
||||||
|
|
71
src/orders/types/OrderFulfillData.ts
Normal file
71
src/orders/types/OrderFulfillData.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -97,6 +97,12 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment
|
||||||
orderLine: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice_gross {
|
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
|
||||||
orderLine: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice_gross {
|
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines {
|
||||||
orderLine: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderLineDelete_draftOrderLineDelete_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice_gross {
|
export interface OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines {
|
||||||
orderLine: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice_gross {
|
export interface OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines {
|
||||||
orderLine: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice_gross {
|
export interface OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines {
|
||||||
orderLine: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice_gross {
|
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderRefund_orderRefund_order_fulfillments_lines {
|
||||||
orderLine: OrderRefund_orderRefund_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderRefund_orderRefund_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderRefund_orderRefund_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderRefund_orderRefund_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderRefund_orderRefund_order_lines_unitPrice_gross {
|
export interface OrderRefund_orderRefund_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,6 +97,12 @@ export interface OrderVoid_orderVoid_order_fulfillments_lines {
|
||||||
orderLine: OrderVoid_orderVoid_order_fulfillments_lines_orderLine | null;
|
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 {
|
export interface OrderVoid_orderVoid_order_fulfillments {
|
||||||
__typename: "Fulfillment";
|
__typename: "Fulfillment";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,6 +110,7 @@ export interface OrderVoid_orderVoid_order_fulfillments {
|
||||||
fulfillmentOrder: number;
|
fulfillmentOrder: number;
|
||||||
status: FulfillmentStatus;
|
status: FulfillmentStatus;
|
||||||
trackingNumber: string;
|
trackingNumber: string;
|
||||||
|
warehouse: OrderVoid_orderVoid_order_fulfillments_warehouse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderVoid_orderVoid_order_lines_unitPrice_gross {
|
export interface OrderVoid_orderVoid_order_lines_unitPrice_gross {
|
||||||
|
|
|
@ -97,10 +97,14 @@ export type OrderUrlDialog =
|
||||||
| "edit-shipping"
|
| "edit-shipping"
|
||||||
| "edit-shipping-address"
|
| "edit-shipping-address"
|
||||||
| "finalize"
|
| "finalize"
|
||||||
| "fulfill"
|
|
||||||
| "mark-paid"
|
| "mark-paid"
|
||||||
| "refund"
|
| "refund"
|
||||||
| "void";
|
| "void";
|
||||||
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
||||||
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
||||||
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
||||||
|
export const orderFulfillPath = (id: string) =>
|
||||||
|
urlJoin(orderPath(id), "fulfill");
|
||||||
|
export const orderFulfillUrl = (id: string) =>
|
||||||
|
orderFulfillPath(encodeURIComponent(id));
|
||||||
|
|
|
@ -7,7 +7,6 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl
|
||||||
import { OrderAddNote } from "../../types/OrderAddNote";
|
import { OrderAddNote } from "../../types/OrderAddNote";
|
||||||
import { OrderCancel } from "../../types/OrderCancel";
|
import { OrderCancel } from "../../types/OrderCancel";
|
||||||
import { OrderCapture } from "../../types/OrderCapture";
|
import { OrderCapture } from "../../types/OrderCapture";
|
||||||
import { OrderCreateFulfillment } from "../../types/OrderCreateFulfillment";
|
|
||||||
import { OrderDraftCancel } from "../../types/OrderDraftCancel";
|
import { OrderDraftCancel } from "../../types/OrderDraftCancel";
|
||||||
import { OrderDraftFinalize } from "../../types/OrderDraftFinalize";
|
import { OrderDraftFinalize } from "../../types/OrderDraftFinalize";
|
||||||
import { OrderDraftUpdate } from "../../types/OrderDraftUpdate";
|
import { OrderDraftUpdate } from "../../types/OrderDraftUpdate";
|
||||||
|
@ -31,7 +30,6 @@ interface OrderDetailsMessages {
|
||||||
handleNoteAdd: (data: OrderAddNote) => void;
|
handleNoteAdd: (data: OrderAddNote) => void;
|
||||||
handleOrderCancel: (data: OrderCancel) => void;
|
handleOrderCancel: (data: OrderCancel) => void;
|
||||||
handleOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
|
handleOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
|
||||||
handleOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void;
|
|
||||||
handleOrderFulfillmentUpdate: (
|
handleOrderFulfillmentUpdate: (
|
||||||
data: OrderFulfillmentUpdateTracking
|
data: OrderFulfillmentUpdateTracking
|
||||||
) => void;
|
) => void;
|
||||||
|
@ -86,17 +84,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
|
||||||
closeModal();
|
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 handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => {
|
||||||
const errs = data.orderMarkAsPaid?.errors;
|
const errs = data.orderMarkAsPaid?.errors;
|
||||||
if (errs.length === 0) {
|
if (errs.length === 0) {
|
||||||
|
@ -256,7 +243,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
|
||||||
handleNoteAdd,
|
handleNoteAdd,
|
||||||
handleOrderCancel,
|
handleOrderCancel,
|
||||||
handleOrderFulfillmentCancel,
|
handleOrderFulfillmentCancel,
|
||||||
handleOrderFulfillmentCreate,
|
|
||||||
handleOrderFulfillmentUpdate,
|
handleOrderFulfillmentUpdate,
|
||||||
handleOrderLineDelete,
|
handleOrderLineDelete,
|
||||||
handleOrderLineUpdate,
|
handleOrderLineUpdate,
|
||||||
|
|
|
@ -8,6 +8,8 @@ import useUser from "@saleor/hooks/useUser";
|
||||||
import useCustomerSearch from "@saleor/searches/useCustomerSearch";
|
import useCustomerSearch from "@saleor/searches/useCustomerSearch";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
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 { customerUrl } from "../../../customers/urls";
|
||||||
import {
|
import {
|
||||||
maybe,
|
maybe,
|
||||||
|
@ -15,7 +17,7 @@ import {
|
||||||
getStringOrPlaceholder
|
getStringOrPlaceholder
|
||||||
} from "../../../misc";
|
} from "../../../misc";
|
||||||
import { productUrl } from "../../../products/urls";
|
import { productUrl } from "../../../products/urls";
|
||||||
import { OrderStatus } from "../../../types/globalTypes";
|
import { OrderStatus, FulfillmentStatus } from "../../../types/globalTypes";
|
||||||
import OrderAddressEditDialog from "../../components/OrderAddressEditDialog";
|
import OrderAddressEditDialog from "../../components/OrderAddressEditDialog";
|
||||||
import OrderCancelDialog from "../../components/OrderCancelDialog";
|
import OrderCancelDialog from "../../components/OrderCancelDialog";
|
||||||
import OrderDetailsPage from "../../components/OrderDetailsPage";
|
import OrderDetailsPage from "../../components/OrderDetailsPage";
|
||||||
|
@ -25,7 +27,6 @@ import OrderDraftFinalizeDialog, {
|
||||||
} from "../../components/OrderDraftFinalizeDialog";
|
} from "../../components/OrderDraftFinalizeDialog";
|
||||||
import OrderDraftPage from "../../components/OrderDraftPage";
|
import OrderDraftPage from "../../components/OrderDraftPage";
|
||||||
import OrderFulfillmentCancelDialog from "../../components/OrderFulfillmentCancelDialog";
|
import OrderFulfillmentCancelDialog from "../../components/OrderFulfillmentCancelDialog";
|
||||||
import OrderFulfillmentDialog from "../../components/OrderFulfillmentDialog";
|
|
||||||
import OrderFulfillmentTrackingDialog from "../../components/OrderFulfillmentTrackingDialog";
|
import OrderFulfillmentTrackingDialog from "../../components/OrderFulfillmentTrackingDialog";
|
||||||
import OrderMarkAsPaidDialog from "../../components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog";
|
import OrderMarkAsPaidDialog from "../../components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog";
|
||||||
import OrderPaymentDialog from "../../components/OrderPaymentDialog";
|
import OrderPaymentDialog from "../../components/OrderPaymentDialog";
|
||||||
|
@ -39,7 +40,8 @@ import {
|
||||||
orderListUrl,
|
orderListUrl,
|
||||||
orderUrl,
|
orderUrl,
|
||||||
OrderUrlQueryParams,
|
OrderUrlQueryParams,
|
||||||
OrderUrlDialog
|
OrderUrlDialog,
|
||||||
|
orderFulfillUrl
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
import { OrderDetailsMessages } from "./OrderDetailsMessages";
|
import { OrderDetailsMessages } from "./OrderDetailsMessages";
|
||||||
|
|
||||||
|
@ -95,6 +97,12 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
} = useOrderVariantSearch({
|
} = useOrderVariantSearch({
|
||||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||||
});
|
});
|
||||||
|
const warehouses = useWarehouseList({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: {
|
||||||
|
first: 30
|
||||||
|
}
|
||||||
|
});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
|
@ -118,9 +126,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
{orderMessages => (
|
{orderMessages => (
|
||||||
<OrderOperations
|
<OrderOperations
|
||||||
order={id}
|
order={id}
|
||||||
onOrderFulfillmentCreate={
|
|
||||||
orderMessages.handleOrderFulfillmentCreate
|
|
||||||
}
|
|
||||||
onNoteAdd={orderMessages.handleNoteAdd}
|
onNoteAdd={orderMessages.handleNoteAdd}
|
||||||
onOrderCancel={orderMessages.handleOrderCancel}
|
onOrderCancel={orderMessages.handleOrderCancel}
|
||||||
onOrderVoid={orderMessages.handleOrderVoid}
|
onOrderVoid={orderMessages.handleOrderVoid}
|
||||||
|
@ -147,7 +152,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
{({
|
{({
|
||||||
orderAddNote,
|
orderAddNote,
|
||||||
orderCancel,
|
orderCancel,
|
||||||
orderCreateFulfillment,
|
|
||||||
orderDraftUpdate,
|
orderDraftUpdate,
|
||||||
orderLinesAdd,
|
orderLinesAdd,
|
||||||
orderLineDelete,
|
orderLineDelete,
|
||||||
|
@ -194,7 +198,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
)}
|
)}
|
||||||
userPermissions={user?.userPermissions || []}
|
userPermissions={user?.userPermissions || []}
|
||||||
onOrderCancel={() => openModal("cancel")}
|
onOrderCancel={() => openModal("cancel")}
|
||||||
onOrderFulfill={() => openModal("fulfill")}
|
onOrderFulfill={() => navigate(orderFulfillUrl(id))}
|
||||||
onFulfillmentCancel={fulfillmentId =>
|
onFulfillmentCancel={fulfillmentId =>
|
||||||
navigate(
|
navigate(
|
||||||
orderUrl(id, {
|
orderUrl(id, {
|
||||||
|
@ -226,6 +230,17 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
navigate(customerUrl(order.user.id))
|
navigate(customerUrl(order.user.id))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<OrderCannotCancelOrderDialog
|
||||||
|
onClose={closeModal}
|
||||||
|
open={
|
||||||
|
params.action === "cancel" &&
|
||||||
|
order?.fulfillments.some(
|
||||||
|
fulfillment =>
|
||||||
|
fulfillment.status ===
|
||||||
|
FulfillmentStatus.FULFILLED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<OrderCancelDialog
|
<OrderCancelDialog
|
||||||
confirmButtonState={orderCancel.opts.status}
|
confirmButtonState={orderCancel.opts.status}
|
||||||
errors={
|
errors={
|
||||||
|
@ -234,10 +249,9 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
number={order?.number}
|
number={order?.number}
|
||||||
open={params.action === "cancel"}
|
open={params.action === "cancel"}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onSubmit={variables =>
|
onSubmit={() =>
|
||||||
orderCancel.mutate({
|
orderCancel.mutate({
|
||||||
id,
|
id
|
||||||
...variables
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -298,38 +312,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<OrderFulfillmentDialog
|
|
||||||
confirmButtonState={
|
|
||||||
orderCreateFulfillment.opts.status
|
|
||||||
}
|
|
||||||
errors={
|
|
||||||
orderCreateFulfillment.opts.data
|
|
||||||
?.orderFulfillmentCreate.errors || []
|
|
||||||
}
|
|
||||||
open={params.action === "fulfill"}
|
|
||||||
lines={maybe(() => 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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<OrderFulfillmentCancelDialog
|
<OrderFulfillmentCancelDialog
|
||||||
confirmButtonState={
|
confirmButtonState={
|
||||||
orderFulfillmentCancel.opts.status
|
orderFulfillmentCancel.opts.status
|
||||||
|
@ -339,6 +321,11 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
?.orderFulfillmentCancel.errors || []
|
?.orderFulfillmentCancel.errors || []
|
||||||
}
|
}
|
||||||
open={params.action === "cancel-fulfillment"}
|
open={params.action === "cancel-fulfillment"}
|
||||||
|
warehouses={
|
||||||
|
warehouses.data?.warehouses.edges.map(
|
||||||
|
edge => edge.node
|
||||||
|
) || []
|
||||||
|
}
|
||||||
onConfirm={variables =>
|
onConfirm={variables =>
|
||||||
orderFulfillmentCancel.mutate({
|
orderFulfillmentCancel.mutate({
|
||||||
id: params.id,
|
id: params.id,
|
||||||
|
|
94
src/orders/views/OrderFulfill/OrderFulfill.tsx
Normal file
94
src/orders/views/OrderFulfill/OrderFulfill.tsx
Normal file
|
@ -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<OrderFulfillProps> = ({ 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 (
|
||||||
|
<>
|
||||||
|
<WindowTitle
|
||||||
|
title={
|
||||||
|
data?.order?.number
|
||||||
|
? intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "Fulfill Order #{orderNumber}",
|
||||||
|
description: "window title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
orderNumber: data.order.number
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Fulfill Order",
|
||||||
|
description: "window title"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<OrderFulfillPage
|
||||||
|
disabled={loading || warehousesLoading || fulfillOrderOpts.loading}
|
||||||
|
errors={fulfillOrderOpts.data?.orderFulfill.errors}
|
||||||
|
onBack={() => 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;
|
2
src/orders/views/OrderFulfill/index.ts
Normal file
2
src/orders/views/OrderFulfill/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./OrderFulfill";
|
||||||
|
export { default } from "./OrderFulfill";
|
|
@ -1,12 +1,10 @@
|
||||||
import Button from "@material-ui/core/Button";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -14,20 +12,15 @@ import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe, getStringOrPlaceholder } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import { getSortParams } from "@saleor/utils/sort";
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
||||||
import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog";
|
|
||||||
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
||||||
import {
|
import { useOrderDraftCreateMutation } from "../../mutations";
|
||||||
TypedOrderBulkCancelMutation,
|
|
||||||
useOrderDraftCreateMutation
|
|
||||||
} from "../../mutations";
|
|
||||||
import { useOrderListQuery } from "../../queries";
|
import { useOrderListQuery } from "../../queries";
|
||||||
import { OrderBulkCancel } from "../../types/OrderBulkCancel";
|
|
||||||
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
||||||
import {
|
import {
|
||||||
orderListUrl,
|
orderListUrl,
|
||||||
|
@ -56,9 +49,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const paginate = usePaginator();
|
const paginate = usePaginator();
|
||||||
const shop = useShop();
|
const shop = useShop();
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
|
||||||
params.ids
|
|
||||||
);
|
|
||||||
const { updateListSettings, settings } = useListSettings(
|
const { updateListSettings, settings } = useListSettings(
|
||||||
ListViews.ORDER_LIST
|
ListViews.ORDER_LIST
|
||||||
);
|
);
|
||||||
|
@ -91,7 +81,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
resetFilters,
|
resetFilters,
|
||||||
handleSearchChange
|
handleSearchChange
|
||||||
] = createFilterHandlers({
|
] = createFilterHandlers({
|
||||||
cleanupFn: reset,
|
|
||||||
createUrl: orderListUrl,
|
createUrl: orderListUrl,
|
||||||
getFilterQueryParam,
|
getFilterQueryParam,
|
||||||
navigate,
|
navigate,
|
||||||
|
@ -103,19 +92,16 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
OrderListUrlQueryParams
|
OrderListUrlQueryParams
|
||||||
>(navigate, orderListUrl, params);
|
>(navigate, orderListUrl, params);
|
||||||
|
|
||||||
const handleTabChange = (tab: number) => {
|
const handleTabChange = (tab: number) =>
|
||||||
reset();
|
|
||||||
navigate(
|
navigate(
|
||||||
orderListUrl({
|
orderListUrl({
|
||||||
activeTab: tab.toString(),
|
activeTab: tab.toString(),
|
||||||
...getFilterTabs()[tab - 1].data
|
...getFilterTabs()[tab - 1].data
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const handleFilterTabDelete = () => {
|
const handleFilterTabDelete = () => {
|
||||||
deleteFilterTab(currentTab);
|
deleteFilterTab(currentTab);
|
||||||
reset();
|
|
||||||
navigate(orderListUrl());
|
navigate(orderListUrl());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +121,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
}),
|
}),
|
||||||
[params, settings.rowNumber]
|
[params, settings.rowNumber]
|
||||||
);
|
);
|
||||||
const { data, loading, refetch } = useOrderListQuery({
|
const { data, loading } = useOrderListQuery({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: queryVariables
|
variables: queryVariables
|
||||||
});
|
});
|
||||||
|
@ -146,32 +132,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
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);
|
const handleSort = createSortHandler(navigate, orderListUrl, params);
|
||||||
|
|
||||||
return (
|
|
||||||
<TypedOrderBulkCancelMutation onCompleted={handleOrderBulkCancel}>
|
|
||||||
{(orderBulkCancel, orderBulkCancelOpts) => {
|
|
||||||
const onOrderBulkCancel = (restock: boolean) =>
|
|
||||||
orderBulkCancel({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids,
|
|
||||||
restock
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<OrderListPage
|
<OrderListPage
|
||||||
|
@ -189,25 +151,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
onUpdateListSettings={updateListSettings}
|
onUpdateListSettings={updateListSettings}
|
||||||
onRowClick={id => () => navigate(orderUrl(id))}
|
onRowClick={id => () => navigate(orderUrl(id))}
|
||||||
onSort={handleSort}
|
onSort={handleSort}
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() =>
|
|
||||||
openModal("cancel", {
|
|
||||||
ids: listElements
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Cancel"
|
|
||||||
description="cancel orders, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
onSearchChange={handleSearchChange}
|
onSearchChange={handleSearchChange}
|
||||||
onFilterChange={changeFilters}
|
onFilterChange={changeFilters}
|
||||||
onTabSave={() => openModal("save-search")}
|
onTabSave={() => openModal("save-search")}
|
||||||
|
@ -217,13 +160,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
tabs={getFilterTabs().map(tab => tab.name)}
|
tabs={getFilterTabs().map(tab => tab.name)}
|
||||||
onAll={resetFilters}
|
onAll={resetFilters}
|
||||||
/>
|
/>
|
||||||
<OrderBulkCancelDialog
|
|
||||||
confirmButtonState={orderBulkCancelOpts.status}
|
|
||||||
numberOfOrders={maybe(() => params.ids.length.toString(), "...")}
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={onOrderBulkCancel}
|
|
||||||
open={params.action === "cancel"}
|
|
||||||
/>
|
|
||||||
<SaveFilterTabDialog
|
<SaveFilterTabDialog
|
||||||
open={params.action === "save-search"}
|
open={params.action === "save-search"}
|
||||||
confirmButtonState="default"
|
confirmButtonState="default"
|
||||||
|
@ -235,13 +171,10 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
confirmButtonState="default"
|
confirmButtonState="default"
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onSubmit={handleFilterTabDelete}
|
onSubmit={handleFilterTabDelete}
|
||||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
|
||||||
</TypedOrderBulkCancelMutation>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderList;
|
export default OrderList;
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/
|
||||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
||||||
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
import { FetchMoreProps } from "../../../types";
|
import { FetchMoreProps } from "../../../types";
|
||||||
import {
|
import {
|
||||||
createAttributeChangeHandler,
|
createAttributeChangeHandler,
|
||||||
|
@ -41,7 +42,7 @@ import ProductAttributes, {
|
||||||
import ProductDetailsForm from "../ProductDetailsForm";
|
import ProductDetailsForm from "../ProductDetailsForm";
|
||||||
import ProductOrganization from "../ProductOrganization";
|
import ProductOrganization from "../ProductOrganization";
|
||||||
import ProductPricing from "../ProductPricing";
|
import ProductPricing from "../ProductPricing";
|
||||||
import ProductStock from "../ProductStock";
|
import ProductStocks, { ProductStockInput } from "../ProductStocks";
|
||||||
|
|
||||||
interface FormData {
|
interface FormData {
|
||||||
basePrice: number;
|
basePrice: number;
|
||||||
|
@ -57,9 +58,11 @@ interface FormData {
|
||||||
seoTitle: string;
|
seoTitle: string;
|
||||||
sku: string;
|
sku: string;
|
||||||
stockQuantity: number;
|
stockQuantity: number;
|
||||||
|
trackInventory: boolean;
|
||||||
}
|
}
|
||||||
export interface ProductCreatePageSubmitData extends FormData {
|
export interface ProductCreatePageSubmitData extends FormData {
|
||||||
attributes: ProductAttributeInput[];
|
attributes: ProductAttributeInput[];
|
||||||
|
stocks: ProductStockInput[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProductCreatePageProps {
|
interface ProductCreatePageProps {
|
||||||
|
@ -79,9 +82,11 @@ interface ProductCreatePageProps {
|
||||||
}>;
|
}>;
|
||||||
header: string;
|
header: string;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
|
warehouses: SearchWarehouses_search_edges_node[];
|
||||||
fetchCategories: (data: string) => void;
|
fetchCategories: (data: string) => void;
|
||||||
fetchCollections: (data: string) => void;
|
fetchCollections: (data: string) => void;
|
||||||
fetchProductTypes: (data: string) => void;
|
fetchProductTypes: (data: string) => void;
|
||||||
|
onWarehouseEdit: () => void;
|
||||||
onBack?();
|
onBack?();
|
||||||
onSubmit?(data: ProductCreatePageSubmitData);
|
onSubmit?(data: ProductCreatePageSubmitData);
|
||||||
}
|
}
|
||||||
|
@ -100,9 +105,11 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
header,
|
header,
|
||||||
productTypes: productTypeChoiceList,
|
productTypes: productTypeChoiceList,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
|
warehouses,
|
||||||
onBack,
|
onBack,
|
||||||
fetchProductTypes,
|
fetchProductTypes,
|
||||||
onSubmit
|
onSubmit,
|
||||||
|
onWarehouseEdit
|
||||||
}: ProductCreatePageProps) => {
|
}: ProductCreatePageProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const localizeDate = useDateLocalize();
|
const localizeDate = useDateLocalize();
|
||||||
|
@ -112,6 +119,18 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
data: attributes,
|
data: attributes,
|
||||||
set: setAttributeData
|
set: setAttributeData
|
||||||
} = useFormset<ProductAttributeInputData>([]);
|
} = useFormset<ProductAttributeInputData>([]);
|
||||||
|
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
|
// Ensures that it will not change after component rerenders, because it
|
||||||
// generates different block keys and it causes editor to lose its content.
|
// generates different block keys and it causes editor to lose its content.
|
||||||
|
@ -131,7 +150,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
seoDescription: "",
|
seoDescription: "",
|
||||||
seoTitle: "",
|
seoTitle: "",
|
||||||
sku: null,
|
sku: null,
|
||||||
stockQuantity: null
|
stockQuantity: null,
|
||||||
|
trackInventory: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Display values
|
// Display values
|
||||||
|
@ -145,12 +165,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
MultiAutocompleteChoiceType[]
|
MultiAutocompleteChoiceType[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
const [productType, setProductType] = React.useState<ProductType>({
|
const [productType, setProductType] = React.useState<ProductType>(null);
|
||||||
hasVariants: false,
|
|
||||||
id: "",
|
|
||||||
name: "",
|
|
||||||
productAttributes: []
|
|
||||||
});
|
|
||||||
|
|
||||||
const categories = getChoices(categoryChoiceList);
|
const categories = getChoices(categoryChoiceList);
|
||||||
const collections = getChoices(collectionChoiceList);
|
const collections = getChoices(collectionChoiceList);
|
||||||
|
@ -159,6 +174,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
const handleSubmit = (data: FormData) =>
|
const handleSubmit = (data: FormData) =>
|
||||||
onSubmit({
|
onSubmit({
|
||||||
attributes,
|
attributes,
|
||||||
|
stocks,
|
||||||
...data
|
...data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -232,14 +248,16 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
{!productType.hasVariants && (
|
{!!productType && !productType.hasVariants && (
|
||||||
<>
|
<>
|
||||||
<ProductStock
|
<ProductStocks
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
product={undefined}
|
onChange={changeStockData}
|
||||||
onChange={change}
|
onFormDataChange={change}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
|
stocks={stocks}
|
||||||
|
onWarehousesEdit={onWarehouseEdit}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
</>
|
</>
|
||||||
|
@ -273,7 +291,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
fetchMoreProductTypes={fetchMoreProductTypes}
|
fetchMoreProductTypes={fetchMoreProductTypes}
|
||||||
fetchProductTypes={fetchProductTypes}
|
fetchProductTypes={fetchProductTypes}
|
||||||
productType={productType}
|
productType={productType}
|
||||||
productTypeInputDisplayValue={productType.name}
|
productTypeInputDisplayValue={productType?.name || ""}
|
||||||
productTypes={productTypes}
|
productTypes={productTypes}
|
||||||
onCategoryChange={handleCategorySelect}
|
onCategoryChange={handleCategorySelect}
|
||||||
onCollectionChange={handleCollectionSelect}
|
onCollectionChange={handleCollectionSelect}
|
||||||
|
|
|
@ -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<any>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProductStock: React.FC<ProductStockProps> = props => {
|
|
||||||
const { data, disabled, product, onChange, errors } = props;
|
|
||||||
|
|
||||||
const classes = useStyles(props);
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
const formErrors = getFormErrors(["sku", "stockQuantity"], errors);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardTitle
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Inventory",
|
|
||||||
description: "product stock, section header",
|
|
||||||
id: "productStockHeader"
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<CardContent>
|
|
||||||
<div className={classes.root}>
|
|
||||||
<TextField
|
|
||||||
disabled={disabled}
|
|
||||||
name="sku"
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "SKU (Stock Keeping Unit)"
|
|
||||||
})}
|
|
||||||
value={data.sku}
|
|
||||||
onChange={onChange}
|
|
||||||
error={!!formErrors.sku}
|
|
||||||
helperText={getProductErrorMessage(formErrors.sku, intl)}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
disabled={disabled}
|
|
||||||
name="stockQuantity"
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Inventory",
|
|
||||||
description: "product stock",
|
|
||||||
id: "prodictStockInventoryLabel"
|
|
||||||
})}
|
|
||||||
value={data.stockQuantity}
|
|
||||||
type="number"
|
|
||||||
onChange={onChange}
|
|
||||||
helperText={
|
|
||||||
getProductErrorMessage(formErrors.stockQuantity, intl) ||
|
|
||||||
(product &&
|
|
||||||
intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: "Allocated: {quantity}",
|
|
||||||
description: "allocated product stock"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quantity: product?.variants[0].quantityAllocated
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
ProductStock.displayName = "ProductStock";
|
|
||||||
export default ProductStock;
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default } from "./ProductStock";
|
|
||||||
export * from "./ProductStock";
|
|
218
src/products/components/ProductStocks/ProductStocks.tsx
Normal file
218
src/products/components/ProductStocks/ProductStocks.tsx
Normal file
|
@ -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<null, string>;
|
||||||
|
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<ProductStocksProps> = ({
|
||||||
|
data,
|
||||||
|
disabled,
|
||||||
|
errors,
|
||||||
|
stocks,
|
||||||
|
onChange,
|
||||||
|
onFormDataChange,
|
||||||
|
onWarehousesEdit
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({});
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Inventory",
|
||||||
|
description: "product stock, section header",
|
||||||
|
id: "productStockHeader"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
|
<div className={classes.skuInputContainer}>
|
||||||
|
<TextField
|
||||||
|
disabled={disabled}
|
||||||
|
error={!!getFieldError(errors, "sku")}
|
||||||
|
fullWidth
|
||||||
|
helperText={getFieldError(errors, "sku")?.message}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "SKU (Stock Keeping Unit)"
|
||||||
|
})}
|
||||||
|
name="sku"
|
||||||
|
onChange={onFormDataChange}
|
||||||
|
value={data.sku}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FormSpacer />
|
||||||
|
<ControlledCheckbox
|
||||||
|
checked={data.trackInventory}
|
||||||
|
name="trackInventory"
|
||||||
|
onChange={onFormDataChange}
|
||||||
|
disabled={disabled}
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Track Inventory"
|
||||||
|
description="product inventory, checkbox"
|
||||||
|
/>
|
||||||
|
<Typography variant="caption">
|
||||||
|
<FormattedMessage defaultMessage="Active inventory tracking will automatically calculate changes of stock" />
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
<Hr />
|
||||||
|
<CardContent className={classes.quantityContainer}>
|
||||||
|
<Typography>
|
||||||
|
<div className={classes.quantityHeader}>
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Quantity"
|
||||||
|
description="header"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
className={classes.editWarehouses}
|
||||||
|
color="primary"
|
||||||
|
data-cy="edit-warehouses"
|
||||||
|
onClick={onWarehousesEdit}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Edit Warehouses"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell className={classes.colName}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Warehouse Name"
|
||||||
|
description="tabel column header"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Quantity Available"
|
||||||
|
description="tabel column header"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{renderCollection(
|
||||||
|
stocks,
|
||||||
|
stock => (
|
||||||
|
<TableRow key={stock.id}>
|
||||||
|
<TableCell className={classes.colName}>{stock.label}</TableCell>
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
<TextField
|
||||||
|
className={classes.inputComponent}
|
||||||
|
disabled={disabled}
|
||||||
|
fullWidth
|
||||||
|
inputProps={{
|
||||||
|
className: classes.input,
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
onChange={event => onChange(stock.id, event.target.value)}
|
||||||
|
value={stock.value}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
),
|
||||||
|
() => (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={2}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage={
|
||||||
|
"This product doesn't have any stock. You can add it <l>here</l>."
|
||||||
|
}
|
||||||
|
values={{
|
||||||
|
l: str => <Link onClick={onWarehousesEdit}>{str}</Link>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductStocks.displayName = "ProductStocks";
|
||||||
|
export default ProductStocks;
|
2
src/products/components/ProductStocks/index.ts
Normal file
2
src/products/components/ProductStocks/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./ProductStocks";
|
||||||
|
export { default } from "./ProductStocks";
|
|
@ -34,7 +34,8 @@ import {
|
||||||
getProductUpdatePageFormData,
|
getProductUpdatePageFormData,
|
||||||
getSelectedAttributesFromProduct,
|
getSelectedAttributesFromProduct,
|
||||||
ProductAttributeValueChoices,
|
ProductAttributeValueChoices,
|
||||||
ProductUpdatePageFormData
|
ProductUpdatePageFormData,
|
||||||
|
getStockInputFromProduct
|
||||||
} from "../../utils/data";
|
} from "../../utils/data";
|
||||||
import {
|
import {
|
||||||
createAttributeChangeHandler,
|
createAttributeChangeHandler,
|
||||||
|
@ -45,8 +46,8 @@ import ProductDetailsForm from "../ProductDetailsForm";
|
||||||
import ProductImages from "../ProductImages";
|
import ProductImages from "../ProductImages";
|
||||||
import ProductOrganization from "../ProductOrganization";
|
import ProductOrganization from "../ProductOrganization";
|
||||||
import ProductPricing from "../ProductPricing";
|
import ProductPricing from "../ProductPricing";
|
||||||
import ProductStock from "../ProductStock";
|
|
||||||
import ProductVariants from "../ProductVariants";
|
import ProductVariants from "../ProductVariants";
|
||||||
|
import ProductStocks, { ProductStockInput } from "../ProductStocks";
|
||||||
|
|
||||||
export interface ProductUpdatePageProps extends ListActions {
|
export interface ProductUpdatePageProps extends ListActions {
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
|
@ -63,6 +64,7 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
fetchCategories: (query: string) => void;
|
fetchCategories: (query: string) => void;
|
||||||
fetchCollections: (query: string) => void;
|
fetchCollections: (query: string) => void;
|
||||||
|
onWarehousesEdit: () => void;
|
||||||
onVariantsAdd: () => void;
|
onVariantsAdd: () => void;
|
||||||
onVariantShow: (id: string) => () => void;
|
onVariantShow: (id: string) => () => void;
|
||||||
onImageDelete: (id: string) => () => void;
|
onImageDelete: (id: string) => () => void;
|
||||||
|
@ -71,7 +73,6 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
onImageEdit?(id: string);
|
onImageEdit?(id: string);
|
||||||
onImageReorder?(event: { oldIndex: number; newIndex: number });
|
onImageReorder?(event: { oldIndex: number; newIndex: number });
|
||||||
onImageUpload(file: File);
|
onImageUpload(file: File);
|
||||||
onProductShow?();
|
|
||||||
onSeoClick?();
|
onSeoClick?();
|
||||||
onSubmit?(data: ProductUpdatePageSubmitData);
|
onSubmit?(data: ProductUpdatePageSubmitData);
|
||||||
onVariantAdd?();
|
onVariantAdd?();
|
||||||
|
@ -80,6 +81,7 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
|
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
|
||||||
attributes: ProductAttributeInput[];
|
attributes: ProductAttributeInput[];
|
||||||
collections: string[];
|
collections: string[];
|
||||||
|
stocks: ProductStockInput[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
|
@ -108,6 +110,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
onVariantsAdd,
|
onVariantsAdd,
|
||||||
onVariantShow,
|
onVariantShow,
|
||||||
|
onWarehousesEdit,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -120,9 +123,13 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
() => getAttributeInputFromProduct(product),
|
() => getAttributeInputFromProduct(product),
|
||||||
[product]
|
[product]
|
||||||
);
|
);
|
||||||
|
const stockInput = React.useMemo(() => getStockInputFromProduct(product), [
|
||||||
|
product
|
||||||
|
]);
|
||||||
const { change: changeAttributeData, data: attributes } = useFormset(
|
const { change: changeAttributeData, data: attributes } = useFormset(
|
||||||
attributeInput
|
attributeInput
|
||||||
);
|
);
|
||||||
|
const { change: changeStockData, data: stocks } = useFormset(stockInput);
|
||||||
|
|
||||||
const [selectedAttributes, setSelectedAttributes] = useStateFromProps<
|
const [selectedAttributes, setSelectedAttributes] = useStateFromProps<
|
||||||
ProductAttributeValueChoices[]
|
ProductAttributeValueChoices[]
|
||||||
|
@ -149,6 +156,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
const handleSubmit = (data: ProductUpdatePageFormData) =>
|
const handleSubmit = (data: ProductUpdatePageFormData) =>
|
||||||
onSubmit({
|
onSubmit({
|
||||||
attributes,
|
attributes,
|
||||||
|
stocks,
|
||||||
...data
|
...data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -239,12 +247,17 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProductStock
|
<ProductStocks
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
product={product}
|
|
||||||
onChange={change}
|
|
||||||
errors={errors}
|
errors={errors}
|
||||||
|
stocks={stocks}
|
||||||
|
onChange={(id, value) => {
|
||||||
|
triggerChange();
|
||||||
|
changeStockData(id, value);
|
||||||
|
}}
|
||||||
|
onFormDataChange={change}
|
||||||
|
onWarehousesEdit={onWarehousesEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
|
|
|
@ -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 => (
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
margin: "auto",
|
|
||||||
overflow: "visible",
|
|
||||||
width: 800
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent>{storyFn()}</CardContent>
|
|
||||||
</Card>
|
|
||||||
))
|
|
||||||
.addDecorator(Decorator)
|
|
||||||
.add("choose values", () => <ProductVariantCreateContent {...props} />)
|
|
||||||
.add("prices and SKU", () => (
|
|
||||||
<ProductVariantCreateContent {...props} step="prices" />
|
|
||||||
));
|
|
||||||
|
|
||||||
storiesOf("Views / Products / Create multiple variants / summary", module)
|
|
||||||
.addDecorator(storyFn => (
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
margin: "auto",
|
|
||||||
overflow: "visible",
|
|
||||||
width: 800
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent>{storyFn()}</CardContent>
|
|
||||||
</Card>
|
|
||||||
))
|
|
||||||
.addDecorator(Decorator)
|
|
||||||
.add("default", () => (
|
|
||||||
<ProductVariantCreateContent {...props} step="summary" />
|
|
||||||
))
|
|
||||||
.add("errors", () => (
|
|
||||||
<ProductVariantCreateContent {...props} step="summary" errors={errors} />
|
|
||||||
));
|
|
||||||
|
|
||||||
storiesOf("Views / Products / Create multiple variants", module)
|
|
||||||
.addDecorator(Decorator)
|
|
||||||
.add("interactive", () => (
|
|
||||||
<ProductVariantCreateDialog
|
|
||||||
{...props}
|
|
||||||
defaultPrice="10.99"
|
|
||||||
open={true}
|
|
||||||
onClose={() => undefined}
|
|
||||||
onSubmit={() => undefined}
|
|
||||||
/>
|
|
||||||
));
|
|
|
@ -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<ProductVariantCreateReducerAction>;
|
|
||||||
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
|
|
||||||
step: ProductVariantCreateStep;
|
|
||||||
onStepClick: (step: ProductVariantCreateStep) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProductVariantCreateContent: React.FC<ProductVariantCreateContentProps> = 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 (
|
|
||||||
<div>
|
|
||||||
<ProductVariantCreateTabs step={step} onStepClick={onStepClick} />
|
|
||||||
<div className={classes.root}>
|
|
||||||
{step === "values" && (
|
|
||||||
<ProductVariantCreateValues
|
|
||||||
attributes={selectedAttributes}
|
|
||||||
data={data}
|
|
||||||
onValueClick={(attributeId, valueId) =>
|
|
||||||
dispatchFormDataAction({
|
|
||||||
attributeId,
|
|
||||||
type: "selectValue",
|
|
||||||
valueId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{step === "prices" && (
|
|
||||||
<ProductVariantCreatePrices
|
|
||||||
attributes={selectedAttributes}
|
|
||||||
currencySymbol={currencySymbol}
|
|
||||||
data={data}
|
|
||||||
onApplyPriceOrStockChange={(all, type) =>
|
|
||||||
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" && (
|
|
||||||
<ProductVariantCreateSummary
|
|
||||||
attributes={selectedAttributes}
|
|
||||||
currencySymbol={currencySymbol}
|
|
||||||
data={data}
|
|
||||||
errors={errors}
|
|
||||||
onVariantDataChange={(variantIndex, field, value) =>
|
|
||||||
dispatchFormDataAction({
|
|
||||||
field,
|
|
||||||
type: "changeVariantData",
|
|
||||||
value,
|
|
||||||
variantIndex
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onVariantDelete={variantIndex =>
|
|
||||||
dispatchFormDataAction({
|
|
||||||
type: "deleteVariant",
|
|
||||||
variantIndex
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProductVariantCreateContent.displayName = "ProductVariantCreateContent";
|
|
||||||
export default ProductVariantCreateContent;
|
|
|
@ -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<ProductVariantCreateStep>("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 (
|
|
||||||
<Dialog open={open} maxWidth="md">
|
|
||||||
<DialogTitle>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Assign Attribute"
|
|
||||||
description="dialog header"
|
|
||||||
/>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent className={classes.content}>
|
|
||||||
<ProductVariantCreateContent
|
|
||||||
{...contentProps}
|
|
||||||
attributes={attributes}
|
|
||||||
data={data}
|
|
||||||
dispatchFormDataAction={dispatchFormDataAction}
|
|
||||||
errors={errors}
|
|
||||||
step={step}
|
|
||||||
onStepClick={step => setStep(step)}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button className={classes.button} onClick={onClose}>
|
|
||||||
<FormattedMessage defaultMessage="Cancel" description="button" />
|
|
||||||
</Button>
|
|
||||||
<div className={classes.spacer} />
|
|
||||||
{step !== "values" && (
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
color="primary"
|
|
||||||
onClick={handlePrevStep}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Previous"
|
|
||||||
description="previous step, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{step !== "summary" ? (
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
color="primary"
|
|
||||||
disabled={!canHitNext(step, data)}
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleNextStep}
|
|
||||||
>
|
|
||||||
<FormattedMessage defaultMessage="Next" description="button" />
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
className={classes.button}
|
|
||||||
color="primary"
|
|
||||||
disabled={!canHitNext(step, data)}
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => onSubmit(data.variants)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Create"
|
|
||||||
description="create multiple variants, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProductVariantCreateDialog.displayName = "ProductVariantCreateDialog";
|
|
||||||
export default ProductVariantCreateDialog;
|
|
|
@ -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 (
|
|
||||||
<>
|
|
||||||
<Typography color="textSecondary" variant="h5">
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Price"
|
|
||||||
description="variant price, header"
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
<Hr className={classes.hr} />
|
|
||||||
<RadioGroup value={data.price.all ? "applyToAll" : "applyToAttribute"}>
|
|
||||||
<FormControlLabel
|
|
||||||
value="applyToAll"
|
|
||||||
control={<Radio color="primary" />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Apply single price to all SKUs"
|
|
||||||
})}
|
|
||||||
onChange={() => onApplyPriceOrStockChange(true, "price")}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<TextField
|
|
||||||
className={classes.shortInput}
|
|
||||||
inputProps={{
|
|
||||||
min: 0,
|
|
||||||
type: "number"
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: currencySymbol
|
|
||||||
}}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Price",
|
|
||||||
id: "productVariantCreatePricesPriceInputLabel"
|
|
||||||
})}
|
|
||||||
value={data.price.value}
|
|
||||||
onChange={event => onApplyToAllChange(event.target.value, "price")}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<FormControlLabel
|
|
||||||
value="applyToAttribute"
|
|
||||||
control={<Radio color="primary" />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Apply unique prices by attribute to each SKU"
|
|
||||||
})}
|
|
||||||
onChange={() => onApplyPriceOrStockChange(false, "price")}
|
|
||||||
/>
|
|
||||||
</RadioGroup>
|
|
||||||
{!data.price.all && (
|
|
||||||
<>
|
|
||||||
<FormSpacer />
|
|
||||||
<Grid variant="uniform">
|
|
||||||
<div className={classes.label}>
|
|
||||||
<Typography>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Choose attribute"
|
|
||||||
description="variant attribute"
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SingleSelectField
|
|
||||||
choices={attributeChoices}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Attribute",
|
|
||||||
description: "variant attribute"
|
|
||||||
})}
|
|
||||||
value={data.price.attribute}
|
|
||||||
onChange={event =>
|
|
||||||
onAttributeSelect(event.target.value, "price")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
<Hr className={classes.hrAttribute} />
|
|
||||||
{priceAttributeValues &&
|
|
||||||
priceAttributeValues.map(attributeValue => (
|
|
||||||
<React.Fragment key={attributeValue.id}>
|
|
||||||
<FormSpacer />
|
|
||||||
<Grid variant="uniform">
|
|
||||||
<div className={classes.label}>
|
|
||||||
<Typography>{attributeValue.name}</Typography>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<TextField
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Price",
|
|
||||||
description: "variant price",
|
|
||||||
id: "productVariantCreatePricesSetPricePlaceholder"
|
|
||||||
})}
|
|
||||||
inputProps={{
|
|
||||||
min: 0,
|
|
||||||
type: "number"
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: currencySymbol
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
value={
|
|
||||||
data.price.values.find(
|
|
||||||
value => value.slug === attributeValue.slug
|
|
||||||
).value
|
|
||||||
}
|
|
||||||
onChange={event =>
|
|
||||||
onAttributeValueChange(
|
|
||||||
attributeValue.slug,
|
|
||||||
event.target.value,
|
|
||||||
"price"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<FormSpacer />
|
|
||||||
<Typography color="textSecondary" variant="h5">
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Stock"
|
|
||||||
description="variant stock, header"
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
<Hr className={classes.hr} />
|
|
||||||
<RadioGroup value={data.stock.all ? "applyToAll" : "applyToAttribute"}>
|
|
||||||
<FormControlLabel
|
|
||||||
value="applyToAll"
|
|
||||||
control={<Radio color="primary" />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Apply single stock to all SKUs"
|
|
||||||
})}
|
|
||||||
onChange={() => onApplyPriceOrStockChange(true, "stock")}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<TextField
|
|
||||||
className={classes.shortInput}
|
|
||||||
inputProps={{
|
|
||||||
min: 0,
|
|
||||||
type: "number"
|
|
||||||
}}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Stock",
|
|
||||||
id: "productVariantCreatePricesStockInputLabel"
|
|
||||||
})}
|
|
||||||
value={data.stock.value}
|
|
||||||
onChange={event => onApplyToAllChange(event.target.value, "stock")}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<FormControlLabel
|
|
||||||
value="applyToAttribute"
|
|
||||||
control={<Radio color="primary" />}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Apply unique stock by attribute to each SKU"
|
|
||||||
})}
|
|
||||||
onChange={() => onApplyPriceOrStockChange(false, "stock")}
|
|
||||||
/>
|
|
||||||
</RadioGroup>
|
|
||||||
{!data.stock.all && (
|
|
||||||
<>
|
|
||||||
<FormSpacer />
|
|
||||||
<Grid variant="uniform">
|
|
||||||
<div className={classes.label}>
|
|
||||||
<Typography>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Choose attribute"
|
|
||||||
description="variant attribute"
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SingleSelectField
|
|
||||||
choices={attributeChoices}
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Attribute",
|
|
||||||
description: "variant attribute"
|
|
||||||
})}
|
|
||||||
value={data.stock.attribute}
|
|
||||||
onChange={event =>
|
|
||||||
onAttributeSelect(event.target.value, "stock")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
<Hr className={classes.hrAttribute} />
|
|
||||||
{stockAttributeValues &&
|
|
||||||
stockAttributeValues.map(attributeValue => (
|
|
||||||
<React.Fragment key={attributeValue.id}>
|
|
||||||
<FormSpacer />
|
|
||||||
<Grid variant="uniform">
|
|
||||||
<div className={classes.label}>
|
|
||||||
<Typography>{attributeValue.name}</Typography>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<TextField
|
|
||||||
label={intl.formatMessage({
|
|
||||||
defaultMessage: "Stock",
|
|
||||||
description: "variant stock",
|
|
||||||
id: "productVariantCreatePricesSetStockPlaceholder"
|
|
||||||
})}
|
|
||||||
fullWidth
|
|
||||||
value={
|
|
||||||
data.stock.values.find(
|
|
||||||
value => value.slug === attributeValue.slug
|
|
||||||
).value
|
|
||||||
}
|
|
||||||
onChange={event =>
|
|
||||||
onAttributeValueChange(
|
|
||||||
attributeValue.slug,
|
|
||||||
event.target.value,
|
|
||||||
"stock"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProductVariantCreatePrices.displayName = "ProductVariantCreatePrices";
|
|
||||||
export default ProductVariantCreatePrices;
|
|
|
@ -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 => (
|
|
||||||
<React.Fragment key={attribute.id}>
|
|
||||||
<Typography color="textSecondary" variant="h5">
|
|
||||||
{maybe<React.ReactNode>(() => attribute.name, <Skeleton />)}
|
|
||||||
</Typography>
|
|
||||||
<Hr className={classes.hr} />
|
|
||||||
<div className={classes.valueContainer}>
|
|
||||||
{attribute.values.map(value => (
|
|
||||||
<Debounce
|
|
||||||
debounceFn={() => onValueClick(attribute.id, value.slug)}
|
|
||||||
time={100}
|
|
||||||
key={value.slug}
|
|
||||||
>
|
|
||||||
{change => (
|
|
||||||
<ControlledCheckbox
|
|
||||||
checked={isSelected(
|
|
||||||
value.slug,
|
|
||||||
data.attributes.find(
|
|
||||||
dataAttribute => attribute.id === dataAttribute.id
|
|
||||||
).values,
|
|
||||||
(a, b) => a === b
|
|
||||||
)}
|
|
||||||
name={`value:${value.slug}`}
|
|
||||||
label={value.name}
|
|
||||||
onChange={change}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Debounce>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProductVariantCreateValues.displayName = "ProductVariantCreateValues";
|
|
||||||
export default ProductVariantCreateValues;
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,185 +0,0 @@
|
||||||
import { attributes, fourthStep, secondStep, thirdStep } from "./fixtures";
|
|
||||||
import reducer, { VariantField } from "./reducer";
|
|
||||||
|
|
||||||
function execActions<TState, TAction>(
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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;
|
|
|
@ -1 +0,0 @@
|
||||||
export type ProductVariantCreateStep = "values" | "prices" | "summary";
|
|
|
@ -15,6 +15,7 @@ import useFormset, {
|
||||||
} from "@saleor/hooks/useFormset";
|
} from "@saleor/hooks/useFormset";
|
||||||
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
||||||
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
||||||
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
import { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData";
|
import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData";
|
||||||
import ProductVariantAttributes, {
|
import ProductVariantAttributes, {
|
||||||
|
@ -22,7 +23,7 @@ import ProductVariantAttributes, {
|
||||||
} from "../ProductVariantAttributes";
|
} from "../ProductVariantAttributes";
|
||||||
import ProductVariantNavigation from "../ProductVariantNavigation";
|
import ProductVariantNavigation from "../ProductVariantNavigation";
|
||||||
import ProductVariantPrice from "../ProductVariantPrice";
|
import ProductVariantPrice from "../ProductVariantPrice";
|
||||||
import ProductVariantStock from "../ProductVariantStock";
|
import ProductStocks, { ProductStockInput } from "../ProductStocks";
|
||||||
|
|
||||||
interface ProductVariantCreatePageFormData {
|
interface ProductVariantCreatePageFormData {
|
||||||
costPrice: string;
|
costPrice: string;
|
||||||
|
@ -30,35 +31,41 @@ interface ProductVariantCreatePageFormData {
|
||||||
priceOverride: string;
|
priceOverride: string;
|
||||||
quantity: string;
|
quantity: string;
|
||||||
sku: string;
|
sku: string;
|
||||||
|
trackInventory: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductVariantCreatePageSubmitData
|
export interface ProductVariantCreatePageSubmitData
|
||||||
extends ProductVariantCreatePageFormData {
|
extends ProductVariantCreatePageFormData {
|
||||||
attributes: FormsetData<VariantAttributeInputData>;
|
attributes: FormsetData<VariantAttributeInputData>;
|
||||||
|
stocks: ProductStockInput[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProductVariantCreatePageProps {
|
interface ProductVariantCreatePageProps {
|
||||||
currencySymbol: string;
|
currencySymbol: string;
|
||||||
|
disabled: boolean;
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
header: string;
|
header: string;
|
||||||
loading: boolean;
|
|
||||||
product: ProductVariantCreateData_product;
|
product: ProductVariantCreateData_product;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
|
warehouses: SearchWarehouses_search_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
|
onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
|
||||||
onVariantClick: (variantId: string) => void;
|
onVariantClick: (variantId: string) => void;
|
||||||
|
onWarehouseEdit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
currencySymbol,
|
currencySymbol,
|
||||||
|
disabled,
|
||||||
errors,
|
errors,
|
||||||
loading,
|
|
||||||
header,
|
header,
|
||||||
product,
|
product,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
|
warehouses,
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onVariantClick
|
onVariantClick,
|
||||||
|
onWarehouseEdit
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const attributeInput = React.useMemo(
|
const attributeInput = React.useMemo(
|
||||||
|
@ -68,28 +75,33 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
const { change: changeAttributeData, data: attributes } = useFormset(
|
const { change: changeAttributeData, data: attributes } = useFormset(
|
||||||
attributeInput
|
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 = {
|
const initialForm: ProductVariantCreatePageFormData = {
|
||||||
attributes: maybe(
|
|
||||||
() =>
|
|
||||||
product.productType.variantAttributes.map(attribute => ({
|
|
||||||
name: attribute.name,
|
|
||||||
slug: attribute.slug,
|
|
||||||
values: [""]
|
|
||||||
})),
|
|
||||||
[]
|
|
||||||
),
|
|
||||||
costPrice: "",
|
costPrice: "",
|
||||||
images: maybe(() => product.images.map(image => image.id)),
|
images: maybe(() => product.images.map(image => image.id)),
|
||||||
priceOverride: "",
|
priceOverride: "",
|
||||||
quantity: "0",
|
quantity: "0",
|
||||||
sku: ""
|
sku: "",
|
||||||
|
trackInventory: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (data: ProductVariantCreatePageFormData) =>
|
const handleSubmit = (data: ProductVariantCreatePageFormData) =>
|
||||||
onSubmit({
|
onSubmit({
|
||||||
...data,
|
...data,
|
||||||
attributes
|
attributes,
|
||||||
|
stocks
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -119,7 +131,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<ProductVariantAttributes
|
<ProductVariantAttributes
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
disabled={loading}
|
disabled={disabled}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={handleAttributeChange}
|
onChange={handleAttributeChange}
|
||||||
/>
|
/>
|
||||||
|
@ -129,21 +141,23 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
priceOverride={data.priceOverride}
|
priceOverride={data.priceOverride}
|
||||||
currencySymbol={currencySymbol}
|
currencySymbol={currencySymbol}
|
||||||
costPrice={data.costPrice}
|
costPrice={data.costPrice}
|
||||||
loading={loading}
|
loading={disabled}
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<ProductVariantStock
|
<ProductStocks
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={changeStockData}
|
||||||
|
onFormDataChange={change}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
sku={data.sku}
|
stocks={stocks}
|
||||||
quantity={data.quantity}
|
onWarehousesEdit={onWarehouseEdit}
|
||||||
loading={loading}
|
|
||||||
onChange={change}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<SaveButtonBar
|
<SaveButtonBar
|
||||||
disabled={loading || !onSubmit || !hasChanged}
|
disabled={disabled || !onSubmit || !hasChanged}
|
||||||
labels={{
|
labels={{
|
||||||
delete: intl.formatMessage({
|
delete: intl.formatMessage({
|
||||||
defaultMessage: "Delete Variant",
|
defaultMessage: "Delete Variant",
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
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 Container from "@saleor/components/Container";
|
||||||
|
import { warehouseList } from "@saleor/warehouses/fixtures";
|
||||||
|
import Decorator from "../../../storybook/Decorator";
|
||||||
|
import { createVariants } from "./createVariants";
|
||||||
|
import { AllOrAttribute, ProductVariantCreateFormData } from "./form";
|
||||||
|
import ProductVariantCreatorContent, {
|
||||||
|
ProductVariantCreatorContentProps
|
||||||
|
} from "./ProductVariantCreatorContent";
|
||||||
|
import ProductVariantCreatorPage from "./ProductVariantCreatorPage";
|
||||||
|
import { ProductVariantCreatorStep } from "./types";
|
||||||
|
|
||||||
|
const selectedAttributes = [1, 4, 5].map(index => attributes[index]);
|
||||||
|
const selectedWarehouses = [0, 1, 3].map(index => warehouseList[index]);
|
||||||
|
|
||||||
|
const price: AllOrAttribute<string> = {
|
||||||
|
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<number[]> = {
|
||||||
|
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 => <Container>{storyFn()}</Container>)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("choose values", () => <ProductVariantCreatorContent {...props} />);
|
||||||
|
|
||||||
|
storiesOf(
|
||||||
|
"Views / Products / Create multiple variants / prices and SKUs",
|
||||||
|
module
|
||||||
|
)
|
||||||
|
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("apply to all", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
data={{
|
||||||
|
...data,
|
||||||
|
stock: {
|
||||||
|
...data.stock,
|
||||||
|
mode: "all"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
step={ProductVariantCreatorStep.prices}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
.add("apply to all when one warehouse", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
data={{
|
||||||
|
...data,
|
||||||
|
stock: {
|
||||||
|
...data.stock,
|
||||||
|
mode: "all"
|
||||||
|
},
|
||||||
|
warehouses: [data.warehouses[0]]
|
||||||
|
}}
|
||||||
|
step={ProductVariantCreatorStep.prices}
|
||||||
|
warehouses={[props.warehouses[0]]}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
.add("apply to attribute", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
step={ProductVariantCreatorStep.prices}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
.add("apply to attribute when one warehouse", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
data={{
|
||||||
|
...data,
|
||||||
|
warehouses: [data.warehouses[0]]
|
||||||
|
}}
|
||||||
|
step={ProductVariantCreatorStep.prices}
|
||||||
|
warehouses={[props.warehouses[0]]}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
storiesOf("Views / Products / Create multiple variants / summary", module)
|
||||||
|
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("default", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
step={ProductVariantCreatorStep.summary}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
.add("errors", () => (
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...props}
|
||||||
|
step={ProductVariantCreatorStep.summary}
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
storiesOf("Views / Products / Create multiple variants", module)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("interactive", () => (
|
||||||
|
<ProductVariantCreatorPage
|
||||||
|
{...props}
|
||||||
|
defaultPrice="10.99"
|
||||||
|
onSubmit={() => undefined}
|
||||||
|
/>
|
||||||
|
));
|
|
@ -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<ProductVariantCreateReducerAction>;
|
||||||
|
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
|
||||||
|
step: ProductVariantCreatorStep;
|
||||||
|
warehouses: WarehouseFragment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProductVariantCreatorContent: React.FC<ProductVariantCreatorContentProps> = ({
|
||||||
|
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 && (
|
||||||
|
<ProductVariantCreateValues
|
||||||
|
attributes={selectedAttributes}
|
||||||
|
data={data}
|
||||||
|
onValueClick={(attributeId, valueId) =>
|
||||||
|
dispatchFormDataAction({
|
||||||
|
selectValue: {
|
||||||
|
attributeId,
|
||||||
|
valueId
|
||||||
|
},
|
||||||
|
type: ProductVariantCreateReducerActionType.selectValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{step === ProductVariantCreatorStep.prices && (
|
||||||
|
<ProductVariantCreatePriceAndSku
|
||||||
|
attributes={selectedAttributes}
|
||||||
|
currencySymbol={currencySymbol}
|
||||||
|
data={data}
|
||||||
|
warehouses={warehouses}
|
||||||
|
onApplyToAllChange={(mode, type) =>
|
||||||
|
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 && (
|
||||||
|
<ProductVariantCreateSummary
|
||||||
|
attributes={selectedAttributes}
|
||||||
|
currencySymbol={currencySymbol}
|
||||||
|
data={data}
|
||||||
|
errors={errors}
|
||||||
|
onVariantDataChange={(variantIndex, field, value) =>
|
||||||
|
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;
|
|
@ -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<ProductVariantCreatePageProps> = 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 (
|
||||||
|
<Container>
|
||||||
|
<ProductVariantCreateTabs step={step} onStepClick={setStep} />
|
||||||
|
<PageHeader
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
{getTitle(step, intl)}
|
||||||
|
<Typography className={classes.description} variant="body2">
|
||||||
|
{getDescription(step, intl)}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{step !== ProductVariantCreatorStep.values && (
|
||||||
|
<Button className={classes.button} color="primary" onClick={prevStep}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Previous"
|
||||||
|
description="previous step, button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{step !== ProductVariantCreatorStep.summary ? (
|
||||||
|
<Button
|
||||||
|
className={classes.button}
|
||||||
|
color="primary"
|
||||||
|
disabled={!canHitNext(step, wizardData)}
|
||||||
|
variant="contained"
|
||||||
|
onClick={nextStep}
|
||||||
|
>
|
||||||
|
<FormattedMessage defaultMessage="Next" description="button" />
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
className={classes.button}
|
||||||
|
color="primary"
|
||||||
|
disabled={!canHitNext(step, wizardData)}
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => onSubmit(wizardData.variants)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Create"
|
||||||
|
description="create multiple variants, button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</PageHeader>
|
||||||
|
<Hr className={classes.hr} />
|
||||||
|
<ProductVariantCreatorContent
|
||||||
|
{...contentProps}
|
||||||
|
attributes={attributes}
|
||||||
|
data={wizardData}
|
||||||
|
dispatchFormDataAction={dispatchFormDataAction}
|
||||||
|
errors={errors}
|
||||||
|
step={step}
|
||||||
|
warehouses={warehouses}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductVariantCreatePage.displayName = "ProductVariantCreatePage";
|
||||||
|
export default ProductVariantCreatePage;
|
|
@ -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<ProductVariantCreatorPriceAndSkuProps> = ({
|
||||||
|
attributes,
|
||||||
|
currencySymbol,
|
||||||
|
data,
|
||||||
|
warehouses,
|
||||||
|
onApplyToAllChange,
|
||||||
|
onApplyToAllPriceChange,
|
||||||
|
onApplyToAllStockChange,
|
||||||
|
onAttributeSelect,
|
||||||
|
onAttributePriceChange,
|
||||||
|
onAttributeStockChange,
|
||||||
|
onWarehouseToggle
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<ProductVariantCreatorPrices
|
||||||
|
attributes={attributes}
|
||||||
|
currencySymbol={currencySymbol}
|
||||||
|
data={data}
|
||||||
|
onApplyToAllChange={value => onApplyToAllChange(value, "price")}
|
||||||
|
onApplyToAllPriceChange={onApplyToAllPriceChange}
|
||||||
|
onAttributeSelect={id => onAttributeSelect(id, "price")}
|
||||||
|
onAttributeValueChange={onAttributePriceChange}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<ProductVariantCreatorStock
|
||||||
|
attributes={attributes}
|
||||||
|
data={data}
|
||||||
|
warehouses={warehouses}
|
||||||
|
onApplyToAllChange={value => onApplyToAllChange(value, "stock")}
|
||||||
|
onApplyToAllStockChange={onApplyToAllStockChange}
|
||||||
|
onAttributeSelect={id => onAttributeSelect(id, "stock")}
|
||||||
|
onAttributeValueChange={onAttributeStockChange}
|
||||||
|
onWarehouseToggle={onWarehouseToggle}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
ProductVariantCreatorPriceAndSku.displayName =
|
||||||
|
"ProductVariantCreatorPriceAndSku";
|
||||||
|
export default ProductVariantCreatorPriceAndSku;
|
|
@ -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<ProductVariantCreatorPricesProps> = 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 (
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Price",
|
||||||
|
description: "variant price, header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
|
<RadioGroup value={data.price.mode}>
|
||||||
|
<FormControlLabel
|
||||||
|
value="all"
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Apply single price to all SKUs"
|
||||||
|
})}
|
||||||
|
onChange={() => onApplyToAllChange("all")}
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<TextField
|
||||||
|
className={classes.shortInput}
|
||||||
|
inputProps={{
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: currencySymbol
|
||||||
|
}}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Price",
|
||||||
|
id: "productVariantCreatePricesPriceInputLabel"
|
||||||
|
})}
|
||||||
|
value={data.price.value}
|
||||||
|
onChange={event => onApplyToAllPriceChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<FormControlLabel
|
||||||
|
value="attribute"
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Apply unique prices by attribute to each SKU"
|
||||||
|
})}
|
||||||
|
onChange={() => onApplyToAllChange("attribute")}
|
||||||
|
/>
|
||||||
|
</RadioGroup>
|
||||||
|
{data.price.mode === "attribute" && (
|
||||||
|
<>
|
||||||
|
<FormSpacer />
|
||||||
|
<Grid variant="uniform">
|
||||||
|
<div className={classes.label}>
|
||||||
|
<Typography>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Choose attribute"
|
||||||
|
description="variant attribute"
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<SingleSelectField
|
||||||
|
choices={attributeChoices}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Attribute",
|
||||||
|
description: "variant attribute"
|
||||||
|
})}
|
||||||
|
value={data.price.attribute}
|
||||||
|
onChange={event => onAttributeSelect(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
{priceAttributeValues &&
|
||||||
|
priceAttributeValues.map(attributeValue => (
|
||||||
|
<React.Fragment key={attributeValue.id}>
|
||||||
|
<Hr className={classes.hrAttribute} />
|
||||||
|
<FormSpacer />
|
||||||
|
<Grid variant="uniform">
|
||||||
|
<div className={classes.label}>
|
||||||
|
<Typography>{attributeValue.name}</Typography>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Price",
|
||||||
|
description: "variant price",
|
||||||
|
id: "productVariantCreatePricesSetPricePlaceholder"
|
||||||
|
})}
|
||||||
|
inputProps={{
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: currencySymbol
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
value={
|
||||||
|
data.price.values.find(
|
||||||
|
value => value.slug === attributeValue.slug
|
||||||
|
).value
|
||||||
|
}
|
||||||
|
onChange={event =>
|
||||||
|
onAttributeValueChange(
|
||||||
|
attributeValue.slug,
|
||||||
|
event.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductVariantCreatorPrices.displayName = "ProductVariantCreatorPrices";
|
||||||
|
export default ProductVariantCreatorPrices;
|
|
@ -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<ProductVariantCreatorStockProps> = 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 (
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Stock and Warehousing",
|
||||||
|
description: "variant stock, header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
|
{warehouses.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Typography className={classes.warehouseHeader} variant="h5">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Warehouses"
|
||||||
|
description="header"
|
||||||
|
id="productVariantCreatorWarehouseSectionHeader"
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<Typography className={classes.warehouseSubheader}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Based on your selections we will create {numberOfProducts} products. Use this step to customize price and stocks for your new products"
|
||||||
|
values={{
|
||||||
|
numberOfProducts: data.attributes.reduce(
|
||||||
|
(acc, attr) => acc + attr.values.length,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.warehouseContainer}>
|
||||||
|
{warehouses.map(warehouse => (
|
||||||
|
<ControlledCheckbox
|
||||||
|
checked={isSelected(
|
||||||
|
warehouse.id,
|
||||||
|
data.warehouses,
|
||||||
|
(a, b) => a === b
|
||||||
|
)}
|
||||||
|
name={`warehouse:${warehouse.id}`}
|
||||||
|
label={warehouse.name}
|
||||||
|
onChange={() => onWarehouseToggle(warehouse.id)}
|
||||||
|
key={warehouse.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<CardSpacer />
|
||||||
|
<Hr />
|
||||||
|
<CardSpacer />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Typography className={classes.stockHeader} variant="h5">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Stock"
|
||||||
|
description="variant stock, header"
|
||||||
|
id="productVariantCreatorStockSectionHeader"
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<RadioGroup value={data.stock.mode}>
|
||||||
|
<FormControlLabel
|
||||||
|
value="all"
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Apply single stock to all SKUs"
|
||||||
|
})}
|
||||||
|
onChange={() => onApplyToAllChange("all")}
|
||||||
|
/>
|
||||||
|
{data.stock.mode === "all" && (
|
||||||
|
<div className={classes.stockContainer}>
|
||||||
|
{data.warehouses.map((warehouseId, warehouseIndex) => (
|
||||||
|
<div key={warehouseId}>
|
||||||
|
<Typography className={classes.warehouseName}>
|
||||||
|
{
|
||||||
|
warehouses.find(warehouse => warehouse.id === warehouseId)
|
||||||
|
.name
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
inputProps={{
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Stock",
|
||||||
|
id: "productVariantCreatePricesStockInputLabel"
|
||||||
|
})}
|
||||||
|
value={data.stock.value[warehouseIndex]}
|
||||||
|
onChange={event =>
|
||||||
|
onApplyToAllStockChange(
|
||||||
|
parseInt(event.target.value, 10),
|
||||||
|
warehouseIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormSpacer />
|
||||||
|
<FormControlLabel
|
||||||
|
value="attribute"
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Apply unique stock by attribute to each SKU"
|
||||||
|
})}
|
||||||
|
onChange={() => onApplyToAllChange("attribute")}
|
||||||
|
/>
|
||||||
|
{data.stock.mode === "attribute" && (
|
||||||
|
<>
|
||||||
|
<FormSpacer />
|
||||||
|
<SingleSelectField
|
||||||
|
className={classes.shortInput}
|
||||||
|
choices={attributeChoices}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Select Attribute",
|
||||||
|
description: "variant attribute"
|
||||||
|
})}
|
||||||
|
value={data.stock.attribute}
|
||||||
|
onChange={event => onAttributeSelect(event.target.value)}
|
||||||
|
/>
|
||||||
|
{stockAttributeValues && (
|
||||||
|
<>
|
||||||
|
<Hr className={classes.hrAttribute} />
|
||||||
|
<FormSpacer />
|
||||||
|
<div className={classes.attributeStockScroll}>
|
||||||
|
<div className={classes.attributeStockContainer}>
|
||||||
|
<div />
|
||||||
|
{data.stock.attribute &&
|
||||||
|
data.warehouses.map(warehouseId => (
|
||||||
|
<Typography
|
||||||
|
className={classes.warehouseName}
|
||||||
|
key={warehouseId}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
warehouses.find(
|
||||||
|
warehouse => warehouse.id === warehouseId
|
||||||
|
).name
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
{stockAttributeValues.map(attributeValue => (
|
||||||
|
<React.Fragment key={attributeValue.id}>
|
||||||
|
<Typography>{attributeValue.name}</Typography>
|
||||||
|
{data.warehouses.map(
|
||||||
|
(warehouseId, warehouseIndex) => (
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
inputProps={{
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Stock",
|
||||||
|
id:
|
||||||
|
"productVariantCreatePricesStockInputLabel"
|
||||||
|
})}
|
||||||
|
value={
|
||||||
|
data.stock.values.find(
|
||||||
|
value => value.slug === attributeValue.slug
|
||||||
|
).value[warehouseIndex]
|
||||||
|
}
|
||||||
|
onChange={event =>
|
||||||
|
onAttributeValueChange(
|
||||||
|
attributeValue.slug,
|
||||||
|
parseInt(event.target.value, 10),
|
||||||
|
warehouseIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
key={warehouseId}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{data.stock.mode === "attribute" && !!data.stock.attribute && (
|
||||||
|
<>
|
||||||
|
<FormSpacer />
|
||||||
|
<Hr />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormSpacer />
|
||||||
|
<FormControlLabel
|
||||||
|
value="skip"
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Skip stock for now"
|
||||||
|
})}
|
||||||
|
onChange={() => onApplyToAllChange("skip")}
|
||||||
|
/>
|
||||||
|
</RadioGroup>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductVariantCreatorStock.displayName = "ProductVariantCreatorStock";
|
||||||
|
export default ProductVariantCreatorStock;
|
|
@ -3,62 +3,87 @@ import cyan from "@material-ui/core/colors/cyan";
|
||||||
import green from "@material-ui/core/colors/green";
|
import green from "@material-ui/core/colors/green";
|
||||||
import purple from "@material-ui/core/colors/purple";
|
import purple from "@material-ui/core/colors/purple";
|
||||||
import yellow from "@material-ui/core/colors/yellow";
|
import yellow from "@material-ui/core/colors/yellow";
|
||||||
|
import Card from "@material-ui/core/Card";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
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 TextField from "@material-ui/core/TextField";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import Hr from "@saleor/components/Hr";
|
|
||||||
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
|
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||||
import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes";
|
import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes";
|
||||||
import { getFormErrors } from "@saleor/utils/errors";
|
import { getFormErrors } from "@saleor/utils/errors";
|
||||||
import { getBulkProductErrorMessage } from "@saleor/utils/errors/product";
|
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 { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails";
|
||||||
import { ProductVariantCreateFormData } from "./form";
|
import { ProductVariantCreateFormData } from "./form";
|
||||||
import { VariantField } from "./reducer";
|
import { VariantField } from "./reducer";
|
||||||
|
|
||||||
export interface ProductVariantCreateSummaryProps {
|
export interface ProductVariantCreatorSummaryProps {
|
||||||
attributes: ProductDetails_product_productType_variantAttributes[];
|
attributes: ProductDetails_product_productType_variantAttributes[];
|
||||||
currencySymbol: string;
|
currencySymbol: string;
|
||||||
data: ProductVariantCreateFormData;
|
data: ProductVariantCreateFormData;
|
||||||
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
|
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
|
||||||
|
warehouses: WarehouseFragment[];
|
||||||
onVariantDataChange: (
|
onVariantDataChange: (
|
||||||
variantIndex: number,
|
variantIndex: number,
|
||||||
field: VariantField,
|
field: VariantField,
|
||||||
value: string
|
value: string
|
||||||
) => void;
|
) => void;
|
||||||
|
onVariantStockDataChange: (
|
||||||
|
variantIndex: number,
|
||||||
|
warehouseId: string,
|
||||||
|
value: string
|
||||||
|
) => void;
|
||||||
onVariantDelete: (variantIndex: number) => 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 colors = [blue, cyan, green, purple, yellow].map(color => color[800]);
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles<
|
||||||
|
Theme,
|
||||||
|
ProductVariantCreatorSummaryProps,
|
||||||
|
ClassKey
|
||||||
|
>(
|
||||||
theme => ({
|
theme => ({
|
||||||
attributeValue: {
|
attributeValue: {
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
marginRight: theme.spacing(1)
|
marginRight: theme.spacing(1)
|
||||||
},
|
},
|
||||||
col: {
|
card: {
|
||||||
...theme.typography.body1,
|
paddingBottom: theme.spacing()
|
||||||
fontSize: 14,
|
|
||||||
paddingLeft: theme.spacing(),
|
|
||||||
paddingRight: theme.spacing(1)
|
|
||||||
},
|
},
|
||||||
colHeader: {
|
col: {
|
||||||
...theme.typography.body1,
|
...theme.typography.body1,
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
colName: {
|
colHeader: {
|
||||||
"&&": {
|
...theme.typography.body1,
|
||||||
paddingLeft: "0 !important"
|
fontSize: 14,
|
||||||
|
paddingTop: theme.spacing(3)
|
||||||
},
|
},
|
||||||
|
colName: {
|
||||||
"&:not($colHeader)": {
|
"&:not($colHeader)": {
|
||||||
paddingTop: theme.spacing(2)
|
paddingTop: theme.spacing(2)
|
||||||
}
|
},
|
||||||
|
paddingLeft: theme.spacing(3)
|
||||||
},
|
},
|
||||||
colPrice: {},
|
colPrice: {},
|
||||||
colSku: {},
|
colSku: {},
|
||||||
|
@ -66,26 +91,25 @@ const useStyles = makeStyles(
|
||||||
delete: {
|
delete: {
|
||||||
marginTop: theme.spacing(0.5)
|
marginTop: theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
errorRow: {},
|
|
||||||
hr: {
|
hr: {
|
||||||
marginBottom: theme.spacing(),
|
gridColumn: props => `span ${4 + props.data.variants[0].stocks.length}`
|
||||||
marginTop: theme.spacing(0.5)
|
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
"& input": {
|
"& input": {
|
||||||
padding: "16px 12px 17px"
|
padding: "16px 12px 17px"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
marginTop: theme.spacing(0.5)
|
summary: {
|
||||||
},
|
columnGap: theme.spacing(3),
|
||||||
row: {
|
|
||||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "1fr 180px 120px 180px 64px",
|
gridTemplateColumns: props =>
|
||||||
padding: theme.spacing(1, 0)
|
`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,29 +132,29 @@ function getVariantName(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> = props => {
|
const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps> = props => {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
currencySymbol,
|
currencySymbol,
|
||||||
data,
|
data,
|
||||||
errors,
|
errors,
|
||||||
|
warehouses,
|
||||||
onVariantDataChange,
|
onVariantDataChange,
|
||||||
onVariantDelete
|
onVariantDelete,
|
||||||
|
onVariantStockDataChange
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Card className={classes.card}>
|
||||||
<Typography color="textSecondary" variant="h5">
|
<CardTitle
|
||||||
<FormattedMessage
|
title={intl.formatMessage({
|
||||||
defaultMessage="You will create variants below"
|
defaultMessage: "Created Variants",
|
||||||
description="header"
|
description: "variant creator summary card header"
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</Typography>
|
<div className={classes.summary}>
|
||||||
<Hr className={classes.hr} />
|
|
||||||
<div>
|
|
||||||
<div className={classes.row}>
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
classes.col,
|
classes.col,
|
||||||
|
@ -155,28 +179,25 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
description="variant price"
|
description="variant price"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{data.warehouses.map(warehouseId => (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
classes.col,
|
classes.col,
|
||||||
classes.colHeader,
|
classes.colHeader,
|
||||||
classes.colStock
|
classes.colStock
|
||||||
)}
|
)}
|
||||||
|
key={warehouseId}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
{warehouses.find(warehouse => warehouse.id === warehouseId).name}
|
||||||
defaultMessage="Inventory"
|
|
||||||
description="variant stock amount"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(classes.col, classes.colHeader, classes.colSku)}
|
||||||
classes.col,
|
|
||||||
classes.colHeader,
|
|
||||||
classes.colSku
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<FormattedMessage defaultMessage="SKU" />
|
<FormattedMessage defaultMessage="SKU" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className={classNames(classes.col, classes.colHeader)} />
|
||||||
|
<Hr className={classes.hr} />
|
||||||
{data.variants.map((variant, variantIndex) => {
|
{data.variants.map((variant, variantIndex) => {
|
||||||
const variantErrors = errors.filter(
|
const variantErrors = errors.filter(
|
||||||
error => error.index === variantIndex
|
error => error.index === variantIndex
|
||||||
|
@ -187,10 +208,7 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<React.Fragment
|
||||||
className={classNames(classes.row, {
|
|
||||||
[classes.errorRow]: variantErrors.length > 0
|
|
||||||
})}
|
|
||||||
key={variant.attributes
|
key={variant.attributes
|
||||||
.map(attribute => attribute.values[0])
|
.map(attribute => attribute.values[0])
|
||||||
.join(":")}
|
.join(":")}
|
||||||
|
@ -203,7 +221,7 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
style={{
|
style={{
|
||||||
color: colors[valueIndex % colors.length]
|
color: colors[valueIndex % colors.length]
|
||||||
}}
|
}}
|
||||||
key={value}
|
key={`${value}:${valueIndex}`}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
|
@ -236,7 +254,11 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames(classes.col, classes.colStock)}>
|
{variant.stocks.map(stock => (
|
||||||
|
<div
|
||||||
|
className={classNames(classes.col, classes.colStock)}
|
||||||
|
key={stock.warehouse}
|
||||||
|
>
|
||||||
<TextField
|
<TextField
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
error={!!variantFormErrors.quantity}
|
error={!!variantFormErrors.quantity}
|
||||||
|
@ -249,16 +271,17 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
type: "number"
|
type: "number"
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
value={variant.quantity}
|
value={stock.quantity}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
onVariantDataChange(
|
onVariantStockDataChange(
|
||||||
variantIndex,
|
variantIndex,
|
||||||
"stock",
|
stock.warehouse,
|
||||||
event.target.value
|
event.target.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div className={classNames(classes.col, classes.colSku)}>
|
<div className={classNames(classes.col, classes.colSku)}>
|
||||||
<TextField
|
<TextField
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
|
@ -283,13 +306,16 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{variantIndex !== data.variants.length - 1 && (
|
||||||
|
<Hr className={classes.hr} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProductVariantCreateSummary.displayName = "ProductVariantCreateSummary";
|
ProductVariantCreatorSummary.displayName = "ProductVariantCreatorSummary";
|
||||||
export default ProductVariantCreateSummary;
|
export default ProductVariantCreatorSummary;
|
|
@ -4,11 +4,11 @@ import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IntlShape, useIntl } from "react-intl";
|
import { IntlShape, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { ProductVariantCreateStep } from "./types";
|
import { ProductVariantCreatorStep } from "./types";
|
||||||
|
|
||||||
interface Step {
|
interface Step {
|
||||||
label: string;
|
label: string;
|
||||||
value: ProductVariantCreateStep;
|
value: ProductVariantCreatorStep;
|
||||||
}
|
}
|
||||||
function getSteps(intl: IntlShape): Step[] {
|
function getSteps(intl: IntlShape): Step[] {
|
||||||
return [
|
return [
|
||||||
|
@ -17,21 +17,21 @@ function getSteps(intl: IntlShape): Step[] {
|
||||||
defaultMessage: "Select Values",
|
defaultMessage: "Select Values",
|
||||||
description: "attribute values, variant creation step"
|
description: "attribute values, variant creation step"
|
||||||
}),
|
}),
|
||||||
value: "values"
|
value: ProductVariantCreatorStep.values
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: intl.formatMessage({
|
||||||
defaultMessage: "Prices and SKU",
|
defaultMessage: "Prices and SKU",
|
||||||
description: "variant creation step"
|
description: "variant creation step"
|
||||||
}),
|
}),
|
||||||
value: "prices"
|
value: ProductVariantCreatorStep.prices
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: intl.formatMessage({
|
||||||
defaultMessage: "Summary",
|
defaultMessage: "Summary",
|
||||||
description: "variant creation step"
|
description: "variant creation step"
|
||||||
}),
|
}),
|
||||||
value: "summary"
|
value: ProductVariantCreatorStep.summary
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -62,18 +62,16 @@ const useStyles = makeStyles(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "ProductVariantCreateTabs"
|
name: "ProductVariantCreatorTabs"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ProductVariantCreateTabsProps {
|
export interface ProductVariantCreatorTabsProps {
|
||||||
step: ProductVariantCreateStep;
|
step: ProductVariantCreatorStep;
|
||||||
onStepClick: (step: ProductVariantCreateStep) => void;
|
onStepClick: (step: ProductVariantCreatorStep) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantCreateTabs: React.FC<
|
const ProductVariantCreatorTabs: React.FC<ProductVariantCreatorTabsProps> = props => {
|
||||||
ProductVariantCreateTabsProps
|
|
||||||
> = props => {
|
|
||||||
const { step: currentStep, onStepClick } = props;
|
const { step: currentStep, onStepClick } = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -104,5 +102,5 @@ const ProductVariantCreateTabs: React.FC<
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProductVariantCreateTabs.displayName = "ProductVariantCreateTabs";
|
ProductVariantCreatorTabs.displayName = "ProductVariantCreatorTabs";
|
||||||
export default ProductVariantCreateTabs;
|
export default ProductVariantCreatorTabs;
|
|
@ -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<ProductVariantCreatorValuesProps> = props => {
|
||||||
|
const { attributes, data, onValueClick } = props;
|
||||||
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{attributes.map(attribute => (
|
||||||
|
<React.Fragment key={attribute.id}>
|
||||||
|
<Card>
|
||||||
|
<CardTitle title={attribute?.name || <Skeleton />} />
|
||||||
|
<CardContent className={classes.valueContainer}>
|
||||||
|
{attribute.values.map(value => (
|
||||||
|
<Debounce
|
||||||
|
debounceFn={() => onValueClick(attribute.id, value.slug)}
|
||||||
|
time={100}
|
||||||
|
key={value.slug}
|
||||||
|
>
|
||||||
|
{change => (
|
||||||
|
<ControlledCheckbox
|
||||||
|
checked={isSelected(
|
||||||
|
value.slug,
|
||||||
|
data.attributes.find(
|
||||||
|
dataAttribute => attribute.id === dataAttribute.id
|
||||||
|
)?.values || [],
|
||||||
|
(a, b) => a === b
|
||||||
|
)}
|
||||||
|
name={`value:${value.slug}`}
|
||||||
|
label={value.name}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Debounce>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<CardSpacer />
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductVariantCreatorValues.displayName = "ProductVariantCreatorValues";
|
||||||
|
export default ProductVariantCreatorValues;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue