commit
2b0f2933ec
127 changed files with 6869 additions and 1625 deletions
|
@ -21,3 +21,4 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- UI improvements - #166 by @benekex2
|
||||
- Fix en locale matching - #165 by @dominik-zeglen
|
||||
- Implement the Credential Management API - #158 by @patrys
|
||||
- Add search bars - #172 by @dominik-zeglen
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2019-09-10T11:00:36.829Z\n"
|
||||
"POT-Creation-Date: 2019-09-12T15:12:49.543Z\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -455,8 +455,8 @@ msgctxt "button"
|
|||
msgid "Add staff member"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryList/CategoryList.json
|
||||
#. [src.categories.components.CategoryList.435697837] - button
|
||||
#: build/locale/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.json
|
||||
#. [src.categories.components.CategoryUpdatePage.435697837] - button
|
||||
#. defaultMessage is:
|
||||
#. Add subcategory
|
||||
msgctxt "button"
|
||||
|
@ -627,6 +627,46 @@ msgctxt "tax rate"
|
|||
msgid "Agricultural supplies"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeListPage/AttributeListPage.json
|
||||
#. [src.attributes.components.AttributeListPage.2417065806] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Attributes
|
||||
msgctxt "tab name"
|
||||
msgid "All Attributes"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryListPage/CategoryListPage.json
|
||||
#. [src.categories.components.CategoryListPage.4294878092] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Categories
|
||||
msgctxt "tab name"
|
||||
msgid "All Categories"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/components/CollectionListPage/CollectionListPage.json
|
||||
#. [src.collections.components.CollectionListPage.1631917001] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Collections
|
||||
msgctxt "tab name"
|
||||
msgid "All Collections"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/components/CustomerListPage/CustomerListPage.json
|
||||
#. [src.customers.components.CustomerListPage.477293306] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Customers
|
||||
msgctxt "tab name"
|
||||
msgid "All Customers"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderDraftListPage/OrderDraftListPage.json
|
||||
#. [src.orders.components.OrderDraftListPage.551325728] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Drafts
|
||||
msgctxt "tab name"
|
||||
msgid "All Drafts"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListPage/OrderListPage.json
|
||||
#. [src.orders.components.OrderListPage.875489544] - tab name
|
||||
#. defaultMessage is:
|
||||
|
@ -643,22 +683,54 @@ msgctxt "section header"
|
|||
msgid "All Photos"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListPage/ProductListPage.json
|
||||
#. [src.products.components.ProductListPage.821159718] - tab name
|
||||
#: build/locale/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.json
|
||||
#. [src.productTypes.components.ProductTypeListPage.1776073799] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Product Types
|
||||
msgctxt "tab name"
|
||||
msgid "All Product Types"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.821159718] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Products
|
||||
msgctxt "tab name"
|
||||
msgid "All Products"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryList/CategoryList.json
|
||||
#. [src.categories.components.CategoryList.3229914152] - section header
|
||||
#: build/locale/src/discounts/components/SaleListPage/SaleListPage.json
|
||||
#. [src.discounts.components.SaleListPage.3907768880] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Sales
|
||||
msgctxt "tab name"
|
||||
msgid "All Sales"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffListPage/StaffListPage.json
|
||||
#. [src.staff.components.StaffListPage.2852350932] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Staff Members
|
||||
msgctxt "tab name"
|
||||
msgid "All Staff Members"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.json
|
||||
#. [src.categories.components.CategoryUpdatePage.3229914152] - section header
|
||||
#. defaultMessage is:
|
||||
#. All Subcategories
|
||||
msgctxt "section header"
|
||||
msgid "All Subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/components/VoucherListPage/VoucherListPage.json
|
||||
#. [src.discounts.components.VoucherListPage.1112241061] - tab name
|
||||
#. defaultMessage is:
|
||||
#. All Vouchers
|
||||
msgctxt "tab name"
|
||||
msgid "All Vouchers"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/taxes/components/TaxConfiguration/TaxConfiguration.json
|
||||
#. [src.taxes.components.TaxConfiguration.142803418]
|
||||
#. defaultMessage is:
|
||||
|
@ -815,10 +887,6 @@ msgstr ""
|
|||
#. [src.categories.views.299584400]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} categories}}?
|
||||
#: build/locale/src/categories/views/CategoryList.json
|
||||
#. [src.categories.views.844574071]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} categories}}?
|
||||
msgctxt "description"
|
||||
msgid "Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} categories}}?"
|
||||
msgstr ""
|
||||
|
@ -831,16 +899,24 @@ msgctxt "description"
|
|||
msgid "Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} products}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.2497542455]
|
||||
#: build/locale/src/categories/views/CategoryList/CategoryList.json
|
||||
#. [src.categories.views.CategoryList.2144707585]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?
|
||||
msgctxt "description"
|
||||
msgid "Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.2497542455]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this collection} other{{displayQuantity} collections}}?
|
||||
msgctxt "description"
|
||||
msgid "Are you sure you want to delete {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/views/CustomerList.json
|
||||
#. [src.customers.views.409347866]
|
||||
#: build/locale/src/customers/views/CustomerList/CustomerList.json
|
||||
#. [src.customers.views.CustomerList.409347866]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this customer} other{{displayQuantity} customers}}?
|
||||
msgctxt "description"
|
||||
|
@ -855,8 +931,8 @@ msgctxt "description"
|
|||
msgid "Are you sure you want to delete {counter,plural,one{this menu} other{{displayQuantity} menus}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/views/OrderDraftList.json
|
||||
#. [src.orders.views.1389231130] - dialog content
|
||||
#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
|
||||
#. [src.orders.views.OrderDraftList.1389231130] - dialog content
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this order draft} other{{displayQuantity} orderDrafts}}?
|
||||
msgctxt "dialog content"
|
||||
|
@ -871,8 +947,8 @@ msgctxt "dialog content"
|
|||
msgid "Are you sure you want to delete {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/productTypes/views/ProductTypeList.json
|
||||
#. [src.productTypes.views.2294091098] - dialog content
|
||||
#: build/locale/src/productTypes/views/ProductTypeList/ProductTypeList.json
|
||||
#. [src.productTypes.views.ProductTypeList.2294091098] - dialog content
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this product type} other{{displayQuantity} product types}}?
|
||||
msgctxt "dialog content"
|
||||
|
@ -887,8 +963,8 @@ msgctxt "dialog content"
|
|||
msgid "Are you sure you want to delete {counter,plural,one{this product} other{{displayQuantity} products}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/views/SaleList.json
|
||||
#. [src.discounts.views.2516361175] - dialog content
|
||||
#: build/locale/src/discounts/views/SaleList/SaleList.json
|
||||
#. [src.discounts.views.SaleList.2516361175] - dialog content
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this sale} other{{displayQuantity} sales}}?
|
||||
msgctxt "dialog content"
|
||||
|
@ -911,8 +987,8 @@ msgctxt "dialog content"
|
|||
msgid "Are you sure you want to delete {counter,plural,one{this variant} other{{displayQuantity} variants}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/views/VoucherList.json
|
||||
#. [src.discounts.views.1791926983] - dialog content
|
||||
#: build/locale/src/discounts/views/VoucherList/VoucherList.json
|
||||
#. [src.discounts.views.VoucherList.1791926983] - dialog content
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to delete {counter,plural,one{this voucher} other{{displayQuantity} vouchers}}?
|
||||
msgctxt "dialog content"
|
||||
|
@ -1031,8 +1107,8 @@ msgctxt "description"
|
|||
msgid "Are you sure you want to mark this order as paid?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.1348793822]
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.1348793822]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to publish {counter,plural,one{this collection} other{{displayQuantity} collections}}?
|
||||
msgctxt "description"
|
||||
|
@ -1155,8 +1231,8 @@ msgctxt "dialog content"
|
|||
msgid "Are you sure you want to unassign {counter,plural,one{this product} other{{displayQuantity} products}}?"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.3944356444]
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.3944356444]
|
||||
#. defaultMessage is:
|
||||
#. Are you sure you want to unpublish {counter,plural,one{this collection} other{{displayQuantity} collections}}?
|
||||
msgctxt "description"
|
||||
|
@ -1439,10 +1515,6 @@ msgctxt "description"
|
|||
msgid "Availability"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2157131639] - product status
|
||||
#. defaultMessage is:
|
||||
#. Available
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.2157131639] - product status
|
||||
#. defaultMessage is:
|
||||
|
@ -2319,6 +2391,10 @@ msgstr ""
|
|||
#. [src.components.FilterBar.2340527467]
|
||||
#. defaultMessage is:
|
||||
#. Custom Filter
|
||||
#: build/locale/src/components/SearchBar/SearchBar.json
|
||||
#. [src.components.SearchBar.2340527467]
|
||||
#. defaultMessage is:
|
||||
#. Custom Filter
|
||||
msgctxt "description"
|
||||
msgid "Custom Filter"
|
||||
msgstr ""
|
||||
|
@ -2571,8 +2647,8 @@ msgctxt "dialog title"
|
|||
msgid "Delete Collection"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/views/OrderDraftList.json
|
||||
#. [src.orders.views.1161115149] - dialog header
|
||||
#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
|
||||
#. [src.orders.views.OrderDraftList.1161115149] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Delete Order Drafts
|
||||
msgctxt "dialog header"
|
||||
|
@ -2611,8 +2687,8 @@ msgctxt "dialog header"
|
|||
msgid "Delete Product Type"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/productTypes/views/ProductTypeList.json
|
||||
#. [src.productTypes.views.4080551769] - dialog header
|
||||
#: build/locale/src/productTypes/views/ProductTypeList/ProductTypeList.json
|
||||
#. [src.productTypes.views.ProductTypeList.4080551769] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Delete Product Types
|
||||
msgctxt "dialog header"
|
||||
|
@ -2643,8 +2719,8 @@ msgctxt "dialog header"
|
|||
msgid "Delete Sale"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/views/SaleList.json
|
||||
#. [src.discounts.views.2809303671] - dialog header
|
||||
#: build/locale/src/discounts/views/SaleList/SaleList.json
|
||||
#. [src.discounts.views.SaleList.2809303671] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Delete Sales
|
||||
msgctxt "dialog header"
|
||||
|
@ -2659,6 +2735,10 @@ msgctxt "custom search delete, dialog header"
|
|||
msgid "Delete Search"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/Filter/FilterSearch.json
|
||||
#. [src.components.Filter.2173195312] - button
|
||||
#. defaultMessage is:
|
||||
#. Delete Search
|
||||
#: build/locale/src/components/TableFilter/FilterChips.json
|
||||
#. [src.components.TableFilter.2173195312] - button
|
||||
#. defaultMessage is:
|
||||
|
@ -2727,8 +2807,8 @@ msgctxt "dialog header"
|
|||
msgid "Delete Voucher"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/views/VoucherList.json
|
||||
#. [src.discounts.views.367317371] - dialog header
|
||||
#: build/locale/src/discounts/views/VoucherList/VoucherList.json
|
||||
#. [src.discounts.views.VoucherList.367317371] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Delete Vouchers
|
||||
msgctxt "dialog header"
|
||||
|
@ -2763,8 +2843,8 @@ msgstr ""
|
|||
#. [src.categories.views.712767046] - dialog title
|
||||
#. defaultMessage is:
|
||||
#. Delete categories
|
||||
#: build/locale/src/categories/views/CategoryList.json
|
||||
#. [src.categories.views.712767046] - dialog title
|
||||
#: build/locale/src/categories/views/CategoryList/CategoryList.json
|
||||
#. [src.categories.views.CategoryList.712767046] - dialog title
|
||||
#. defaultMessage is:
|
||||
#. Delete categories
|
||||
msgctxt "dialog title"
|
||||
|
@ -2783,8 +2863,8 @@ msgctxt "dialog title"
|
|||
msgid "Delete category"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.3817188998] - dialog title
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.3817188998] - dialog title
|
||||
#. defaultMessage is:
|
||||
#. Delete collections
|
||||
msgctxt "dialog title"
|
||||
|
@ -2799,8 +2879,8 @@ msgctxt "dialog header"
|
|||
msgid "Delete customer"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/views/CustomerList.json
|
||||
#. [src.customers.views.1946482599] - dialog header
|
||||
#: build/locale/src/customers/views/CustomerList/CustomerList.json
|
||||
#. [src.customers.views.CustomerList.1946482599] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Delete customers
|
||||
msgctxt "dialog header"
|
||||
|
@ -3551,18 +3631,6 @@ msgctxt "subheader"
|
|||
msgid "Here is some information we gathered about your store"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.77815154] - product is hidden
|
||||
#. defaultMessage is:
|
||||
#. Hidden
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.77815154] - product is hidden
|
||||
#. defaultMessage is:
|
||||
#. Hidden
|
||||
msgctxt "product is hidden"
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.77815154]
|
||||
#. defaultMessage is:
|
||||
|
@ -3571,6 +3639,14 @@ msgctxt "description"
|
|||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.77815154] - product is hidden
|
||||
#. defaultMessage is:
|
||||
#. Hidden
|
||||
msgctxt "product is hidden"
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/views/ProductList/filters.json
|
||||
#. [src.products.views.ProductList.hidden] - filter products by visibility
|
||||
#. defaultMessage is:
|
||||
|
@ -4895,8 +4971,8 @@ msgctxt "order history message"
|
|||
msgid "Order confirmation was sent to customer"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/views/OrderDraftList.json
|
||||
#. [src.orders.views.1872939752]
|
||||
#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
|
||||
#. [src.orders.views.OrderDraftList.1872939752]
|
||||
#. defaultMessage is:
|
||||
#. Order draft succesfully created
|
||||
#: build/locale/src/orders/views/OrderList/OrderList.json
|
||||
|
@ -5035,10 +5111,6 @@ msgctxt "description"
|
|||
msgid "Original String"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1640493122] - product status
|
||||
#. defaultMessage is:
|
||||
#. Out Of Stock
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1640493122] - product status
|
||||
#. defaultMessage is:
|
||||
|
@ -5375,18 +5447,6 @@ msgctxt "order payment"
|
|||
msgid "Preauthorized amount"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
msgctxt "description"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryProductList/CategoryProductList.json
|
||||
#. [src.categories.components.CategoryProductList.1134347598] - product price
|
||||
#. defaultMessage is:
|
||||
|
@ -5435,6 +5495,14 @@ msgctxt "product unit price"
|
|||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
msgctxt "description"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariants/ProductVariants.json
|
||||
#. [src.products.components.ProductVariants.1134347598] - product variant price
|
||||
#. defaultMessage is:
|
||||
|
@ -5747,8 +5815,8 @@ msgctxt "description"
|
|||
msgid "Provided email address does not exist in our database."
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.1547167026] - publish collections
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.1547167026] - publish collections
|
||||
#. defaultMessage is:
|
||||
#. Publish
|
||||
msgctxt "publish collections"
|
||||
|
@ -5787,8 +5855,8 @@ msgctxt "dialog header"
|
|||
msgid "Publish Products"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.2823425739] - dialog title
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.2823425739] - dialog title
|
||||
#. defaultMessage is:
|
||||
#. Publish collections
|
||||
msgctxt "dialog title"
|
||||
|
@ -5919,10 +5987,6 @@ msgctxt "description"
|
|||
msgid "Quick Pick"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2545228781]
|
||||
#. defaultMessage is:
|
||||
#. Range
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.2545228781]
|
||||
#. defaultMessage is:
|
||||
|
@ -6031,8 +6095,8 @@ msgstr ""
|
|||
#. [src.categories.views.3488150607]
|
||||
#. defaultMessage is:
|
||||
#. Remember this will also delete all products assigned to this category.
|
||||
#: build/locale/src/categories/views/CategoryList.json
|
||||
#. [src.categories.views.3488150607]
|
||||
#: build/locale/src/categories/views/CategoryList/CategoryList.json
|
||||
#. [src.categories.views.CategoryList.3488150607]
|
||||
#. defaultMessage is:
|
||||
#. Remember this will also delete all products assigned to this category.
|
||||
msgctxt "description"
|
||||
|
@ -6071,8 +6135,8 @@ msgctxt "unassign country, dialog header"
|
|||
msgid "Remove from shipping zone"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/views/OrderDraftList.json
|
||||
#. [src.orders.views.3880993240]
|
||||
#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
|
||||
#. [src.orders.views.OrderDraftList.3880993240]
|
||||
#. defaultMessage is:
|
||||
#. Removed draft orders
|
||||
msgctxt "description"
|
||||
|
@ -6243,14 +6307,10 @@ msgctxt "button"
|
|||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/SaveFilterTabDialog/SaveFilterTabDialog.json
|
||||
#. [src.components.SaveFilterTabDialog.1514415736] - save filter tab, header
|
||||
#: build/locale/src/components/Filter/FilterSearch.json
|
||||
#. [src.components.Filter.1514415736] - button
|
||||
#. defaultMessage is:
|
||||
#. Save Custom Search
|
||||
msgctxt "save filter tab, header"
|
||||
msgid "Save Custom Search"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/TableFilter/FilterChips.json
|
||||
#. [src.components.TableFilter.1514415736] - button
|
||||
#. defaultMessage is:
|
||||
|
@ -6259,6 +6319,14 @@ msgctxt "button"
|
|||
msgid "Save Custom Search"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/SaveFilterTabDialog/SaveFilterTabDialog.json
|
||||
#. [src.components.SaveFilterTabDialog.1514415736] - save filter tab, header
|
||||
#. defaultMessage is:
|
||||
#. Save Custom Search
|
||||
msgctxt "save filter tab, header"
|
||||
msgid "Save Custom Search"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.json
|
||||
#. [src.products.components.ProductVariantCreatePage.2853608829] - button
|
||||
#. defaultMessage is:
|
||||
|
@ -6275,6 +6343,14 @@ msgctxt "description"
|
|||
msgid "Saved changes"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeListPage/AttributeListPage.json
|
||||
#. [src.attributes.components.AttributeListPage.3916653510]
|
||||
#. defaultMessage is:
|
||||
#. Search Attribute
|
||||
msgctxt "description"
|
||||
msgid "Search Attribute"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.json
|
||||
#. [src.productTypes.components.AssignAttributeDialog.902296540]
|
||||
#. defaultMessage is:
|
||||
|
@ -6291,10 +6367,30 @@ msgctxt "description"
|
|||
msgid "Search Categories"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryListPage/CategoryListPage.json
|
||||
#. [src.categories.components.CategoryListPage.3841025483]
|
||||
#. defaultMessage is:
|
||||
#. Search Category
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.3841025483]
|
||||
#. defaultMessage is:
|
||||
#. Search Category
|
||||
msgctxt "description"
|
||||
msgid "Search Category"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/components/CollectionListPage/CollectionListPage.json
|
||||
#. [src.collections.components.CollectionListPage.4057224233]
|
||||
#. defaultMessage is:
|
||||
#. Search Collection
|
||||
#: build/locale/src/components/AssignCollectionDialog/AssignCollectionDialog.json
|
||||
#. [src.components.AssignCollectionDialog.4057224233]
|
||||
#. defaultMessage is:
|
||||
#. Search Collection
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.4057224233]
|
||||
#. defaultMessage is:
|
||||
#. Search Collection
|
||||
msgctxt "description"
|
||||
msgid "Search Collection"
|
||||
msgstr ""
|
||||
|
@ -6307,6 +6403,14 @@ msgctxt "description"
|
|||
msgid "Search Countries"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/components/CustomerListPage/CustomerListPage.json
|
||||
#. [src.customers.components.CustomerListPage.1643417013]
|
||||
#. defaultMessage is:
|
||||
#. Search Customer
|
||||
msgctxt "description"
|
||||
msgid "Search Customer"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderCustomer/OrderCustomer.json
|
||||
#. [src.orders.components.OrderCustomer.2433460203]
|
||||
#. defaultMessage is:
|
||||
|
@ -6315,6 +6419,14 @@ msgctxt "description"
|
|||
msgid "Search Customers"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderDraftListPage/OrderDraftListPage.json
|
||||
#. [src.orders.components.OrderDraftListPage.77765281]
|
||||
#. defaultMessage is:
|
||||
#. Search Draft
|
||||
msgctxt "description"
|
||||
msgid "Search Draft"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/translations/components/TranslationsCategoriesPage/TranslationsCategoriesPage.json
|
||||
#. [src.translations.components.TranslationsCategoriesPage.1406947243]
|
||||
#. defaultMessage is:
|
||||
|
@ -6395,6 +6507,34 @@ msgctxt "description"
|
|||
msgid "Search Orders..."
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.2559018090]
|
||||
#. defaultMessage is:
|
||||
#. Search Page
|
||||
msgctxt "description"
|
||||
msgid "Search Page"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.2105464697]
|
||||
#. defaultMessage is:
|
||||
#. Search Product
|
||||
msgctxt "description"
|
||||
msgid "Search Product"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.json
|
||||
#. [src.productTypes.components.ProductTypeListPage.3420445375]
|
||||
#. defaultMessage is:
|
||||
#. Search Product Type
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.3420445375]
|
||||
#. defaultMessage is:
|
||||
#. Search Product Type
|
||||
msgctxt "description"
|
||||
msgid "Search Product Type"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/AssignProductDialog/AssignProductDialog.json
|
||||
#. [src.components.AssignProductDialog.2850255786]
|
||||
#. defaultMessage is:
|
||||
|
@ -6407,14 +6547,46 @@ msgctxt "description"
|
|||
msgid "Search Products"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListPage/ProductListPage.json
|
||||
#. [src.products.components.ProductListPage.3550330425]
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3550330425]
|
||||
#. defaultMessage is:
|
||||
#. Search Products...
|
||||
msgctxt "description"
|
||||
msgid "Search Products..."
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/components/SaleListPage/SaleListPage.json
|
||||
#. [src.discounts.components.SaleListPage.1866913828]
|
||||
#. defaultMessage is:
|
||||
#. Search Sale
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.1866913828]
|
||||
#. defaultMessage is:
|
||||
#. Search Sale
|
||||
msgctxt "description"
|
||||
msgid "Search Sale"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffListPage/StaffListPage.json
|
||||
#. [src.staff.components.StaffListPage.61043583]
|
||||
#. defaultMessage is:
|
||||
#. Search Staff Member
|
||||
msgctxt "description"
|
||||
msgid "Search Staff Member"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/discounts/components/VoucherListPage/VoucherListPage.json
|
||||
#. [src.discounts.components.VoucherListPage.1930485532]
|
||||
#. defaultMessage is:
|
||||
#. Search Voucher
|
||||
#: build/locale/src/translations/components/TranslationsEntitiesListPage/TranslationsEntitiesListPage.json
|
||||
#. [src.translations.components.TranslationsEntitiesListPage.1930485532]
|
||||
#. defaultMessage is:
|
||||
#. Search Voucher
|
||||
msgctxt "description"
|
||||
msgid "Search Voucher"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.json
|
||||
#. [src.productTypes.components.AssignAttributeDialog.524117994]
|
||||
#. defaultMessage is:
|
||||
|
@ -6519,8 +6691,8 @@ msgctxt "description"
|
|||
msgid "Select all orders where:"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListPage/ProductListPage.json
|
||||
#. [src.products.components.ProductListPage.1421689426]
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1421689426]
|
||||
#. defaultMessage is:
|
||||
#. Select all products where:
|
||||
msgctxt "description"
|
||||
|
@ -6835,10 +7007,6 @@ msgctxt "description"
|
|||
msgid "Specific Date"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2844426531]
|
||||
#. defaultMessage is:
|
||||
#. Specific Price
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.2844426531]
|
||||
#. defaultMessage is:
|
||||
|
@ -6919,18 +7087,6 @@ msgctxt "voucher is active from date"
|
|||
msgid "Starts"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
msgctxt "product status"
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/components/CustomerOrders/CustomerOrders.json
|
||||
#. [src.customers.components.CustomerOrders.1756106276] - order status
|
||||
#. defaultMessage is:
|
||||
|
@ -6955,6 +7111,14 @@ msgctxt "plugin status"
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
msgctxt "product status"
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariants/ProductVariants.json
|
||||
#. [src.products.components.ProductVariants.1756106276] - product variant status
|
||||
#. defaultMessage is:
|
||||
|
@ -6963,10 +7127,6 @@ msgctxt "product variant status"
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3841616483] - product stock
|
||||
#. defaultMessage is:
|
||||
#. Stock
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3841616483] - product stock
|
||||
#. defaultMessage is:
|
||||
|
@ -6983,10 +7143,6 @@ msgctxt "product variant stock, section header"
|
|||
msgid "Stock"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3645081351]
|
||||
#. defaultMessage is:
|
||||
#. Stock quantity
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3645081351]
|
||||
#. defaultMessage is:
|
||||
|
@ -7815,8 +7971,8 @@ msgctxt "payment status"
|
|||
msgid "Unpaid"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.2237014112] - unpublish collections
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.2237014112] - unpublish collections
|
||||
#. defaultMessage is:
|
||||
#. Unpublish
|
||||
msgctxt "unpublish collections"
|
||||
|
@ -7855,8 +8011,8 @@ msgctxt "dialog header"
|
|||
msgid "Unpublish Products"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/collections/views/CollectionList.json
|
||||
#. [src.collections.views.2637364047] - dialog title
|
||||
#: build/locale/src/collections/views/CollectionList/CollectionList.json
|
||||
#. [src.collections.views.CollectionList.2637364047] - dialog title
|
||||
#. defaultMessage is:
|
||||
#. Unpublish collections
|
||||
msgctxt "dialog title"
|
||||
|
@ -8155,18 +8311,6 @@ msgctxt "description"
|
|||
msgid "View and update your site settings"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
msgctxt "product visibility"
|
||||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.1459686496] - section header
|
||||
#. defaultMessage is:
|
||||
|
@ -8183,6 +8327,14 @@ msgctxt "page status"
|
|||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
msgctxt "product visibility"
|
||||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeList/AttributeList.json
|
||||
#. [src.attributes.components.AttributeList.643174786] - attribute is visible
|
||||
#. defaultMessage is:
|
||||
|
@ -8191,18 +8343,6 @@ msgctxt "attribute is visible"
|
|||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.643174786] - product is visible
|
||||
#. defaultMessage is:
|
||||
#. Visible
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.643174786] - product is visible
|
||||
#. defaultMessage is:
|
||||
#. Visible
|
||||
msgctxt "product is visible"
|
||||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.643174786]
|
||||
#. defaultMessage is:
|
||||
|
@ -8211,6 +8351,14 @@ msgctxt "description"
|
|||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.643174786] - product is visible
|
||||
#. defaultMessage is:
|
||||
#. Visible
|
||||
msgctxt "product is visible"
|
||||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeProperties/AttributeProperties.json
|
||||
#. [src.attributes.components.AttributeProperties.3876764312] - attribute
|
||||
#. defaultMessage is:
|
||||
|
@ -8359,18 +8507,6 @@ msgctxt "order does not require shipping"
|
|||
msgid "does not apply"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3477667254] - product price
|
||||
#. defaultMessage is:
|
||||
#. equals
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3477667254] - product price
|
||||
#. defaultMessage is:
|
||||
#. equals
|
||||
msgctxt "product price"
|
||||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.3477667254]
|
||||
#. defaultMessage is:
|
||||
|
@ -8379,6 +8515,14 @@ msgctxt "description"
|
|||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3477667254] - product price
|
||||
#. defaultMessage is:
|
||||
#. equals
|
||||
msgctxt "product price"
|
||||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/Filter/FilterElement.json
|
||||
#. [src.components.Filter.2755325844]
|
||||
#. defaultMessage is:
|
||||
|
@ -8403,18 +8547,6 @@ msgctxt "weight"
|
|||
msgid "from {value} {unit}"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1438173764] - product status is set as
|
||||
#. defaultMessage is:
|
||||
#. is set as
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1438173764] - product status is set as
|
||||
#. defaultMessage is:
|
||||
#. is set as
|
||||
msgctxt "product status is set as"
|
||||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.1438173764] - date is set as
|
||||
#. defaultMessage is:
|
||||
|
@ -8423,6 +8555,14 @@ msgctxt "date is set as"
|
|||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1438173764] - product status is set as
|
||||
#. defaultMessage is:
|
||||
#. is set as
|
||||
msgctxt "product status is set as"
|
||||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/views/StaffDetails.json
|
||||
#. [src.staff.views.2240444792] - dialog header
|
||||
#. defaultMessage is:
|
||||
|
|
108
schema.graphql
108
schema.graphql
|
@ -466,6 +466,10 @@ type CategoryDelete {
|
|||
category: Category
|
||||
}
|
||||
|
||||
input CategoryFilterInput {
|
||||
search: String
|
||||
}
|
||||
|
||||
input CategoryInput {
|
||||
description: String
|
||||
descriptionJson: JSONString
|
||||
|
@ -1627,6 +1631,10 @@ type MenuDelete {
|
|||
menu: Menu
|
||||
}
|
||||
|
||||
input MenuFilterInput {
|
||||
search: String
|
||||
}
|
||||
|
||||
input MenuInput {
|
||||
name: String
|
||||
}
|
||||
|
@ -1682,6 +1690,10 @@ type MenuItemDelete {
|
|||
menuItem: MenuItem
|
||||
}
|
||||
|
||||
input MenuItemFilterInput {
|
||||
search: String
|
||||
}
|
||||
|
||||
input MenuItemInput {
|
||||
name: String
|
||||
url: String
|
||||
|
@ -1980,6 +1992,11 @@ type Mutations {
|
|||
userBulkSetActive(ids: [ID]!, isActive: Boolean!): UserBulkSetActive
|
||||
userUpdatePrivateMetadata(id: ID!, input: MetaInput!): UserUpdatePrivateMeta
|
||||
userClearStoredPrivateMetadata(id: ID!, input: MetaPath!): UserClearStoredPrivateMeta
|
||||
serviceAccountCreate(input: ServiceAccountInput!): ServiceAccountCreate
|
||||
serviceAccountUpdate(id: ID!, input: ServiceAccountInput!): ServiceAccountUpdate
|
||||
serviceAccountDelete(id: ID!): ServiceAccountDelete
|
||||
serviceAccountUpdatePrivateMetadata(id: ID!, input: MetaInput!): ServiceAccountUpdatePrivateMeta
|
||||
serviceAccountClearStoredPrivateMetadata(id: ID!, input: MetaPath!): ServiceAccountClearStoredPrivateMeta
|
||||
passwordReset(email: String!): PasswordReset
|
||||
}
|
||||
|
||||
|
@ -2095,6 +2112,7 @@ enum OrderDirection {
|
|||
input OrderDraftFilterInput {
|
||||
customer: String
|
||||
created: DateRangeInput
|
||||
search: String
|
||||
}
|
||||
|
||||
type OrderEvent implements Node {
|
||||
|
@ -2171,12 +2189,13 @@ input OrderFilterInput {
|
|||
status: [OrderStatusFilter]
|
||||
customer: String
|
||||
created: DateRangeInput
|
||||
search: String
|
||||
}
|
||||
|
||||
type OrderLine implements Node {
|
||||
id: ID!
|
||||
productName: String!
|
||||
translatedProductName: String!
|
||||
variantName: String!
|
||||
productSku: String!
|
||||
isShippingRequired: Boolean!
|
||||
quantity: Int!
|
||||
|
@ -2186,6 +2205,8 @@ type OrderLine implements Node {
|
|||
thumbnail(size: Int): Image
|
||||
unitPrice: TaxedMoney
|
||||
variant: ProductVariant
|
||||
translatedProductName: String!
|
||||
translatedVariantName: String!
|
||||
}
|
||||
|
||||
input OrderLineCreateInput {
|
||||
|
@ -2294,6 +2315,10 @@ type PageDelete {
|
|||
page: Page
|
||||
}
|
||||
|
||||
input PageFilterInput {
|
||||
search: String
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
|
@ -2430,6 +2455,7 @@ type PermissionDisplay {
|
|||
enum PermissionEnum {
|
||||
MANAGE_USERS
|
||||
MANAGE_STAFF
|
||||
MANAGE_SERVICE_ACCOUNTS
|
||||
IMPERSONATE_USERS
|
||||
MANAGE_DISCOUNTS
|
||||
MANAGE_GIFT_CARD
|
||||
|
@ -2462,6 +2488,11 @@ type PluginCountableEdge {
|
|||
cursor: String!
|
||||
}
|
||||
|
||||
input PluginFilterInput {
|
||||
active: Boolean
|
||||
search: String
|
||||
}
|
||||
|
||||
type PluginUpdate {
|
||||
errors: [Error!]
|
||||
plugin: Plugin
|
||||
|
@ -2661,6 +2692,8 @@ enum ProductOrderField {
|
|||
PRICE
|
||||
MINIMAL_PRICE
|
||||
DATE
|
||||
TYPE
|
||||
PUBLISHED
|
||||
}
|
||||
|
||||
type ProductPricingInfo {
|
||||
|
@ -2922,7 +2955,7 @@ type Query {
|
|||
digitalContents(query: String, level: Int, before: String, after: String, first: Int, last: Int): DigitalContentCountableConnection
|
||||
attributes(query: String, inCategory: ID, inCollection: ID, filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection
|
||||
attribute(id: ID!): Attribute
|
||||
categories(query: String, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||
categories(query: String, filter: CategoryFilterInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||
category(id: ID!): Category
|
||||
collection(id: ID!): Collection
|
||||
collections(filter: CollectionFilterInput, query: String, before: String, after: String, first: Int, last: Int): CollectionCountableConnection
|
||||
|
@ -2937,7 +2970,7 @@ type Query {
|
|||
payments(before: String, after: String, first: Int, last: Int): PaymentCountableConnection
|
||||
paymentClientToken(gateway: GatewaysEnum): String
|
||||
page(id: ID, slug: String): Page
|
||||
pages(query: String, before: String, after: String, first: Int, last: Int): PageCountableConnection
|
||||
pages(query: String, filter: PageFilterInput, before: String, after: String, first: Int, last: Int): PageCountableConnection
|
||||
homepageEvents(before: String, after: String, first: Int, last: Int): OrderEventCountableConnection
|
||||
order(id: ID!): Order
|
||||
orders(filter: OrderFilterInput, query: String, created: ReportingPeriod, status: OrderStatusFilter, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
||||
|
@ -2945,13 +2978,13 @@ type Query {
|
|||
ordersTotal(period: ReportingPeriod): TaxedMoney
|
||||
orderByToken(token: String!): Order
|
||||
menu(id: ID, name: String): Menu
|
||||
menus(query: String, before: String, after: String, first: Int, last: Int): MenuCountableConnection
|
||||
menus(query: String, filter: MenuFilterInput, before: String, after: String, first: Int, last: Int): MenuCountableConnection
|
||||
menuItem(id: ID!): MenuItem
|
||||
menuItems(query: String, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
||||
menuItems(query: String, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
||||
giftCard(id: ID!): GiftCard
|
||||
giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
||||
plugin(id: ID!): Plugin
|
||||
plugins(before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
||||
plugins(filter: PluginFilterInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
||||
sale(id: ID!): Sale
|
||||
sales(filter: SaleFilterInput, query: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection
|
||||
voucher(id: ID!): Voucher
|
||||
|
@ -2965,6 +2998,8 @@ type Query {
|
|||
customers(filter: CustomerFilterInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||
me: User
|
||||
staffUsers(filter: StaffUserInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||
serviceAccounts(filter: ServiceAccountFilterInput, before: String, after: String, first: Int, last: Int): ServiceAccountCountableConnection
|
||||
serviceAccount(id: ID!): ServiceAccount
|
||||
user(id: ID!): User
|
||||
node(id: ID!): Node
|
||||
}
|
||||
|
@ -3092,6 +3127,65 @@ input SeoInput {
|
|||
description: String
|
||||
}
|
||||
|
||||
type ServiceAccount implements Node {
|
||||
id: ID!
|
||||
authToken: String
|
||||
created: DateTime
|
||||
isActive: Boolean
|
||||
privateMeta: [MetaStore]!
|
||||
meta: [MetaStore]!
|
||||
permissions: [PermissionDisplay]
|
||||
name: String
|
||||
}
|
||||
|
||||
type ServiceAccountClearStoredPrivateMeta {
|
||||
errors: [Error!]
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type ServiceAccountCountableConnection {
|
||||
pageInfo: PageInfo!
|
||||
edges: [ServiceAccountCountableEdge!]!
|
||||
totalCount: Int
|
||||
}
|
||||
|
||||
type ServiceAccountCountableEdge {
|
||||
node: ServiceAccount!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type ServiceAccountCreate {
|
||||
errors: [Error!]
|
||||
authToken: String
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type ServiceAccountDelete {
|
||||
errors: [Error!]
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
input ServiceAccountFilterInput {
|
||||
search: String
|
||||
isActive: Boolean
|
||||
}
|
||||
|
||||
input ServiceAccountInput {
|
||||
name: String
|
||||
isActive: Boolean
|
||||
permissions: [PermissionEnum]
|
||||
}
|
||||
|
||||
type ServiceAccountUpdate {
|
||||
errors: [Error!]
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type ServiceAccountUpdatePrivateMeta {
|
||||
errors: [Error!]
|
||||
serviceAccount: ServiceAccount
|
||||
}
|
||||
|
||||
type SetPassword {
|
||||
token: String
|
||||
errors: [Error]!
|
||||
|
@ -3307,6 +3401,7 @@ input StaffCreateInput {
|
|||
note: String
|
||||
permissions: [PermissionEnum]
|
||||
sendPasswordEmail: Boolean
|
||||
redirectUrl: String
|
||||
}
|
||||
|
||||
type StaffDelete {
|
||||
|
@ -3534,6 +3629,7 @@ input UserCreateInput {
|
|||
isActive: Boolean
|
||||
note: String
|
||||
sendPasswordEmail: Boolean
|
||||
redirectUrl: String
|
||||
}
|
||||
|
||||
type UserUpdateMeta {
|
||||
|
|
|
@ -4,19 +4,37 @@ import Card from "@material-ui/core/Card";
|
|||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import Container from "../../../components/Container";
|
||||
import PageHeader from "../../../components/PageHeader";
|
||||
import { ListActions, PageListProps } from "../../../types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "../../../types";
|
||||
import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
|
||||
import AttributeList from "../AttributeList/AttributeList";
|
||||
|
||||
export interface AttributeListPageProps extends PageListProps, ListActions {
|
||||
export interface AttributeListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
attributes: AttributeList_attributes_edges_node[];
|
||||
}
|
||||
|
||||
const AttributeListPage: React.FC<AttributeListPageProps> = ({
|
||||
onAdd,
|
||||
initialSearch,
|
||||
onSearchChange,
|
||||
currentTab,
|
||||
onAll,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -32,6 +50,23 @@ const AttributeListPage: React.FC<AttributeListPageProps> = ({
|
|||
</Button>
|
||||
</PageHeader>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Attributes",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Attribute"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<AttributeList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
|
|
|
@ -52,7 +52,7 @@ const attributeList = gql`
|
|||
${attributeFragment}
|
||||
${pageInfoFragment}
|
||||
query AttributeList(
|
||||
$query: String
|
||||
$filter: AttributeFilterInput
|
||||
$inCategory: ID
|
||||
$inCollection: ID
|
||||
$before: String
|
||||
|
@ -61,7 +61,7 @@ const attributeList = gql`
|
|||
$last: Int
|
||||
) {
|
||||
attributes(
|
||||
query: $query
|
||||
filter: $filter
|
||||
inCategory: $inCategory
|
||||
inCollection: $inCollection
|
||||
before: $before
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeFilterInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: AttributeList
|
||||
// ====================================================
|
||||
|
@ -40,7 +42,7 @@ export interface AttributeList {
|
|||
}
|
||||
|
||||
export interface AttributeListVariables {
|
||||
query?: string | null;
|
||||
filter?: AttributeFilterInput | null;
|
||||
inCategory?: string | null;
|
||||
inCollection?: string | null;
|
||||
before?: string | null;
|
||||
|
|
|
@ -1,12 +1,26 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { BulkAction, Dialog, Pagination, SingleAction } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
SingleAction,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
export const attributeSection = "/attributes/";
|
||||
|
||||
export type AttributeListUrlDialog = "remove";
|
||||
export type AttributeListUrlQueryParams = BulkAction &
|
||||
export enum AttributeListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type AttributeListUrlFilters = Filters<AttributeListUrlFiltersEnum>;
|
||||
export type AttributeListUrlDialog = "remove" | TabActionDialog;
|
||||
export type AttributeListUrlQueryParams = ActiveTab &
|
||||
AttributeListUrlFilters &
|
||||
BulkAction &
|
||||
Dialog<AttributeListUrlDialog> &
|
||||
Pagination;
|
||||
export const attributeListPath = attributeSection;
|
||||
|
|
|
@ -3,6 +3,18 @@ import DeleteIcon from "@material-ui/icons/Delete";
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "@saleor/attributes/views/AttributeList/filters";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import usePaginator, {
|
||||
|
@ -20,6 +32,7 @@ import {
|
|||
attributeAddUrl,
|
||||
attributeListUrl,
|
||||
AttributeListUrlDialog,
|
||||
AttributeListUrlFilters,
|
||||
AttributeListUrlQueryParams,
|
||||
attributeUrl
|
||||
} from "../../urls";
|
||||
|
@ -37,6 +50,15 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
attributeListUrl({
|
||||
|
@ -56,8 +78,46 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
|||
})
|
||||
);
|
||||
|
||||
const changeFilterField = (filter: AttributeListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
attributeListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
attributeListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(attributeListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const queryVariables = React.useMemo(() => paginationState, [params]);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<AttributeListQuery variables={queryVariables}>
|
||||
|
@ -99,14 +159,22 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
|||
attributes={maybe(() =>
|
||||
data.attributes.edges.map(edge => edge.node)
|
||||
)}
|
||||
currentTab={currentTab}
|
||||
disabled={loading || attributeBulkDeleteOpts.loading}
|
||||
initialSearch={params.query || ""}
|
||||
isChecked={isSelected}
|
||||
onAdd={() => navigate(attributeAddUrl())}
|
||||
onAll={() => navigate(attributeListUrl())}
|
||||
onNextPage={loadNextPage}
|
||||
onPreviousPage={loadPreviousPage}
|
||||
onRowClick={id => () => navigate(attributeUrl(id))}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
pageInfo={pageInfo}
|
||||
selected={listElements.length}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={
|
||||
|
@ -130,6 +198,19 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
|||
onClose={closeModal}
|
||||
quantity={maybe(() => params.ids.length)}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
|
31
src/attributes/views/AttributeList/filters.ts
Normal file
31
src/attributes/views/AttributeList/filters.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { AttributeFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
AttributeListUrlFilters,
|
||||
AttributeListUrlFiltersEnum,
|
||||
AttributeListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const PRODUCT_FILTERS_KEY = "productFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: AttributeListUrlFilters
|
||||
): AttributeFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<AttributeListUrlFilters>(PRODUCT_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
AttributeListUrlQueryParams,
|
||||
AttributeListUrlFilters
|
||||
>(AttributeListUrlFiltersEnum);
|
|
@ -1,5 +1,3 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -12,9 +10,9 @@ import TableCell from "@material-ui/core/TableCell";
|
|||
import TableFooter from "@material-ui/core/TableFooter";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import { CategoryFragment } from "@saleor/categories/types/CategoryFragment";
|
||||
import Checkbox from "@saleor/components/Checkbox";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
|
@ -49,20 +47,8 @@ const styles = (theme: Theme) =>
|
|||
}
|
||||
});
|
||||
|
||||
interface CategoryListProps
|
||||
extends ListProps,
|
||||
ListActions,
|
||||
WithStyles<typeof styles> {
|
||||
categories?: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
children: {
|
||||
totalCount: number;
|
||||
};
|
||||
products: {
|
||||
totalCount: number;
|
||||
};
|
||||
}>;
|
||||
interface CategoryListProps extends ListProps, ListActions {
|
||||
categories?: CategoryFragment[];
|
||||
isRoot: boolean;
|
||||
onAdd?();
|
||||
}
|
||||
|
@ -75,144 +61,119 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
|||
classes,
|
||||
disabled,
|
||||
settings,
|
||||
isRoot,
|
||||
pageInfo,
|
||||
isChecked,
|
||||
isRoot,
|
||||
selected,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar,
|
||||
onAdd,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onUpdateListSettings,
|
||||
onRowClick
|
||||
}: CategoryListProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
{!isRoot && (
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "All Subcategories",
|
||||
description: "section header"
|
||||
})}
|
||||
toolbar={
|
||||
<Button color="primary" variant="text" onClick={onAdd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Create subcategory"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
}: CategoryListProps & WithStyles<typeof styles>) => (
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={categories}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Category Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSubcategories}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Subcategories"
|
||||
description="number of subcategories"
|
||||
/>
|
||||
)}
|
||||
<Table>
|
||||
<TableHead
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage
|
||||
defaultMessage="No. of Products"
|
||||
description="number of products"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={categories}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Category Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSubcategories}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Subcategories"
|
||||
description="number of subcategories"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage
|
||||
defaultMessage="No. of Products"
|
||||
description="number of products"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : false
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
categories,
|
||||
category => {
|
||||
const isSelected = category ? isChecked(category.id) : false;
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
categories,
|
||||
category => {
|
||||
const isSelected = category ? isChecked(category.id) : false;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
className={classes.tableRow}
|
||||
hover={!!category}
|
||||
onClick={category ? onRowClick(category.id) : undefined}
|
||||
key={category ? category.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(category.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{category && category.name ? category.name : <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSubcategories}>
|
||||
{category &&
|
||||
category.children &&
|
||||
category.children.totalCount !== undefined ? (
|
||||
category.children.totalCount
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
{category &&
|
||||
category.products &&
|
||||
category.products.totalCount !== undefined ? (
|
||||
category.products.totalCount
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
{isRoot ? (
|
||||
<FormattedMessage defaultMessage="No categories found" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="No subcategories found" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<TableRow
|
||||
className={classes.tableRow}
|
||||
hover={!!category}
|
||||
onClick={category ? onRowClick(category.id) : undefined}
|
||||
key={category ? category.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(category.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{category && category.name ? category.name : <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSubcategories}>
|
||||
{category &&
|
||||
category.children &&
|
||||
category.children.totalCount !== undefined ? (
|
||||
category.children.totalCount
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
{category &&
|
||||
category.products &&
|
||||
category.products.totalCount !== undefined ? (
|
||||
category.products.totalCount
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
{isRoot ? (
|
||||
<FormattedMessage defaultMessage="No categories found" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="No subcategories found" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
);
|
||||
CategoryList.displayName = "CategoryList";
|
||||
export default CategoryList;
|
||||
|
|
|
@ -1,44 +1,55 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { CategoryFragment } from "@saleor/categories/types/CategoryFragment";
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import CategoryList from "../CategoryList";
|
||||
|
||||
export interface CategoryTableProps extends PageListProps, ListActions {
|
||||
categories: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
children: {
|
||||
totalCount: number;
|
||||
};
|
||||
products: {
|
||||
totalCount: number;
|
||||
};
|
||||
}>;
|
||||
export interface CategoryTableProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
categories: CategoryFragment[];
|
||||
}
|
||||
|
||||
export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
|
||||
categories,
|
||||
currentTab,
|
||||
disabled,
|
||||
settings,
|
||||
onAdd,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onUpdateListSettings,
|
||||
onRowClick,
|
||||
pageInfo,
|
||||
initialSearch,
|
||||
isChecked,
|
||||
pageInfo,
|
||||
selected,
|
||||
settings,
|
||||
tabs,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar
|
||||
toolbar,
|
||||
onAdd,
|
||||
onAll,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onRowClick,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
onUpdateListSettings
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.categories)}>
|
||||
|
@ -49,23 +60,42 @@ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
|
|||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<CategoryList
|
||||
categories={categories}
|
||||
onAdd={onAdd}
|
||||
onRowClick={onRowClick}
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
isRoot={true}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
pageInfo={pageInfo}
|
||||
isChecked={isChecked}
|
||||
selected={selected}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
/>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Categories",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Category"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<CategoryList
|
||||
categories={categories}
|
||||
disabled={disabled}
|
||||
isChecked={isChecked}
|
||||
isRoot={true}
|
||||
pageInfo={pageInfo}
|
||||
selected={selected}
|
||||
settings={settings}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
onAdd={onAdd}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onRowClick={onRowClick}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import { RawDraftContentState } from "draft-js";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { CardSpacer } from "@saleor/components/CardSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
|
@ -178,21 +181,40 @@ export const CategoryUpdatePage: React.StatelessComponent<
|
|||
</TabContainer>
|
||||
<CardSpacer />
|
||||
{currentTab === CategoryPageTab.categories && (
|
||||
<CategoryList
|
||||
disabled={disabled}
|
||||
isRoot={false}
|
||||
categories={subcategories}
|
||||
onAdd={onAddCategory}
|
||||
onRowClick={onCategoryClick}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
pageInfo={pageInfo}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
selected={selected}
|
||||
isChecked={isChecked}
|
||||
toolbar={subcategoryListToolbar}
|
||||
/>
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "All Subcategories",
|
||||
description: "section header"
|
||||
})}
|
||||
toolbar={
|
||||
<Button
|
||||
color="primary"
|
||||
variant="text"
|
||||
onClick={onAddCategory}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Create subcategory"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<CategoryList
|
||||
categories={subcategories}
|
||||
disabled={disabled}
|
||||
isChecked={isChecked}
|
||||
isRoot={false}
|
||||
pageInfo={pageInfo}
|
||||
selected={selected}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={subcategoryListToolbar}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onRowClick={onCategoryClick}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
{currentTab === CategoryPageTab.products && (
|
||||
<CategoryProducts
|
||||
|
|
|
@ -1,64 +1,83 @@
|
|||
import { content } from "../storybook/stories/components/RichTextEditor";
|
||||
import { CategoryDetails_category } from "./types/CategoryDetails";
|
||||
import { CategoryFragment } from "./types/CategoryFragment";
|
||||
|
||||
export const categories = [
|
||||
export const categories: CategoryFragment[] = [
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 2
|
||||
},
|
||||
id: "123123",
|
||||
name: "Lorem ipsum dolor",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 54
|
||||
},
|
||||
id: "876752",
|
||||
name: "Mauris vehicula tortor vulputate",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 2
|
||||
},
|
||||
id: "876542",
|
||||
name: "Excepteur sint occaecat cupidatat non proident",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 6
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 6
|
||||
},
|
||||
id: "875352",
|
||||
name: "Ut enim ad minim veniam",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 76
|
||||
},
|
||||
id: "865752",
|
||||
name: "Duis aute irure dolor in reprehenderit",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 43
|
||||
}
|
||||
},
|
||||
{
|
||||
__typename: "Category",
|
||||
children: {
|
||||
__typename: "CategoryCountableConnection",
|
||||
totalCount: 11
|
||||
},
|
||||
id: "878752",
|
||||
name: "Neque porro quisquam est",
|
||||
products: {
|
||||
__typename: "ProductCountableConnection",
|
||||
totalCount: 21
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,18 @@ import {
|
|||
} from "./types/CategoryDetails";
|
||||
import { RootCategories } from "./types/RootCategories";
|
||||
|
||||
export const categoryFragment = gql`
|
||||
fragment CategoryFragment on Category {
|
||||
id
|
||||
name
|
||||
children {
|
||||
totalCount
|
||||
}
|
||||
products {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const categoryDetailsFragment = gql`
|
||||
fragment CategoryDetailsFragment on Category {
|
||||
id
|
||||
|
@ -25,11 +37,13 @@ export const categoryDetailsFragment = gql`
|
|||
`;
|
||||
|
||||
export const rootCategories = gql`
|
||||
${categoryFragment}
|
||||
query RootCategories(
|
||||
$first: Int
|
||||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
$filter: CategoryFilterInput
|
||||
) {
|
||||
categories(
|
||||
level: 0
|
||||
|
@ -37,17 +51,11 @@ export const rootCategories = gql`
|
|||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
children {
|
||||
totalCount
|
||||
}
|
||||
products {
|
||||
totalCount
|
||||
}
|
||||
...CategoryFragment
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
|
@ -64,6 +72,7 @@ export const TypedRootCategoriesQuery = TypedQuery<RootCategories, {}>(
|
|||
);
|
||||
|
||||
export const categoryDetails = gql`
|
||||
${categoryFragment}
|
||||
${categoryDetailsFragment}
|
||||
query CategoryDetails(
|
||||
$id: ID!
|
||||
|
@ -77,14 +86,7 @@ export const categoryDetails = gql`
|
|||
children(first: 20) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
children {
|
||||
totalCount
|
||||
}
|
||||
products {
|
||||
totalCount
|
||||
}
|
||||
...CategoryFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
25
src/categories/types/CategoryFragment.ts
Normal file
25
src/categories/types/CategoryFragment.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: CategoryFragment
|
||||
// ====================================================
|
||||
|
||||
export interface CategoryFragment_children {
|
||||
__typename: "CategoryCountableConnection";
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface CategoryFragment_products {
|
||||
__typename: "ProductCountableConnection";
|
||||
totalCount: number | null;
|
||||
}
|
||||
|
||||
export interface CategoryFragment {
|
||||
__typename: "Category";
|
||||
id: string;
|
||||
name: string;
|
||||
children: CategoryFragment_children | null;
|
||||
products: CategoryFragment_products | null;
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { CategoryFilterInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: RootCategories
|
||||
// ====================================================
|
||||
|
@ -52,4 +54,5 @@ export interface RootCategoriesVariables {
|
|||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
filter?: CategoryFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { ActiveTab, BulkAction, Dialog, Pagination } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
import { CategoryPageTab } from "./components/CategoryUpdatePage";
|
||||
|
||||
const categorySectionUrl = "/categories/";
|
||||
|
||||
export const categoryListPath = categorySectionUrl;
|
||||
export type CategoryListUrlDialog = "delete";
|
||||
export type CategoryListUrlQueryParams = BulkAction &
|
||||
export enum CategoryListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type CategoryListUrlFilters = Filters<CategoryListUrlFiltersEnum>;
|
||||
export type CategoryListUrlDialog = "delete" | TabActionDialog;
|
||||
export type CategoryListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
CategoryListUrlFilters &
|
||||
Dialog<CategoryListUrlDialog> &
|
||||
Pagination;
|
||||
export const categoryListUrl = (params?: CategoryListUrlQueryParams) =>
|
||||
|
|
|
@ -5,6 +5,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
|
@ -13,16 +17,26 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage";
|
||||
import { TypedCategoryBulkDeleteMutation } from "../mutations";
|
||||
import { TypedRootCategoriesQuery } from "../queries";
|
||||
import { CategoryBulkDelete } from "../types/CategoryBulkDelete";
|
||||
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
|
||||
import { TypedCategoryBulkDeleteMutation } from "../../mutations";
|
||||
import { TypedRootCategoriesQuery } from "../../queries";
|
||||
import { CategoryBulkDelete } from "../../types/CategoryBulkDelete";
|
||||
import {
|
||||
categoryAddUrl,
|
||||
categoryListUrl,
|
||||
CategoryListUrlDialog,
|
||||
CategoryListUrlFilters,
|
||||
CategoryListUrlQueryParams,
|
||||
categoryUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface CategoryListProps {
|
||||
params: CategoryListUrlQueryParams;
|
||||
|
@ -41,9 +55,77 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: CategoryListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
categoryListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
categoryListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
const openModal = (action: CategoryListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
categoryListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
categoryListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(categoryListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedRootCategoriesQuery displayLoader variables={paginationState}>
|
||||
<TypedRootCategoriesQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.categories.pageInfo),
|
||||
|
@ -78,6 +160,14 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
|||
() => data.categories.edges.map(edge => edge.node),
|
||||
[]
|
||||
)}
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(categoryListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
settings={settings}
|
||||
onAdd={() => navigate(categoryAddUrl())}
|
||||
onRowClick={id => () => navigate(categoryUrl(id))}
|
||||
|
@ -134,7 +224,7 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
|||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {counter, plural,
|
||||
one {this attribute}
|
||||
one {this category}
|
||||
other {{displayQuantity} categories}
|
||||
}?"
|
||||
values={{
|
||||
|
@ -148,6 +238,19 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
|||
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/categories/views/CategoryList/filter.ts
Normal file
31
src/categories/views/CategoryList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CategoryFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
CategoryListUrlFilters,
|
||||
CategoryListUrlFiltersEnum,
|
||||
CategoryListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const CATEGORY_FILTERS_KEY = "categoryFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: CategoryListUrlFilters
|
||||
): CategoryFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<CategoryListUrlFilters>(CATEGORY_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
CategoryListUrlQueryParams,
|
||||
CategoryListUrlFilters
|
||||
>(CategoryListUrlFiltersEnum);
|
2
src/categories/views/CategoryList/index.ts
Normal file
2
src/categories/views/CategoryList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./CategoryList";
|
||||
export * from "./CategoryList";
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -74,118 +73,110 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={collections}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Category Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage defaultMessage="No. of Products" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAvailability}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Availability"
|
||||
description="collection availability"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : false
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
collections,
|
||||
collection => {
|
||||
const isSelected = collection
|
||||
? isChecked(collection.id)
|
||||
: false;
|
||||
return (
|
||||
<TableRow
|
||||
className={classes.tableRow}
|
||||
hover={!!collection}
|
||||
onClick={collection ? onRowClick(collection.id) : undefined}
|
||||
key={collection ? collection.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(collection.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => collection.name,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => collection.products.totalCount,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAvailability}>
|
||||
{maybe(
|
||||
() => (
|
||||
<StatusLabel
|
||||
status={
|
||||
collection.isPublished ? "success" : "error"
|
||||
}
|
||||
label={
|
||||
collection.isPublished
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Published",
|
||||
description: "collection is published"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Not published",
|
||||
description: "collection is not published"
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No collections found" />
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={collections}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Category Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
<FormattedMessage defaultMessage="No. of Products" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAvailability}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Availability"
|
||||
description="collection availability"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
collections,
|
||||
collection => {
|
||||
const isSelected = collection ? isChecked(collection.id) : false;
|
||||
return (
|
||||
<TableRow
|
||||
className={classes.tableRow}
|
||||
hover={!!collection}
|
||||
onClick={collection ? onRowClick(collection.id) : undefined}
|
||||
key={collection ? collection.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(collection.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => collection.name,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colProducts}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => collection.products.totalCount,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAvailability}>
|
||||
{maybe(
|
||||
() => (
|
||||
<StatusLabel
|
||||
status={collection.isPublished ? "success" : "error"}
|
||||
label={
|
||||
collection.isPublished
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Published",
|
||||
description: "collection is published"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Not published",
|
||||
description: "collection is not published"
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No collections found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,22 +1,40 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { Container } from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||
import CollectionList from "../CollectionList/CollectionList";
|
||||
|
||||
export interface CollectionListPageProps extends PageListProps, ListActions {
|
||||
export interface CollectionListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
collections: CollectionList_collections_edges_node[];
|
||||
}
|
||||
|
||||
const CollectionListPage: React.StatelessComponent<CollectionListPageProps> = ({
|
||||
currentTab,
|
||||
disabled,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -36,7 +54,26 @@ const CollectionListPage: React.StatelessComponent<CollectionListPageProps> = ({
|
|||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<CollectionList disabled={disabled} {...listProps} />
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Collections",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Collection"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<CollectionList disabled={disabled} {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -60,8 +60,15 @@ export const collectionList = gql`
|
|||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
$filter: CollectionFilterInput
|
||||
) {
|
||||
collections(first: $first, after: $after, before: $before, last: $last) {
|
||||
collections(
|
||||
first: $first
|
||||
after: $after
|
||||
before: $before
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...CollectionFragment
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { CollectionFilterInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: CollectionList
|
||||
// ====================================================
|
||||
|
@ -47,4 +49,5 @@ export interface CollectionListVariables {
|
|||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
filter?: CollectionFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { BulkAction, Dialog, Pagination } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const collectionSectionUrl = "/collections/";
|
||||
|
||||
export const collectionListPath = collectionSectionUrl;
|
||||
export type CollectionListUrlDialog = "publish" | "unpublish" | "remove";
|
||||
export type CollectionListUrlQueryParams = BulkAction &
|
||||
export enum CollectionListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
|
||||
export type CollectionListUrlDialog =
|
||||
| "publish"
|
||||
| "unpublish"
|
||||
| "remove"
|
||||
| TabActionDialog;
|
||||
export type CollectionListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
CollectionListUrlFilters &
|
||||
Dialog<CollectionListUrlDialog> &
|
||||
Pagination;
|
||||
export const collectionListUrl = (params?: CollectionListUrlQueryParams) =>
|
||||
|
|
|
@ -6,6 +6,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
|
@ -16,21 +20,30 @@ import usePaginator, {
|
|||
import { commonMessages } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import CollectionListPage from "../components/CollectionListPage/CollectionListPage";
|
||||
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
|
||||
import {
|
||||
TypedCollectionBulkDelete,
|
||||
TypedCollectionBulkPublish
|
||||
} from "../mutations";
|
||||
import { TypedCollectionListQuery } from "../queries";
|
||||
import { CollectionBulkDelete } from "../types/CollectionBulkDelete";
|
||||
import { CollectionBulkPublish } from "../types/CollectionBulkPublish";
|
||||
} from "../../mutations";
|
||||
import { TypedCollectionListQuery } from "../../queries";
|
||||
import { CollectionBulkDelete } from "../../types/CollectionBulkDelete";
|
||||
import { CollectionBulkPublish } from "../../types/CollectionBulkPublish";
|
||||
import {
|
||||
collectionAddUrl,
|
||||
collectionListUrl,
|
||||
CollectionListUrlDialog,
|
||||
CollectionListUrlFilters,
|
||||
CollectionListUrlQueryParams,
|
||||
collectionUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface CollectionListProps {
|
||||
params: CollectionListUrlQueryParams;
|
||||
|
@ -50,6 +63,26 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: CollectionListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
collectionListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
collectionListUrl({
|
||||
|
@ -60,17 +93,47 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
|||
true
|
||||
);
|
||||
|
||||
const openModal = (action: CollectionListUrlDialog, ids: string[]) =>
|
||||
const openModal = (action: CollectionListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
collectionListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
collectionListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(collectionListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedCollectionListQuery displayLoader variables={paginationState}>
|
||||
<TypedCollectionListQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.collections.pageInfo),
|
||||
|
@ -130,7 +193,15 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
|||
return (
|
||||
<>
|
||||
<CollectionListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAdd={() => navigate(collectionAddUrl)}
|
||||
onAll={() => navigate(collectionListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
disabled={loading}
|
||||
collections={maybe(() =>
|
||||
data.collections.edges.map(edge => edge.node)
|
||||
|
@ -289,6 +360,19 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
|||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/collections/views/CollectionList/filter.ts
Normal file
31
src/collections/views/CollectionList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CollectionFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
CollectionListUrlFilters,
|
||||
CollectionListUrlFiltersEnum,
|
||||
CollectionListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const COLLECTION_FILTERS_KEY = "collectionFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: CollectionListUrlFilters
|
||||
): CollectionFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<CollectionListUrlFilters>(COLLECTION_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
CollectionListUrlQueryParams,
|
||||
CollectionListUrlFilters
|
||||
>(CollectionListUrlFiltersEnum);
|
2
src/collections/views/CollectionList/index.ts
Normal file
2
src/collections/views/CollectionList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./CollectionList";
|
||||
export * from "./CollectionList";
|
109
src/components/Filter/FilterActions.tsx
Normal file
109
src/components/Filter/FilterActions.tsx
Normal file
|
@ -0,0 +1,109 @@
|
|||
import { Theme } from "@material-ui/core/styles";
|
||||
import TextField, { TextFieldProps } from "@material-ui/core/TextField";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import React from "react";
|
||||
|
||||
import { FilterContentSubmitData, IFilter } from "../Filter";
|
||||
import Filter from "./Filter";
|
||||
|
||||
const useInputStyles = makeStyles({
|
||||
input: {
|
||||
padding: "10px 12px"
|
||||
},
|
||||
root: {
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
|
||||
const Search: React.FC<TextFieldProps> = props => {
|
||||
const classes = useInputStyles({});
|
||||
return (
|
||||
<TextField
|
||||
{...props}
|
||||
className={classes.root}
|
||||
inputProps={{
|
||||
className: classes.input
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles(
|
||||
(theme: Theme) => ({
|
||||
actionContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
padding: `${theme.spacing.unit * 1.5}px ${theme.spacing.unit * 3}px ${
|
||||
theme.spacing.unit
|
||||
}px`
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: "FilterActions"
|
||||
}
|
||||
);
|
||||
|
||||
export interface FilterActionsPropsSearch {
|
||||
placeholder: string;
|
||||
search: string;
|
||||
onSearchChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
export interface FilterActionsPropsFilters<TKeys = string> {
|
||||
currencySymbol: string;
|
||||
menu: IFilter<TKeys>;
|
||||
filterLabel: string;
|
||||
onFilterAdd: (filter: FilterContentSubmitData<TKeys>) => void;
|
||||
}
|
||||
|
||||
export const FilterActionsOnlySearch: React.FC<
|
||||
FilterActionsPropsSearch
|
||||
> = props => {
|
||||
const { onSearchChange, placeholder, search } = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<div className={classes.actionContainer}>
|
||||
<Search
|
||||
fullWidth
|
||||
placeholder={placeholder}
|
||||
value={search}
|
||||
onChange={onSearchChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export type FilterActionsProps = FilterActionsPropsSearch &
|
||||
FilterActionsPropsFilters;
|
||||
const FilterActions: React.FC<FilterActionsProps> = props => {
|
||||
const {
|
||||
currencySymbol,
|
||||
filterLabel,
|
||||
menu,
|
||||
onFilterAdd,
|
||||
onSearchChange,
|
||||
placeholder,
|
||||
search
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<div className={classes.actionContainer}>
|
||||
<Filter
|
||||
currencySymbol={currencySymbol}
|
||||
menu={menu}
|
||||
filterLabel={filterLabel}
|
||||
onFilterAdd={onFilterAdd}
|
||||
/>
|
||||
<Search
|
||||
fullWidth
|
||||
placeholder={placeholder}
|
||||
value={search}
|
||||
onChange={onSearchChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
FilterActions.displayName = "FilterActions";
|
||||
export default FilterActions;
|
98
src/components/Filter/FilterSearch.tsx
Normal file
98
src/components/Filter/FilterSearch.tsx
Normal file
|
@ -0,0 +1,98 @@
|
|||
import { Theme } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { SearchPageProps } from "../../types";
|
||||
import Debounce from "../Debounce";
|
||||
import { FilterActionsOnlySearch } from "../Filter/FilterActions";
|
||||
import Hr from "../Hr";
|
||||
import Link from "../Link";
|
||||
|
||||
export interface FilterSearchProps extends SearchPageProps {
|
||||
displaySearchAction: "save" | "delete" | null;
|
||||
searchPlaceholder: string;
|
||||
onSearchDelete?: () => void;
|
||||
onSearchSave?: () => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
(theme: Theme) => ({
|
||||
tabAction: {
|
||||
display: "inline-block"
|
||||
},
|
||||
tabActionContainer: {
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: theme.spacing.unit,
|
||||
padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px`
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: "FilterSearch"
|
||||
}
|
||||
);
|
||||
|
||||
const FilterSearch: React.FC<FilterSearchProps> = props => {
|
||||
const {
|
||||
displaySearchAction,
|
||||
initialSearch,
|
||||
onSearchChange,
|
||||
onSearchDelete,
|
||||
onSearchSave,
|
||||
searchPlaceholder
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
const [search, setSearch] = React.useState(initialSearch);
|
||||
React.useEffect(() => setSearch(initialSearch), [initialSearch]);
|
||||
|
||||
return (
|
||||
<Debounce debounceFn={onSearchChange}>
|
||||
{debounceSearchChange => {
|
||||
const handleSearchChange = (event: React.ChangeEvent<any>) => {
|
||||
const value = event.target.value;
|
||||
setSearch(value);
|
||||
debounceSearchChange(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterActionsOnlySearch
|
||||
{...props}
|
||||
placeholder={searchPlaceholder}
|
||||
search={search}
|
||||
onSearchChange={handleSearchChange}
|
||||
/>
|
||||
{!!displaySearchAction ? (
|
||||
<div className={classes.tabActionContainer}>
|
||||
<div className={classes.tabAction}>
|
||||
{displaySearchAction === "save" ? (
|
||||
<Link onClick={onSearchSave}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save Custom Search"
|
||||
description="button"
|
||||
/>
|
||||
</Link>
|
||||
) : (
|
||||
<Link onClick={onSearchDelete}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete Search"
|
||||
description="button"
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Hr />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Debounce>
|
||||
);
|
||||
};
|
||||
|
||||
FilterSearch.displayName = "FilterSearch";
|
||||
export default FilterSearch;
|
|
@ -6,9 +6,8 @@ import Debounce from "../Debounce";
|
|||
import { IFilter } from "../Filter/types";
|
||||
import FilterTabs, { FilterChips, FilterTab } from "../TableFilter";
|
||||
|
||||
export interface FilterBarProps<TUrlFilters = object, TFilterKeys = any>
|
||||
extends FilterProps<TUrlFilters, TFilterKeys> {
|
||||
filterMenu: IFilter<TFilterKeys>;
|
||||
export interface FilterBarProps<TKeys = string> extends FilterProps {
|
||||
filterMenu: IFilter<TKeys>;
|
||||
}
|
||||
|
||||
const FilterBar: React.FC<FilterBarProps> = ({
|
||||
|
@ -16,32 +15,32 @@ const FilterBar: React.FC<FilterBarProps> = ({
|
|||
currencySymbol,
|
||||
filterLabel,
|
||||
filtersList,
|
||||
filterTabs,
|
||||
filterMenu,
|
||||
currentTab,
|
||||
initialSearch,
|
||||
searchPlaceholder,
|
||||
tabs,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onFilterAdd,
|
||||
onFilterSave,
|
||||
onTabChange,
|
||||
onFilterDelete
|
||||
onTabDelete,
|
||||
onTabSave
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [search, setSearch] = React.useState(initialSearch);
|
||||
React.useEffect(() => setSearch(initialSearch), [currentTab, initialSearch]);
|
||||
|
||||
const isCustom = currentTab === filterTabs.length + 1;
|
||||
const isCustom = currentTab === tabs.length + 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterTabs currentTab={currentTab}>
|
||||
<FilterTab label={allTabLabel} onClick={onAll} />
|
||||
{filterTabs.map((tab, tabIndex) => (
|
||||
{tabs.map((tab, tabIndex) => (
|
||||
<FilterTab
|
||||
onClick={() => onTabChange(tabIndex + 1)}
|
||||
label={tab.name}
|
||||
label={tab}
|
||||
key={tabIndex}
|
||||
/>
|
||||
))}
|
||||
|
@ -65,6 +64,9 @@ const FilterBar: React.FC<FilterBarProps> = ({
|
|||
return (
|
||||
<FilterChips
|
||||
currencySymbol={currencySymbol}
|
||||
displayTabAction={
|
||||
!!initialSearch ? (isCustom ? "save" : "delete") : null
|
||||
}
|
||||
menu={filterMenu}
|
||||
filtersList={filtersList}
|
||||
filterLabel={filterLabel}
|
||||
|
@ -72,9 +74,9 @@ const FilterBar: React.FC<FilterBarProps> = ({
|
|||
search={search}
|
||||
onSearchChange={handleSearchChange}
|
||||
onFilterAdd={onFilterAdd}
|
||||
onFilterSave={onFilterSave}
|
||||
onFilterSave={onTabSave}
|
||||
isCustomSearch={isCustom}
|
||||
onFilterDelete={onFilterDelete}
|
||||
onFilterDelete={onTabDelete}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -56,6 +56,7 @@ const SaveFilterTabDialog: React.FC<SaveFilterTabDialogProps> = ({
|
|||
<>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
autoFocus
|
||||
fullWidth
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Search Name",
|
||||
|
|
65
src/components/SearchBar/SearchBar.tsx
Normal file
65
src/components/SearchBar/SearchBar.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { SearchPageProps, TabPageProps } from "@saleor/types";
|
||||
import FilterSearch from "../Filter/FilterSearch";
|
||||
import FilterTabs, { FilterTab } from "../TableFilter";
|
||||
|
||||
export interface SearchBarProps extends SearchPageProps, TabPageProps {
|
||||
allTabLabel: string;
|
||||
searchPlaceholder: string;
|
||||
}
|
||||
|
||||
const SearchBar: React.FC<SearchBarProps> = props => {
|
||||
const {
|
||||
allTabLabel,
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onSearchChange,
|
||||
searchPlaceholder,
|
||||
tabs,
|
||||
onAll,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave
|
||||
} = props;
|
||||
const intl = useIntl();
|
||||
|
||||
const isCustom = currentTab === tabs.length + 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterTabs currentTab={currentTab}>
|
||||
<FilterTab label={allTabLabel} onClick={onAll} />
|
||||
{tabs.map((tab, tabIndex) => (
|
||||
<FilterTab
|
||||
onClick={() => onTabChange(tabIndex + 1)}
|
||||
label={tab}
|
||||
key={tabIndex}
|
||||
/>
|
||||
))}
|
||||
{isCustom && (
|
||||
<FilterTab
|
||||
onClick={() => undefined}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Custom Filter"
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</FilterTabs>
|
||||
<FilterSearch
|
||||
displaySearchAction={
|
||||
!!initialSearch ? (isCustom ? "save" : "delete") : null
|
||||
}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
onSearchChange={onSearchChange}
|
||||
onSearchDelete={onTabDelete}
|
||||
onSearchSave={onTabSave}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
SearchBar.displayName = "SearchBar";
|
||||
export default SearchBar;
|
2
src/components/SearchBar/index.ts
Normal file
2
src/components/SearchBar/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./SearchBar";
|
||||
export * from "./SearchBar";
|
|
@ -1,14 +1,14 @@
|
|||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import { Theme } from "@material-ui/core/styles";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
import TextField, { TextFieldProps } from "@material-ui/core/TextField";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ClearIcon from "@material-ui/icons/Clear";
|
||||
import { createStyles, makeStyles, useTheme } from "@material-ui/styles";
|
||||
import { makeStyles, useTheme } from "@material-ui/styles";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Filter, { FilterContentSubmitData, IFilter } from "../Filter";
|
||||
import Filter from "../Filter";
|
||||
import FilterActions, { FilterActionsProps } from "../Filter/FilterActions";
|
||||
import Hr from "../Hr";
|
||||
import Link from "../Link";
|
||||
|
||||
|
@ -17,110 +17,76 @@ export interface Filter {
|
|||
onClick: () => void;
|
||||
}
|
||||
|
||||
const useInputStyles = makeStyles({
|
||||
input: {
|
||||
padding: "10px 12px"
|
||||
},
|
||||
root: {
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
|
||||
const Search: React.FC<TextFieldProps> = props => {
|
||||
const classes = useInputStyles({});
|
||||
return (
|
||||
<TextField
|
||||
{...props}
|
||||
className={classes.root}
|
||||
inputProps={{
|
||||
className: classes.input
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles(
|
||||
(theme: Theme) =>
|
||||
createStyles({
|
||||
actionContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${
|
||||
theme.spacing.unit
|
||||
}px ${theme.spacing.unit * 3}px`
|
||||
(theme: Theme) => ({
|
||||
filterButton: {
|
||||
alignItems: "center",
|
||||
backgroundColor: fade(theme.palette.primary.main, 0.8),
|
||||
borderRadius: "19px",
|
||||
display: "flex",
|
||||
height: "38px",
|
||||
justifyContent: "space-around",
|
||||
margin: `0 ${theme.spacing.unit * 2}px ${theme.spacing.unit}px`,
|
||||
marginLeft: 0,
|
||||
padding: `0 ${theme.spacing.unit * 2}px`
|
||||
},
|
||||
filterChipContainer: {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
flexWrap: "wrap"
|
||||
},
|
||||
filterContainer: {
|
||||
"& a": {
|
||||
paddingBottom: 10
|
||||
},
|
||||
filterButton: {
|
||||
alignItems: "center",
|
||||
backgroundColor: fade(theme.palette.primary.main, 0.8),
|
||||
borderRadius: "19px",
|
||||
display: "flex",
|
||||
height: "38px",
|
||||
justifyContent: "space-around",
|
||||
margin: `0 ${theme.spacing.unit * 2}px ${theme.spacing.unit}px`,
|
||||
marginLeft: 0,
|
||||
padding: "0 16px"
|
||||
},
|
||||
filterChipContainer: {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
flexWrap: "wrap"
|
||||
},
|
||||
filterContainer: {
|
||||
"& a": {
|
||||
paddingBottom: 10
|
||||
},
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
display: "flex",
|
||||
marginTop: theme.spacing.unit,
|
||||
padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px`
|
||||
},
|
||||
filterIcon: {
|
||||
color: theme.palette.common.white,
|
||||
height: 16,
|
||||
width: 16
|
||||
},
|
||||
filterIconContainer: {
|
||||
WebkitAppearance: "none",
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
borderRadius: "100%",
|
||||
cursor: "pointer",
|
||||
height: 32,
|
||||
marginRight: -13,
|
||||
padding: 8,
|
||||
width: 32
|
||||
},
|
||||
filterLabel: {
|
||||
marginBottom: theme.spacing.unit
|
||||
},
|
||||
filterText: {
|
||||
color: theme.palette.common.white,
|
||||
fontSize: 14,
|
||||
fontWeight: 400 as 400,
|
||||
lineHeight: "38px"
|
||||
}
|
||||
}),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
display: "flex",
|
||||
marginTop: theme.spacing.unit,
|
||||
padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px`
|
||||
},
|
||||
filterIcon: {
|
||||
color: theme.palette.common.white,
|
||||
height: 16,
|
||||
width: 16
|
||||
},
|
||||
filterIconContainer: {
|
||||
WebkitAppearance: "none",
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
borderRadius: "100%",
|
||||
cursor: "pointer",
|
||||
height: 32,
|
||||
marginRight: -13,
|
||||
padding: 8,
|
||||
width: 32
|
||||
},
|
||||
filterLabel: {
|
||||
marginBottom: theme.spacing.unit
|
||||
},
|
||||
filterText: {
|
||||
color: theme.palette.common.white,
|
||||
fontSize: 14,
|
||||
fontWeight: 400 as 400,
|
||||
lineHeight: "38px"
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: "FilterChips"
|
||||
}
|
||||
);
|
||||
|
||||
interface FilterChipProps<TFilterKeys = string> {
|
||||
currencySymbol: string;
|
||||
menu: IFilter<TFilterKeys>;
|
||||
interface FilterChipProps extends FilterActionsProps {
|
||||
displayTabAction: "save" | "delete" | null;
|
||||
filtersList: Filter[];
|
||||
filterLabel: string;
|
||||
placeholder: string;
|
||||
search: string;
|
||||
isCustomSearch: boolean;
|
||||
onSearchChange: (event: React.ChangeEvent<any>) => void;
|
||||
onFilterAdd: (filter: FilterContentSubmitData<TFilterKeys>) => void;
|
||||
onFilterDelete: () => void;
|
||||
onFilterSave: () => void;
|
||||
}
|
||||
|
||||
export const FilterChips: React.FC<FilterChipProps> = ({
|
||||
currencySymbol,
|
||||
displayTabAction,
|
||||
filtersList,
|
||||
menu,
|
||||
filterLabel,
|
||||
|
@ -129,29 +95,23 @@ export const FilterChips: React.FC<FilterChipProps> = ({
|
|||
search,
|
||||
onFilterAdd,
|
||||
onFilterSave,
|
||||
onFilterDelete,
|
||||
isCustomSearch
|
||||
onFilterDelete
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const classes = useStyles({ theme });
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.actionContainer}>
|
||||
<Filter
|
||||
currencySymbol={currencySymbol}
|
||||
menu={menu}
|
||||
filterLabel={filterLabel}
|
||||
onFilterAdd={onFilterAdd}
|
||||
/>
|
||||
<Search
|
||||
fullWidth
|
||||
placeholder={placeholder}
|
||||
value={search}
|
||||
onChange={onSearchChange}
|
||||
/>
|
||||
</div>
|
||||
{search || (filtersList && filtersList.length) ? (
|
||||
<FilterActions
|
||||
currencySymbol={currencySymbol}
|
||||
menu={menu}
|
||||
filterLabel={filterLabel}
|
||||
placeholder={placeholder}
|
||||
search={search}
|
||||
onSearchChange={onSearchChange}
|
||||
onFilterAdd={onFilterAdd}
|
||||
/>
|
||||
{search || (filtersList && filtersList.length > 0) ? (
|
||||
<div className={classes.filterContainer}>
|
||||
<div className={classes.filterChipContainer}>
|
||||
{filtersList.map(filter => (
|
||||
|
@ -168,7 +128,7 @@ export const FilterChips: React.FC<FilterChipProps> = ({
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isCustomSearch ? (
|
||||
{displayTabAction === "save" ? (
|
||||
<Link onClick={onFilterSave}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save Custom Search"
|
||||
|
@ -176,12 +136,14 @@ export const FilterChips: React.FC<FilterChipProps> = ({
|
|||
/>
|
||||
</Link>
|
||||
) : (
|
||||
<Link onClick={onFilterDelete}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete Search"
|
||||
description="button"
|
||||
/>
|
||||
</Link>
|
||||
displayTabAction === "delete" && (
|
||||
<Link onClick={onFilterDelete}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete Search"
|
||||
description="button"
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -68,89 +67,87 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
|||
selected,
|
||||
isChecked
|
||||
}: CustomerListProps) => (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={customers}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Customer Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEmail}>
|
||||
<FormattedMessage defaultMessage="Customer Email" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colOrders}>
|
||||
<FormattedMessage defaultMessage="No. of Orders" />
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
customers,
|
||||
customer => {
|
||||
const isSelected = customer ? isChecked(customer.id) : false;
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={customers}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Customer Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEmail}>
|
||||
<FormattedMessage defaultMessage="Customer Email" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colOrders}>
|
||||
<FormattedMessage defaultMessage="No. of Orders" />
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
customers,
|
||||
customer => {
|
||||
const isSelected = customer ? isChecked(customer.id) : false;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
className={!!customer ? classes.tableRow : undefined}
|
||||
hover={!!customer}
|
||||
key={customer ? customer.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
onClick={customer ? onRowClick(customer.id) : undefined}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(customer.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{getUserName(customer)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEmail}>
|
||||
{maybe<React.ReactNode>(() => customer.email, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colOrders}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => customer.orders.totalCount,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No customers found" />
|
||||
return (
|
||||
<TableRow
|
||||
className={!!customer ? classes.tableRow : undefined}
|
||||
hover={!!customer}
|
||||
key={customer ? customer.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
onClick={customer ? onRowClick(customer.id) : undefined}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(customer.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{getUserName(customer)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEmail}>
|
||||
{maybe<React.ReactNode>(() => customer.email, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colOrders}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => customer.orders.totalCount,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No customers found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
);
|
||||
CustomerList.displayName = "CustomerList";
|
||||
|
|
|
@ -1,23 +1,41 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
||||
import CustomerList from "../CustomerList/CustomerList";
|
||||
|
||||
export interface CustomerListPageProps extends PageListProps, ListActions {
|
||||
export interface CustomerListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
customers: ListCustomers_customers_edges_node[];
|
||||
}
|
||||
|
||||
const CustomerListPage: React.StatelessComponent<CustomerListPageProps> = ({
|
||||
currentTab,
|
||||
customers,
|
||||
disabled,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...customerListProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -37,11 +55,30 @@ const CustomerListPage: React.StatelessComponent<CustomerListPageProps> = ({
|
|||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<CustomerList
|
||||
customers={customers}
|
||||
disabled={disabled}
|
||||
{...customerListProps}
|
||||
/>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Customers",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Customer"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<CustomerList
|
||||
customers={customers}
|
||||
disabled={disabled}
|
||||
{...customerListProps}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -64,8 +64,15 @@ const customerList = gql`
|
|||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
$filter: CustomerFilterInput
|
||||
) {
|
||||
customers(after: $after, before: $before, first: $first, last: $last) {
|
||||
customers(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...CustomerFragment
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { CustomerFilterInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: ListCustomers
|
||||
// ====================================================
|
||||
|
@ -48,4 +50,5 @@ export interface ListCustomersVariables {
|
|||
before?: string | null;
|
||||
first?: number | null;
|
||||
last?: number | null;
|
||||
filter?: CustomerFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { BulkAction, Dialog, Pagination, SingleAction } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
SingleAction,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
export const customerSection = "/customers/";
|
||||
|
||||
export const customerListPath = customerSection;
|
||||
export type CustomerListUrlDialog = "remove";
|
||||
export type CustomerListUrlQueryParams = BulkAction &
|
||||
export enum CustomerListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type CustomerListUrlFilters = Filters<CustomerListUrlFiltersEnum>;
|
||||
export type CustomerListUrlDialog = "remove" | TabActionDialog;
|
||||
export type CustomerListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
CustomerListUrlFilters &
|
||||
Dialog<CustomerListUrlDialog> &
|
||||
Pagination;
|
||||
export const customerListUrl = (params?: CustomerListUrlQueryParams) =>
|
||||
|
|
|
@ -5,6 +5,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
|
@ -15,16 +19,26 @@ import usePaginator, {
|
|||
import { commonMessages } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import CustomerListPage from "../components/CustomerListPage";
|
||||
import { TypedBulkRemoveCustomers } from "../mutations";
|
||||
import { TypedCustomerListQuery } from "../queries";
|
||||
import { BulkRemoveCustomers } from "../types/BulkRemoveCustomers";
|
||||
import CustomerListPage from "../../components/CustomerListPage";
|
||||
import { TypedBulkRemoveCustomers } from "../../mutations";
|
||||
import { TypedCustomerListQuery } from "../../queries";
|
||||
import { BulkRemoveCustomers } from "../../types/BulkRemoveCustomers";
|
||||
import {
|
||||
customerAddUrl,
|
||||
customerListUrl,
|
||||
CustomerListUrlDialog,
|
||||
CustomerListUrlFilters,
|
||||
CustomerListUrlQueryParams,
|
||||
customerUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface CustomerListProps {
|
||||
params: CustomerListUrlQueryParams;
|
||||
|
@ -44,6 +58,26 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: CustomerListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
customerListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
customerListUrl({
|
||||
|
@ -54,10 +88,47 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
|||
true
|
||||
);
|
||||
|
||||
const openModal = (action: CustomerListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
customerListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
customerListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(customerListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedCustomerListQuery displayLoader variables={paginationState}>
|
||||
<TypedCustomerListQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.customers.pageInfo),
|
||||
|
@ -90,6 +161,14 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
|||
return (
|
||||
<>
|
||||
<CustomerListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(customerListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
customers={maybe(() =>
|
||||
data.customers.edges.map(edge => edge.node)
|
||||
)}
|
||||
|
@ -156,6 +235,19 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
|||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/customers/views/CustomerList/filter.ts
Normal file
31
src/customers/views/CustomerList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CustomerFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
CustomerListUrlFilters,
|
||||
CustomerListUrlFiltersEnum,
|
||||
CustomerListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const CUSTOMER_FILTERS_KEY = "customerFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: CustomerListUrlFilters
|
||||
): CustomerFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<CustomerListUrlFilters>(CUSTOMER_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
CustomerListUrlQueryParams,
|
||||
CustomerListUrlFilters
|
||||
>(CustomerListUrlFiltersEnum);
|
2
src/customers/views/CustomerList/index.ts
Normal file
2
src/customers/views/CustomerList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./CustomerList";
|
||||
export * from "./CustomerList";
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -83,124 +82,119 @@ const SaleList = withStyles(styles, {
|
|||
toggleAll,
|
||||
toolbar
|
||||
}: SaleListProps & WithStyles<typeof styles>) => (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={sales}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Name" description="sale name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Starts"
|
||||
description="sale start date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ends"
|
||||
description="sale end date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colValue}>
|
||||
<FormattedMessage defaultMessage="Value" description="sale value" />
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
sales,
|
||||
sale => {
|
||||
const isSelected = sale ? isChecked(sale.id) : false;
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={sales}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Name" description="sale name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Starts"
|
||||
description="sale start date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
<FormattedMessage defaultMessage="Ends" description="sale end date" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colValue}>
|
||||
<FormattedMessage defaultMessage="Value" description="sale value" />
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
sales,
|
||||
sale => {
|
||||
const isSelected = sale ? isChecked(sale.id) : false;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
className={!!sale ? classes.tableRow : undefined}
|
||||
hover={!!sale}
|
||||
key={sale ? sale.id : "skeleton"}
|
||||
return (
|
||||
<TableRow
|
||||
className={!!sale ? classes.tableRow : undefined}
|
||||
hover={!!sale}
|
||||
key={sale ? sale.id : "skeleton"}
|
||||
onClick={sale ? onRowClick(sale.id) : undefined}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(sale.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(() => sale.name, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
{sale && sale.startDate ? (
|
||||
<Date date={sale.startDate} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
{sale && sale.endDate ? (
|
||||
<Date date={sale.endDate} />
|
||||
) : sale && sale.endDate === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classes.colValue}
|
||||
onClick={sale ? onRowClick(sale.id) : undefined}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(sale.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(() => sale.name, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
{sale && sale.startDate ? (
|
||||
<Date date={sale.startDate} />
|
||||
{sale && sale.type && sale.value ? (
|
||||
sale.type === SaleType.FIXED ? (
|
||||
<Money
|
||||
money={{
|
||||
amount: sale.value,
|
||||
currency: defaultCurrency
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
{sale && sale.endDate ? (
|
||||
<Date date={sale.endDate} />
|
||||
) : sale && sale.endDate === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classes.colValue}
|
||||
onClick={sale ? onRowClick(sale.id) : undefined}
|
||||
>
|
||||
{sale && sale.type && sale.value ? (
|
||||
sale.type === SaleType.FIXED ? (
|
||||
<Money
|
||||
money={{
|
||||
amount: sale.value,
|
||||
currency: defaultCurrency
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Percent amount={sale.value} />
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No sales found" />
|
||||
<Percent amount={sale.value} />
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No sales found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
);
|
||||
SaleList.displayName = "SaleList";
|
||||
|
|
|
@ -1,22 +1,40 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||
import SaleList from "../SaleList";
|
||||
|
||||
export interface SaleListPageProps extends PageListProps, ListActions {
|
||||
export interface SaleListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
defaultCurrency: string;
|
||||
sales: SaleList_sales_edges_node[];
|
||||
}
|
||||
|
||||
const SaleListPage: React.StatelessComponent<SaleListPageProps> = ({
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -28,7 +46,26 @@ const SaleListPage: React.StatelessComponent<SaleListPageProps> = ({
|
|||
<FormattedMessage defaultMessage="Create Sale" description="button" />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<SaleList {...listProps} />
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Sales",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Sale"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<SaleList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -98,163 +97,155 @@ const VoucherList = withStyles(styles, {
|
|||
toggleAll,
|
||||
toolbar
|
||||
}: VoucherListProps & WithStyles<typeof styles>) => (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={vouchers}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Code"
|
||||
description="voucher code"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colMinSpent}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Min. Spent"
|
||||
description="minimum amount of spent money to activate voucher"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Starts"
|
||||
description="voucher is active from date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ends"
|
||||
description="voucher is active until date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colValue}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Value"
|
||||
description="voucher value"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colUses}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Uses"
|
||||
description="voucher uses"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
vouchers,
|
||||
voucher => {
|
||||
const isSelected = voucher ? isChecked(voucher.id) : false;
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={vouchers}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage defaultMessage="Code" description="voucher code" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colMinSpent}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Min. Spent"
|
||||
description="minimum amount of spent money to activate voucher"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Starts"
|
||||
description="voucher is active from date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Ends"
|
||||
description="voucher is active until date"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colValue}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Value"
|
||||
description="voucher value"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colUses}>
|
||||
<FormattedMessage defaultMessage="Uses" description="voucher uses" />
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
vouchers,
|
||||
voucher => {
|
||||
const isSelected = voucher ? isChecked(voucher.id) : false;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
className={!!voucher ? classes.tableRow : undefined}
|
||||
hover={!!voucher}
|
||||
key={voucher ? voucher.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
return (
|
||||
<TableRow
|
||||
className={!!voucher ? classes.tableRow : undefined}
|
||||
hover={!!voucher}
|
||||
key={voucher ? voucher.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
onClick={voucher ? onRowClick(voucher.id) : undefined}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(voucher.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(() => voucher.code, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colMinSpent}>
|
||||
{voucher && voucher.minAmountSpent ? (
|
||||
<Money money={voucher.minAmountSpent} />
|
||||
) : voucher && voucher.minAmountSpent === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
{voucher && voucher.startDate ? (
|
||||
<Date date={voucher.startDate} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
{voucher && voucher.endDate ? (
|
||||
<Date date={voucher.endDate} />
|
||||
) : voucher && voucher.endDate === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classes.colValue}
|
||||
onClick={voucher ? onRowClick(voucher.id) : undefined}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(voucher.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(() => voucher.code, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colMinSpent}>
|
||||
{voucher && voucher.minAmountSpent ? (
|
||||
<Money money={voucher.minAmountSpent} />
|
||||
) : voucher && voucher.minAmountSpent === null ? (
|
||||
"-"
|
||||
{voucher &&
|
||||
voucher.discountValueType &&
|
||||
voucher.discountValue ? (
|
||||
voucher.discountValueType ===
|
||||
DiscountValueTypeEnum.FIXED ? (
|
||||
<Money
|
||||
money={{
|
||||
amount: voucher.discountValue,
|
||||
currency: defaultCurrency
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colStart}>
|
||||
{voucher && voucher.startDate ? (
|
||||
<Date date={voucher.startDate} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colEnd}>
|
||||
{voucher && voucher.endDate ? (
|
||||
<Date date={voucher.endDate} />
|
||||
) : voucher && voucher.endDate === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classes.colValue}
|
||||
onClick={voucher ? onRowClick(voucher.id) : undefined}
|
||||
>
|
||||
{voucher &&
|
||||
voucher.discountValueType &&
|
||||
voucher.discountValue ? (
|
||||
voucher.discountValueType ===
|
||||
DiscountValueTypeEnum.FIXED ? (
|
||||
<Money
|
||||
money={{
|
||||
amount: voucher.discountValue,
|
||||
currency: defaultCurrency
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Percent amount={voucher.discountValue} />
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colUses}>
|
||||
{voucher && voucher.usageLimit ? (
|
||||
voucher.usageLimit
|
||||
) : voucher && voucher.usageLimit === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No vouchers found" />
|
||||
<Percent amount={voucher.discountValue} />
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colUses}>
|
||||
{voucher && voucher.usageLimit ? (
|
||||
voucher.usageLimit
|
||||
) : voucher && voucher.usageLimit === null ? (
|
||||
"-"
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No vouchers found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
);
|
||||
VoucherList.displayName = "VoucherList";
|
||||
|
|
|
@ -1,36 +1,41 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||
import VoucherList from "../VoucherList";
|
||||
|
||||
export interface VoucherListPageProps extends PageListProps, ListActions {
|
||||
export interface VoucherListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
defaultCurrency: string;
|
||||
vouchers: VoucherList_vouchers_edges_node[];
|
||||
}
|
||||
|
||||
const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
|
||||
defaultCurrency,
|
||||
disabled,
|
||||
settings,
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onUpdateListSettings,
|
||||
onRowClick,
|
||||
pageInfo,
|
||||
vouchers,
|
||||
isChecked,
|
||||
selected,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
|
@ -44,22 +49,26 @@ const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
|
|||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<VoucherList
|
||||
defaultCurrency={defaultCurrency}
|
||||
settings={settings}
|
||||
disabled={disabled}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
onRowClick={onRowClick}
|
||||
pageInfo={pageInfo}
|
||||
vouchers={vouchers}
|
||||
isChecked={isChecked}
|
||||
selected={selected}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
/>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Vouchers",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Voucher"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<VoucherList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -166,8 +166,20 @@ export const voucherDetailsFragment = gql`
|
|||
export const saleList = gql`
|
||||
${pageInfoFragment}
|
||||
${saleFragment}
|
||||
query SaleList($after: String, $before: String, $first: Int, $last: Int) {
|
||||
sales(after: $after, before: $before, first: $first, last: $last) {
|
||||
query SaleList(
|
||||
$after: String
|
||||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
$filter: SaleFilterInput
|
||||
) {
|
||||
sales(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...SaleFragment
|
||||
|
@ -184,8 +196,20 @@ export const TypedSaleList = TypedQuery<SaleList, SaleListVariables>(saleList);
|
|||
export const voucherList = gql`
|
||||
${pageInfoFragment}
|
||||
${voucherFragment}
|
||||
query VoucherList($after: String, $before: String, $first: Int, $last: Int) {
|
||||
vouchers(after: $after, before: $before, first: $first, last: $last) {
|
||||
query VoucherList(
|
||||
$after: String
|
||||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
$filter: VoucherFilterInput
|
||||
) {
|
||||
vouchers(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...VoucherFragment
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { SaleType } from "./../../types/globalTypes";
|
||||
import { SaleFilterInput, SaleType } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: SaleList
|
||||
|
@ -46,4 +46,5 @@ export interface SaleListVariables {
|
|||
before?: string | null;
|
||||
first?: number | null;
|
||||
last?: number | null;
|
||||
filter?: SaleFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||
import { VoucherFilterInput, DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: VoucherList
|
||||
|
@ -62,4 +62,5 @@ export interface VoucherListVariables {
|
|||
before?: string | null;
|
||||
first?: number | null;
|
||||
last?: number | null;
|
||||
filter?: VoucherFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { ActiveTab, BulkAction, Dialog, Pagination } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
import { SaleDetailsPageTab } from "./components/SaleDetailsPage";
|
||||
import { VoucherDetailsPageTab } from "./components/VoucherDetailsPage";
|
||||
|
||||
|
@ -9,10 +16,16 @@ export const discountSection = "/discounts/";
|
|||
|
||||
export const saleSection = urlJoin(discountSection, "sales");
|
||||
export const saleListPath = saleSection;
|
||||
export type SaleListUrlDialog = "remove";
|
||||
export type SaleListUrlQueryParams = BulkAction &
|
||||
export enum SaleListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum>;
|
||||
export type SaleListUrlDialog = "remove" | TabActionDialog;
|
||||
export type SaleListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<SaleListUrlDialog> &
|
||||
Pagination;
|
||||
Pagination &
|
||||
SaleListUrlFilters;
|
||||
export const saleListUrl = (params?: SaleListUrlQueryParams) =>
|
||||
saleListPath + "?" + stringifyQs(params);
|
||||
export const salePath = (id: string) => urlJoin(saleSection, id);
|
||||
|
@ -35,10 +48,16 @@ export const saleAddUrl = saleAddPath;
|
|||
|
||||
export const voucherSection = urlJoin(discountSection, "vouchers");
|
||||
export const voucherListPath = voucherSection;
|
||||
export type VoucherListUrlDialog = "remove";
|
||||
export type VoucherListUrlQueryParams = BulkAction &
|
||||
export enum VoucherListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum>;
|
||||
export type VoucherListUrlDialog = "remove" | TabActionDialog;
|
||||
export type VoucherListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<VoucherListUrlDialog> &
|
||||
Pagination;
|
||||
Pagination &
|
||||
VoucherListUrlFilters;
|
||||
export const voucherListUrl = (params?: VoucherListUrlQueryParams) =>
|
||||
voucherListPath + "?" + stringifyQs(params);
|
||||
export const voucherPath = (id: string) => urlJoin(voucherSection, id);
|
||||
|
|
|
@ -5,6 +5,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
|
@ -17,16 +21,26 @@ import useShop from "@saleor/hooks/useShop";
|
|||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import SaleListPage from "../components/SaleListPage";
|
||||
import { TypedSaleBulkDelete } from "../mutations";
|
||||
import { TypedSaleList } from "../queries";
|
||||
import { SaleBulkDelete } from "../types/SaleBulkDelete";
|
||||
import SaleListPage from "../../components/SaleListPage";
|
||||
import { TypedSaleBulkDelete } from "../../mutations";
|
||||
import { TypedSaleList } from "../../queries";
|
||||
import { SaleBulkDelete } from "../../types/SaleBulkDelete";
|
||||
import {
|
||||
saleAddUrl,
|
||||
saleListUrl,
|
||||
SaleListUrlDialog,
|
||||
SaleListUrlFilters,
|
||||
SaleListUrlQueryParams,
|
||||
saleUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface SaleListProps {
|
||||
params: SaleListUrlQueryParams;
|
||||
|
@ -47,13 +61,79 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const closeModal = () => navigate(saleListUrl(), true);
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: SaleListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
saleListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
saleListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
const openModal = (action: SaleListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
saleListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
saleListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(saleListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
|
||||
|
||||
return (
|
||||
<TypedSaleList displayLoader variables={paginationState}>
|
||||
<TypedSaleList displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.sales.pageInfo),
|
||||
|
@ -91,6 +171,14 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
|
|||
<>
|
||||
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
|
||||
<SaleListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(saleListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
defaultCurrency={maybe(() => shop.defaultCurrency)}
|
||||
sales={maybe(() => data.sales.edges.map(edge => edge.node))}
|
||||
settings={settings}
|
||||
|
@ -150,6 +238,19 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
|
|||
</DialogContentText>
|
||||
)}
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/discounts/views/SaleList/filter.ts
Normal file
31
src/discounts/views/SaleList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { SaleFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
SaleListUrlFilters,
|
||||
SaleListUrlFiltersEnum,
|
||||
SaleListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const SALE_FILTERS_KEY = "saleFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: SaleListUrlFilters
|
||||
): SaleFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<SaleListUrlFilters>(SALE_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
SaleListUrlQueryParams,
|
||||
SaleListUrlFilters
|
||||
>(SaleListUrlFiltersEnum);
|
2
src/discounts/views/SaleList/index.ts
Normal file
2
src/discounts/views/SaleList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./SaleList";
|
||||
export * from "./SaleList";
|
|
@ -5,6 +5,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
|
@ -17,16 +21,26 @@ import useShop from "@saleor/hooks/useShop";
|
|||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import VoucherListPage from "../components/VoucherListPage";
|
||||
import { TypedVoucherBulkDelete } from "../mutations";
|
||||
import { TypedVoucherList } from "../queries";
|
||||
import { VoucherBulkDelete } from "../types/VoucherBulkDelete";
|
||||
import VoucherListPage from "../../components/VoucherListPage";
|
||||
import { TypedVoucherBulkDelete } from "../../mutations";
|
||||
import { TypedVoucherList } from "../../queries";
|
||||
import { VoucherBulkDelete } from "../../types/VoucherBulkDelete";
|
||||
import {
|
||||
voucherAddUrl,
|
||||
voucherListUrl,
|
||||
VoucherListUrlDialog,
|
||||
VoucherListUrlFilters,
|
||||
VoucherListUrlQueryParams,
|
||||
voucherUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface VoucherListProps {
|
||||
params: VoucherListUrlQueryParams;
|
||||
|
@ -47,13 +61,79 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const closeModal = () => navigate(voucherListUrl(), true);
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: VoucherListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
voucherListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
voucherListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
const openModal = (action: VoucherListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
voucherListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
voucherListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(voucherListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
|
||||
|
||||
return (
|
||||
<TypedVoucherList displayLoader variables={paginationState}>
|
||||
<TypedVoucherList displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.vouchers.pageInfo),
|
||||
|
@ -92,6 +172,14 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
|
|||
title={intl.formatMessage(sectionNames.vouchers)}
|
||||
/>
|
||||
<VoucherListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(voucherListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
defaultCurrency={maybe(() => shop.defaultCurrency)}
|
||||
settings={settings}
|
||||
vouchers={maybe(() =>
|
||||
|
@ -153,6 +241,19 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
|
|||
</DialogContentText>
|
||||
)}
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/discounts/views/VoucherList/filter.ts
Normal file
31
src/discounts/views/VoucherList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { VoucherFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
VoucherListUrlFilters,
|
||||
VoucherListUrlFiltersEnum,
|
||||
VoucherListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const VOUCHER_FILTERS_KEY = "VoucherFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: VoucherListUrlFilters
|
||||
): VoucherFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<VoucherListUrlFilters>(VOUCHER_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
VoucherListUrlQueryParams,
|
||||
VoucherListUrlFilters
|
||||
>(VoucherListUrlFiltersEnum);
|
2
src/discounts/views/VoucherList/index.ts
Normal file
2
src/discounts/views/VoucherList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./VoucherList";
|
||||
export * from "./VoucherList";
|
|
@ -3,7 +3,9 @@ import {
|
|||
FetchMoreProps,
|
||||
FilterPageProps,
|
||||
ListActions,
|
||||
PageListProps
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "./types";
|
||||
|
||||
const pageInfo = {
|
||||
|
@ -46,23 +48,26 @@ export const countries = [
|
|||
{ code: "AS", label: "American Samoa" }
|
||||
];
|
||||
|
||||
export const filterPageProps: FilterPageProps<{}, unknown> = {
|
||||
currencySymbol: "USD",
|
||||
export const tabPageProps: TabPageProps = {
|
||||
currentTab: 0,
|
||||
filterTabs: [
|
||||
{
|
||||
data: {},
|
||||
name: "Tab X"
|
||||
}
|
||||
],
|
||||
filtersList: [],
|
||||
initialSearch: "",
|
||||
onAll: () => undefined,
|
||||
onFilterAdd: () => undefined,
|
||||
onFilterDelete: () => undefined,
|
||||
onFilterSave: () => undefined,
|
||||
onSearchChange: () => undefined,
|
||||
onTabChange: () => undefined
|
||||
onTabChange: () => undefined,
|
||||
onTabDelete: () => undefined,
|
||||
onTabSave: () => undefined,
|
||||
tabs: ["Tab X"]
|
||||
};
|
||||
|
||||
export const searchPageProps: SearchPageProps = {
|
||||
initialSearch: "",
|
||||
onSearchChange: () => undefined
|
||||
};
|
||||
|
||||
export const filterPageProps: FilterPageProps = {
|
||||
...searchPageProps,
|
||||
...tabPageProps,
|
||||
currencySymbol: "USD",
|
||||
filtersList: [],
|
||||
onFilterAdd: () => undefined
|
||||
};
|
||||
|
||||
export const filters: Filter[] = [
|
||||
|
|
|
@ -6,18 +6,36 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "@saleor/types";
|
||||
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
||||
import OrderDraftList from "../OrderDraftList";
|
||||
|
||||
export interface OrderDraftListPageProps extends PageListProps, ListActions {
|
||||
export interface OrderDraftListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
orders: OrderDraftList_draftOrders_edges_node[];
|
||||
}
|
||||
|
||||
const OrderDraftListPage: React.StatelessComponent<OrderDraftListPageProps> = ({
|
||||
currentTab,
|
||||
disabled,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -38,6 +56,23 @@ const OrderDraftListPage: React.StatelessComponent<OrderDraftListPageProps> = ({
|
|||
</Button>
|
||||
</PageHeader>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Drafts",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Draft"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<OrderDraftList disabled={disabled} {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
|
|
|
@ -8,9 +8,8 @@ import FilterBar from "@saleor/components/FilterBar";
|
|||
import TimezoneContext from "@saleor/components/Timezone";
|
||||
import { FilterProps } from "../../../types";
|
||||
import { OrderStatusFilter } from "../../../types/globalTypes";
|
||||
import { OrderListUrlFilters } from "../../urls";
|
||||
|
||||
type OrderListFilterProps = FilterProps<OrderListUrlFilters, OrderFilterKeys>;
|
||||
type OrderListFilterProps = FilterProps<OrderFilterKeys>;
|
||||
|
||||
export enum OrderFilterKeys {
|
||||
date = "date",
|
||||
|
|
|
@ -7,17 +7,15 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { OrderFilterKeys } from "@saleor/orders/components/OrderListFilter";
|
||||
import { FilterPageProps, ListActions, PageListProps } from "@saleor/types";
|
||||
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
||||
import { OrderListUrlFilters } from "../../urls";
|
||||
import OrderList from "../OrderList";
|
||||
import OrderListFilter from "../OrderListFilter";
|
||||
import OrderListFilter, { OrderFilterKeys } from "../OrderListFilter";
|
||||
|
||||
export interface OrderListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
FilterPageProps<OrderListUrlFilters, OrderFilterKeys> {
|
||||
FilterPageProps<OrderFilterKeys> {
|
||||
orders: OrderList_orders_edges_node[];
|
||||
}
|
||||
|
||||
|
@ -25,15 +23,15 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
|||
currencySymbol,
|
||||
currentTab,
|
||||
filtersList,
|
||||
filterTabs,
|
||||
initialSearch,
|
||||
tabs,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onFilterAdd,
|
||||
onFilterSave,
|
||||
onTabChange,
|
||||
onFilterDelete,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -59,7 +57,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
|||
filterLabel={intl.formatMessage({
|
||||
defaultMessage: "Select all orders where:"
|
||||
})}
|
||||
filterTabs={filterTabs}
|
||||
tabs={tabs}
|
||||
filtersList={filtersList}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
|
@ -68,9 +66,9 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
|||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onFilterAdd={onFilterAdd}
|
||||
onFilterSave={onFilterSave}
|
||||
onTabChange={onTabChange}
|
||||
onFilterDelete={onFilterDelete}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<OrderList {...listProps} />
|
||||
</Card>
|
||||
|
|
|
@ -168,7 +168,6 @@ export const orderListQuery = gql`
|
|||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
$status: OrderStatusFilter
|
||||
$filter: OrderFilterInput
|
||||
) {
|
||||
orders(
|
||||
|
@ -176,7 +175,6 @@ export const orderListQuery = gql`
|
|||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
status: $status
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
|
@ -221,8 +219,15 @@ export const orderDraftListQuery = gql`
|
|||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
$filter: OrderDraftFilterInput
|
||||
) {
|
||||
draftOrders(before: $before, after: $after, first: $first, last: $last) {
|
||||
draftOrders(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||
import { OrderDraftFilterInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: OrderDraftList
|
||||
|
@ -81,4 +81,5 @@ export interface OrderDraftListVariables {
|
|||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
filter?: OrderDraftFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { OrderStatusFilter, OrderFilterInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||
import { OrderFilterInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: OrderList
|
||||
|
@ -81,6 +81,5 @@ export interface OrderListVariables {
|
|||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
status?: OrderStatusFilter | null;
|
||||
filter?: OrderFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import {
|
|||
Filters,
|
||||
FiltersWithMultipleValues,
|
||||
Pagination,
|
||||
SingleAction
|
||||
SingleAction,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const orderSectionUrl = "/orders";
|
||||
|
@ -18,14 +19,15 @@ export enum OrderListUrlFiltersEnum {
|
|||
dateFrom = "dateFrom",
|
||||
dateTo = "dateTo",
|
||||
email = "email",
|
||||
payment = "payment"
|
||||
payment = "payment",
|
||||
query = "query"
|
||||
}
|
||||
export enum OrderListUrlFiltersWithMultipleValuesEnum {
|
||||
status = "status"
|
||||
}
|
||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
||||
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
||||
export type OrderListUrlDialog = "cancel" | "save-search" | "delete-search";
|
||||
export type OrderListUrlDialog = "cancel" | TabActionDialog;
|
||||
export type OrderListUrlQueryParams = BulkAction &
|
||||
Dialog<OrderListUrlDialog> &
|
||||
OrderListUrlFilters &
|
||||
|
@ -41,9 +43,15 @@ export const orderListUrl = (params?: OrderListUrlQueryParams): string => {
|
|||
};
|
||||
|
||||
export const orderDraftListPath = urlJoin(orderSectionUrl, "drafts");
|
||||
export type OrderDraftListUrlDialog = "remove";
|
||||
export type OrderDraftListUrlQueryParams = BulkAction &
|
||||
export enum OrderDraftListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
|
||||
export type OrderDraftListUrlDialog = "remove" | TabActionDialog;
|
||||
export type OrderDraftListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<OrderDraftListUrlDialog> &
|
||||
OrderDraftListUrlFilters &
|
||||
Pagination;
|
||||
export const orderDraftListUrl = (
|
||||
params?: OrderDraftListUrlQueryParams
|
||||
|
|
|
@ -5,6 +5,10 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
|
@ -14,19 +18,29 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import OrderDraftListPage from "../components/OrderDraftListPage";
|
||||
import OrderDraftListPage from "../../components/OrderDraftListPage";
|
||||
import {
|
||||
TypedOrderDraftBulkCancelMutation,
|
||||
TypedOrderDraftCreateMutation
|
||||
} from "../mutations";
|
||||
import { TypedOrderDraftListQuery } from "../queries";
|
||||
import { OrderDraftBulkCancel } from "../types/OrderDraftBulkCancel";
|
||||
import { OrderDraftCreate } from "../types/OrderDraftCreate";
|
||||
} from "../../mutations";
|
||||
import { TypedOrderDraftListQuery } from "../../queries";
|
||||
import { OrderDraftBulkCancel } from "../../types/OrderDraftBulkCancel";
|
||||
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
||||
import {
|
||||
orderDraftListUrl,
|
||||
OrderDraftListUrlDialog,
|
||||
OrderDraftListUrlFilters,
|
||||
OrderDraftListUrlQueryParams,
|
||||
orderUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface OrderDraftListProps {
|
||||
params: OrderDraftListUrlQueryParams;
|
||||
|
@ -46,13 +60,34 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: OrderDraftListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
orderDraftListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
orderDraftListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
})
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => {
|
||||
|
@ -64,12 +99,49 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
|
|||
navigate(orderUrl(data.draftOrderCreate.order.id));
|
||||
};
|
||||
|
||||
const openModal = (action: OrderDraftListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
orderDraftListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
orderDraftListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(orderDraftListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedOrderDraftCreateMutation onCompleted={handleCreateOrderCreateSuccess}>
|
||||
{createOrder => (
|
||||
<TypedOrderDraftListQuery displayLoader variables={paginationState}>
|
||||
<TypedOrderDraftListQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.draftOrders.pageInfo),
|
||||
|
@ -114,6 +186,14 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
|
|||
return (
|
||||
<>
|
||||
<OrderDraftListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(orderDraftListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
disabled={loading}
|
||||
settings={settings}
|
||||
orders={maybe(() =>
|
||||
|
@ -174,6 +254,19 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
|
|||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/orders/views/OrderDraftList/filter.ts
Normal file
31
src/orders/views/OrderDraftList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { OrderDraftFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
OrderDraftListUrlFilters,
|
||||
OrderDraftListUrlFiltersEnum,
|
||||
OrderDraftListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const ORDER_DRAFT_FILTERS_KEY = "orderDraftFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: OrderDraftListUrlFilters
|
||||
): OrderDraftFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<OrderDraftListUrlFilters>(ORDER_DRAFT_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
OrderDraftListUrlQueryParams,
|
||||
OrderDraftListUrlFilters
|
||||
>(OrderDraftListUrlFiltersEnum);
|
2
src/orders/views/OrderDraftList/index.ts
Normal file
2
src/orders/views/OrderDraftList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./OrderDraftList";
|
||||
export * from "./OrderDraftList";
|
|
@ -228,15 +228,15 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
|
|||
/>
|
||||
</Button>
|
||||
}
|
||||
onSearchChange={email => changeFilterField({ email })}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onFilterAdd={data =>
|
||||
changeFilterField(createFilter(params, data))
|
||||
}
|
||||
onFilterSave={() => openModal("save-search")}
|
||||
onFilterDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabChange={handleTabChange}
|
||||
initialSearch={params.email || ""}
|
||||
filterTabs={getFilterTabs()}
|
||||
initialSearch={params.query || ""}
|
||||
tabs={getFilterTabs().map(tab => tab.name)}
|
||||
onAll={() =>
|
||||
changeFilters({
|
||||
status: undefined
|
||||
|
|
|
@ -10,6 +10,10 @@ Array [
|
|||
"label": "Date to 2019-09-10",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "email@example.com",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Fulfilled",
|
||||
"onClick": [Function],
|
||||
|
@ -88,6 +92,7 @@ Object {
|
|||
"lte": "2019-09-10",
|
||||
},
|
||||
"customer": "email@example.com",
|
||||
"search": "24",
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
"PARTIALLY_FULFILLED",
|
||||
|
@ -102,6 +107,7 @@ Object {
|
|||
"lte": "2019-09-10",
|
||||
},
|
||||
"customer": "email@example.com",
|
||||
"search": "24",
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
],
|
||||
|
|
|
@ -115,6 +115,7 @@ test("Crate filter chips", () => {
|
|||
{
|
||||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
email: "email@example.com",
|
||||
status: [OrderStatus.FULFILLED, OrderStatus.PARTIALLY_FULFILLED]
|
||||
},
|
||||
{
|
||||
|
@ -133,6 +134,7 @@ describe("Get filter variables", () => {
|
|||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
email: "email@example.com",
|
||||
query: "24",
|
||||
status: OrderStatus.FULFILLED.toString()
|
||||
});
|
||||
|
||||
|
@ -144,6 +146,7 @@ describe("Get filter variables", () => {
|
|||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
email: "email@example.com",
|
||||
query: "24",
|
||||
status: [
|
||||
OrderStatus.FULFILLED.toString(),
|
||||
OrderStatus.PARTIALLY_FULFILLED.toString()
|
||||
|
|
|
@ -20,6 +20,7 @@ import { OrderFilterKeys } from "../../components/OrderListFilter";
|
|||
import {
|
||||
OrderListUrlFilters,
|
||||
OrderListUrlFiltersEnum,
|
||||
OrderListUrlFiltersWithMultipleValuesEnum,
|
||||
OrderListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
|
@ -83,6 +84,7 @@ export function getFilterVariables(
|
|||
lte: params.dateTo
|
||||
},
|
||||
customer: params.email,
|
||||
search: params.query,
|
||||
status: Array.isArray(params.status)
|
||||
? params.status.map(status => findInEnum(status, OrderStatusFilter))
|
||||
: params.status
|
||||
|
@ -190,6 +192,20 @@ export function createFilterChips(
|
|||
}
|
||||
}
|
||||
|
||||
if (!!filters.email) {
|
||||
filterChips = [
|
||||
...filterChips,
|
||||
{
|
||||
label: filters.email,
|
||||
onClick: () =>
|
||||
onFilterDelete({
|
||||
...filters,
|
||||
email: undefined
|
||||
})
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (!!filters.status) {
|
||||
const statusFilterChips = Array.isArray(filters.status)
|
||||
? filters.status.map((status, statusIndex) => ({
|
||||
|
@ -228,4 +244,7 @@ export const {
|
|||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
OrderListUrlQueryParams,
|
||||
OrderListUrlFilters
|
||||
>(OrderListUrlFiltersEnum);
|
||||
>({
|
||||
...OrderListUrlFiltersEnum,
|
||||
...OrderListUrlFiltersWithMultipleValuesEnum
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -70,138 +69,132 @@ const ProductTypeList = withStyles(styles, { name: "ProductTypeList" })(
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={productTypes}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Type Name"
|
||||
description="product type name"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Type"
|
||||
description="product type is either simple or configurable"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTax}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Tax"
|
||||
description="tax rate for a product type"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : false
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
productTypes,
|
||||
productType => {
|
||||
const isSelected = productType
|
||||
? isChecked(productType.id)
|
||||
: false;
|
||||
return (
|
||||
<TableRow
|
||||
className={!!productType ? classes.link : undefined}
|
||||
hover={!!productType}
|
||||
key={productType ? productType.id : "skeleton"}
|
||||
onClick={
|
||||
productType ? onRowClick(productType.id) : undefined
|
||||
}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(productType.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{productType ? (
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={productTypes}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Type Name"
|
||||
description="product type name"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Type"
|
||||
description="product type is either simple or configurable"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTax}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Tax"
|
||||
description="tax rate for a product type"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||
onNextPage={onNextPage}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
productTypes,
|
||||
productType => {
|
||||
const isSelected = productType
|
||||
? isChecked(productType.id)
|
||||
: false;
|
||||
return (
|
||||
<TableRow
|
||||
className={!!productType ? classes.link : undefined}
|
||||
hover={!!productType}
|
||||
key={productType ? productType.id : "skeleton"}
|
||||
onClick={productType ? onRowClick(productType.id) : undefined}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(productType.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colName}>
|
||||
{productType ? (
|
||||
<>
|
||||
{productType.name}
|
||||
<Typography variant="caption">
|
||||
{maybe(() => productType.hasVariants)
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Configurable",
|
||||
description: "product type"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Simple product",
|
||||
description: "product type"
|
||||
})}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
{maybe(() => productType.isShippingRequired) !==
|
||||
undefined ? (
|
||||
productType.isShippingRequired ? (
|
||||
<>
|
||||
{productType.name}
|
||||
<Typography variant="caption">
|
||||
{maybe(() => productType.hasVariants)
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Configurable",
|
||||
description: "product type"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Simple product",
|
||||
description: "product type"
|
||||
})}
|
||||
</Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Physical"
|
||||
description="product type"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colType}>
|
||||
{maybe(() => productType.isShippingRequired) !==
|
||||
undefined ? (
|
||||
productType.isShippingRequired ? (
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Physical"
|
||||
description="product type"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Digital"
|
||||
description="product type"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTax}>
|
||||
{maybe(() => productType.taxType) ? (
|
||||
productType.taxType.description
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No product types found" />
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Digital"
|
||||
description="product type"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTax}>
|
||||
{maybe(() => productType.taxType) ? (
|
||||
productType.taxType.description
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage defaultMessage="No product types found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,23 +1,46 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "../../../types";
|
||||
import {
|
||||
ListActions,
|
||||
PageListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps
|
||||
} from "../../../types";
|
||||
import { ProductTypeList_productTypes_edges_node } from "../../types/ProductTypeList";
|
||||
import ProductTypeList from "../ProductTypeList";
|
||||
|
||||
interface ProductTypeListPageProps extends PageListProps, ListActions {
|
||||
export interface ProductTypeListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
productTypes: ProductTypeList_productTypes_edges_node[];
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const ProductTypeListPage: React.StatelessComponent<
|
||||
ProductTypeListPageProps
|
||||
> = ({ disabled, onAdd, onBack, ...listProps }) => {
|
||||
> = ({
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onBack,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
|
@ -26,19 +49,33 @@ const ProductTypeListPage: React.StatelessComponent<
|
|||
{intl.formatMessage(sectionNames.configuration)}
|
||||
</AppHeader>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.productTypes)}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
disabled={disabled}
|
||||
onClick={onAdd}
|
||||
>
|
||||
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="create product type"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<ProductTypeList disabled={disabled} {...listProps} />
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Product Types",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Product Type"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<ProductTypeList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -51,8 +51,15 @@ export const productTypeListQuery = gql`
|
|||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
$filter: ProductTypeFilterInput
|
||||
) {
|
||||
productTypes(after: $after, before: $before, first: $first, last: $last) {
|
||||
productTypes(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...ProductTypeFragment
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ProductTypeFilterInput } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: ProductTypeList
|
||||
// ====================================================
|
||||
|
@ -49,4 +51,5 @@ export interface ProductTypeListVariables {
|
|||
before?: string | null;
|
||||
first?: number | null;
|
||||
last?: number | null;
|
||||
filter?: ProductTypeFilterInput | null;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { BulkAction, Dialog, Pagination, SingleAction } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
SingleAction,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const productTypeSection = "/product-types/";
|
||||
|
||||
export const productTypeListPath = productTypeSection;
|
||||
export type ProductTypeListUrlDialog = "remove";
|
||||
export type ProductTypeListUrlQueryParams = BulkAction &
|
||||
export enum ProductTypeListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type ProductTypeListUrlFilters = Filters<ProductTypeListUrlFiltersEnum>;
|
||||
export type ProductTypeListUrlDialog = "remove" | TabActionDialog;
|
||||
export type ProductTypeListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<ProductTypeListUrlDialog> &
|
||||
Pagination;
|
||||
Pagination &
|
||||
ProductTypeListUrlFilters;
|
||||
export const productTypeListUrl = (params?: ProductTypeListUrlQueryParams) =>
|
||||
productTypeListPath + "?" + stringifyQs(params);
|
||||
|
||||
|
|
|
@ -5,26 +5,41 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import usePaginator, {
|
||||
createPaginationState
|
||||
} from "@saleor/hooks/usePaginator";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { PAGINATE_BY } from "../../config";
|
||||
import { configurationMenuUrl } from "../../configuration";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import ProductTypeListPage from "../components/ProductTypeListPage";
|
||||
import { TypedProductTypeBulkDeleteMutation } from "../mutations";
|
||||
import { TypedProductTypeListQuery } from "../queries";
|
||||
import { ProductTypeBulkDelete } from "../types/ProductTypeBulkDelete";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import { configurationMenuUrl } from "../../../configuration";
|
||||
import { getMutationState, maybe } from "../../../misc";
|
||||
import ProductTypeListPage from "../../components/ProductTypeListPage";
|
||||
import { TypedProductTypeBulkDeleteMutation } from "../../mutations";
|
||||
import { TypedProductTypeListQuery } from "../../queries";
|
||||
import { ProductTypeBulkDelete } from "../../types/ProductTypeBulkDelete";
|
||||
import {
|
||||
productTypeAddUrl,
|
||||
productTypeListUrl,
|
||||
ProductTypeListUrlDialog,
|
||||
ProductTypeListUrlFilters,
|
||||
ProductTypeListUrlQueryParams,
|
||||
productTypeUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface ProductTypeListProps {
|
||||
params: ProductTypeListUrlQueryParams;
|
||||
|
@ -39,13 +54,80 @@ export const ProductTypeList: React.StatelessComponent<
|
|||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||
params.ids
|
||||
);
|
||||
const { settings } = useListSettings(ListViews.PRODUCT_LIST);
|
||||
const intl = useIntl();
|
||||
|
||||
const closeModal = () => navigate(productTypeListUrl(), true);
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: ProductTypeListUrlFilters) => {
|
||||
reset();
|
||||
navigate(
|
||||
productTypeListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
productTypeListUrl({
|
||||
...params,
|
||||
action: undefined,
|
||||
ids: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
||||
const openModal = (action: ProductTypeListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
productTypeListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
reset();
|
||||
navigate(
|
||||
productTypeListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(productTypeListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
return (
|
||||
<TypedProductTypeListQuery displayLoader variables={paginationState}>
|
||||
<TypedProductTypeListQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.productTypes.pageInfo),
|
||||
|
@ -93,6 +175,14 @@ export const ProductTypeList: React.StatelessComponent<
|
|||
return (
|
||||
<>
|
||||
<ProductTypeListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(productTypeListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
disabled={loading}
|
||||
productTypes={maybe(() =>
|
||||
data.productTypes.edges.map(edge => edge.node)
|
||||
|
@ -150,6 +240,19 @@ export const ProductTypeList: React.StatelessComponent<
|
|||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/productTypes/views/ProductTypeList/filter.ts
Normal file
31
src/productTypes/views/ProductTypeList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { ProductTypeFilterInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
ProductTypeListUrlFilters,
|
||||
ProductTypeListUrlFiltersEnum,
|
||||
ProductTypeListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const PRODUCT_TYPE_FILTERS_KEY = "productTypeFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: ProductTypeListUrlFilters
|
||||
): ProductTypeFilterInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<ProductTypeListUrlFilters>(PRODUCT_TYPE_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
ProductTypeListUrlQueryParams,
|
||||
ProductTypeListUrlFilters
|
||||
>(ProductTypeListUrlFiltersEnum);
|
2
src/productTypes/views/ProductTypeList/index.ts
Normal file
2
src/productTypes/views/ProductTypeList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./ProductTypeList";
|
||||
export * from "./ProductTypeList";
|
|
@ -5,11 +5,10 @@ import { FieldType, IFilter } from "@saleor/components/Filter";
|
|||
import FilterBar from "@saleor/components/FilterBar";
|
||||
import { FilterProps } from "@saleor/types";
|
||||
import { StockAvailability } from "@saleor/types/globalTypes";
|
||||
import { ProductListUrlFilters } from "../../urls";
|
||||
|
||||
type ProductListFilterProps = FilterProps<
|
||||
ProductListUrlFilters,
|
||||
ProductFilterKeys
|
||||
type ProductListFilterProps = Omit<
|
||||
FilterProps,
|
||||
"allTabLabel" | "filterLabel" | "searchPlaceholder"
|
||||
>;
|
||||
|
||||
export enum ProductFilterKeys {
|
||||
|
@ -133,7 +132,22 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
}
|
||||
];
|
||||
|
||||
return <FilterBar {...props} filterMenu={filterMenu} />;
|
||||
return (
|
||||
<FilterBar
|
||||
{...props}
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Products",
|
||||
description: "tab name"
|
||||
})}
|
||||
filterMenu={filterMenu}
|
||||
filterLabel={intl.formatMessage({
|
||||
defaultMessage: "Select all products where:"
|
||||
})}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Products..."
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
ProductListFilter.displayName = "ProductListFilter";
|
||||
export default ProductListFilter;
|
||||
|
|
|
@ -24,14 +24,13 @@ import {
|
|||
ListActions,
|
||||
PageListProps
|
||||
} from "@saleor/types";
|
||||
import { ProductListUrlFilters } from "../../urls";
|
||||
import ProductList from "../ProductList";
|
||||
import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter";
|
||||
|
||||
export interface ProductListPageProps
|
||||
extends PageListProps<ProductListColumns>,
|
||||
ListActions,
|
||||
FilterPageProps<ProductListUrlFilters, ProductFilterKeys>,
|
||||
FilterPageProps<ProductFilterKeys>,
|
||||
FetchMoreProps {
|
||||
availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[];
|
||||
currencySymbol: string;
|
||||
|
@ -52,22 +51,22 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
|||
currentTab,
|
||||
defaultSettings,
|
||||
filtersList,
|
||||
filterTabs,
|
||||
gridAttributes,
|
||||
availableInGridAttributes,
|
||||
hasMore,
|
||||
initialSearch,
|
||||
loading,
|
||||
settings,
|
||||
tabs,
|
||||
totalGridAttributes,
|
||||
onAdd,
|
||||
onAll,
|
||||
onFetchMore,
|
||||
onSearchChange,
|
||||
onFilterAdd,
|
||||
onFilterSave,
|
||||
onTabChange,
|
||||
onFilterDelete,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
onUpdateListSettings,
|
||||
...listProps
|
||||
} = props;
|
||||
|
@ -137,27 +136,17 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
|||
</PageHeader>
|
||||
<Card>
|
||||
<ProductListFilter
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Products",
|
||||
description: "tab name"
|
||||
})}
|
||||
currencySymbol={currencySymbol}
|
||||
currentTab={currentTab}
|
||||
filterLabel={intl.formatMessage({
|
||||
defaultMessage: "Select all products where:"
|
||||
})}
|
||||
filterTabs={filterTabs}
|
||||
filtersList={filtersList}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Products..."
|
||||
})}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onFilterAdd={onFilterAdd}
|
||||
onFilterSave={onFilterSave}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onFilterDelete={onFilterDelete}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
tabs={tabs}
|
||||
/>
|
||||
<ProductList
|
||||
{...listProps}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { ActiveTab, BulkAction, Dialog, Filters, Pagination } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const productSection = "/products/";
|
||||
|
||||
|
@ -13,8 +20,7 @@ export type ProductListUrlDialog =
|
|||
| "publish"
|
||||
| "unpublish"
|
||||
| "delete"
|
||||
| "save-search"
|
||||
| "delete-search";
|
||||
| TabActionDialog;
|
||||
export enum ProductListUrlFiltersEnum {
|
||||
isPublished = "isPublished",
|
||||
priceFrom = "priceFrom",
|
||||
|
|
|
@ -351,11 +351,11 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
|||
onFilterAdd={filter =>
|
||||
changeFilterField(createFilter(filter))
|
||||
}
|
||||
onFilterSave={() => openModal("save-search")}
|
||||
onFilterDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabChange={handleTabChange}
|
||||
initialSearch={params.query || ""}
|
||||
filterTabs={getFilterTabs()}
|
||||
tabs={getFilterTabs().map(tab => tab.name)}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "delete"}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { defineMessages, IntlShape } from "react-intl";
|
||||
|
||||
import { FilterContentSubmitData } from "../../../components/Filter";
|
||||
import { Filter } from "../../../components/TableFilter";
|
||||
import {
|
||||
|
|
|
@ -33,7 +33,7 @@ const ShippingZoneDetails: React.StatelessComponent<
|
|||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
|
||||
const closeModal = () => navigate(shippingZoneUrl(id));
|
||||
const closeModal = () => navigate(shippingZoneUrl(id), true);
|
||||
|
||||
const onShippingRateCreate = (data: CreateShippingRate) => {
|
||||
if (data.shippingPriceCreate.errors.length === 0) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
@ -84,108 +83,102 @@ const StaffList = withStyles(styles, { name: "StaffList" })(
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className={classes.wideColumn}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Name"
|
||||
description="staff member full name"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage defaultMessage="Email Address" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={3}
|
||||
settings={settings}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : undefined
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : undefined
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className={classes.wideColumn}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Name"
|
||||
description="staff member full name"
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
staffMembers,
|
||||
staffMember => (
|
||||
<TableRow
|
||||
className={classNames({
|
||||
[classes.tableRow]: !!staffMember
|
||||
})}
|
||||
hover={!!staffMember}
|
||||
onClick={
|
||||
!!staffMember ? onRowClick(staffMember.id) : undefined
|
||||
}
|
||||
key={staffMember ? staffMember.id : "skeleton"}
|
||||
>
|
||||
<TableCell>
|
||||
<div className={classes.avatar}>
|
||||
{maybe(() => staffMember.avatar.url) ? (
|
||||
<img
|
||||
className={classes.avatarImage}
|
||||
src={maybe(() => staffMember.avatar.url)}
|
||||
/>
|
||||
) : (
|
||||
<div className={classes.avatarDefault}>
|
||||
<Typography>
|
||||
{getUserInitials(staffMember)}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Typography>
|
||||
{getUserName(staffMember) || <Skeleton />}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"caption"}
|
||||
className={classes.statusText}
|
||||
>
|
||||
{maybe<React.ReactNode>(
|
||||
() =>
|
||||
staffMember.isActive
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Active",
|
||||
description: "staff member status"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Inactive",
|
||||
description: "staff member status"
|
||||
}),
|
||||
<Skeleton />
|
||||
)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage defaultMessage="Email Address" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={3}
|
||||
settings={settings}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : undefined
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : undefined
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
staffMembers,
|
||||
staffMember => (
|
||||
<TableRow
|
||||
className={classNames({
|
||||
[classes.tableRow]: !!staffMember
|
||||
})}
|
||||
hover={!!staffMember}
|
||||
onClick={!!staffMember ? onRowClick(staffMember.id) : undefined}
|
||||
key={staffMember ? staffMember.id : "skeleton"}
|
||||
>
|
||||
<TableCell>
|
||||
<div className={classes.avatar}>
|
||||
{maybe(() => staffMember.avatar.url) ? (
|
||||
<img
|
||||
className={classes.avatarImage}
|
||||
src={maybe(() => staffMember.avatar.url)}
|
||||
/>
|
||||
) : (
|
||||
<div className={classes.avatarDefault}>
|
||||
<Typography>{getUserInitials(staffMember)}</Typography>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Typography>
|
||||
{getUserName(staffMember) || <Skeleton />}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"caption"}
|
||||
className={classes.statusText}
|
||||
>
|
||||
{maybe<React.ReactNode>(
|
||||
() => staffMember.email,
|
||||
() =>
|
||||
staffMember.isActive
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Active",
|
||||
description: "staff member status"
|
||||
})
|
||||
: intl.formatMessage({
|
||||
defaultMessage: "Inactive",
|
||||
description: "staff member status"
|
||||
}),
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3}>
|
||||
<FormattedMessage defaultMessage="No staff members found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{maybe<React.ReactNode>(
|
||||
() => staffMember.email,
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3}>
|
||||
<FormattedMessage defaultMessage="No staff members found" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import Card from "@material-ui/core/Card";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { Container } from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SearchBar from "@saleor/components/SearchBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListProps } from "@saleor/types";
|
||||
import { ListProps, SearchPageProps, TabPageProps } from "@saleor/types";
|
||||
import { StaffList_staffUsers_edges_node } from "../../types/StaffList";
|
||||
import StaffList from "../StaffList/StaffList";
|
||||
|
||||
export interface StaffListPageProps extends ListProps {
|
||||
export interface StaffListPageProps
|
||||
extends ListProps,
|
||||
SearchPageProps,
|
||||
TabPageProps {
|
||||
staffMembers: StaffList_staffUsers_edges_node[];
|
||||
onAdd: () => void;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const StaffListPage: React.StatelessComponent<StaffListPageProps> = ({
|
||||
disabled,
|
||||
currentTab,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onBack,
|
||||
onSearchChange,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
tabs,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
@ -31,19 +42,33 @@ const StaffListPage: React.StatelessComponent<StaffListPageProps> = ({
|
|||
{intl.formatMessage(sectionNames.configuration)}
|
||||
</AppHeader>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.staff)}>
|
||||
<Button
|
||||
color="primary"
|
||||
disabled={disabled}
|
||||
variant="contained"
|
||||
onClick={onAdd}
|
||||
>
|
||||
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Invite staff member"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<StaffList disabled={disabled} {...listProps} />
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
defaultMessage: "All Staff Members",
|
||||
description: "tab name"
|
||||
})}
|
||||
currentTab={currentTab}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
defaultMessage: "Search Staff Member"
|
||||
})}
|
||||
tabs={tabs}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
/>
|
||||
<StaffList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -30,8 +30,20 @@ export const staffMemberDetailsFragment = gql`
|
|||
`;
|
||||
const staffList = gql`
|
||||
${staffMemberFragment}
|
||||
query StaffList($first: Int, $after: String, $last: Int, $before: String) {
|
||||
staffUsers(before: $before, after: $after, first: $first, last: $last) {
|
||||
query StaffList(
|
||||
$first: Int
|
||||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
$filter: StaffUserInput
|
||||
) {
|
||||
staffUsers(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PermissionEnum } from "./../../types/globalTypes";
|
||||
import { StaffUserInput, PermissionEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: StaffList
|
||||
|
@ -64,4 +64,5 @@ export interface StaffListVariables {
|
|||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
filter?: StaffUserInput | null;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,28 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { BulkAction, Dialog, Pagination } from "../types";
|
||||
import {
|
||||
ActiveTab,
|
||||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
Pagination,
|
||||
TabActionDialog
|
||||
} from "../types";
|
||||
|
||||
const staffSection = "/staff/";
|
||||
|
||||
export const staffListPath = staffSection;
|
||||
export type StaffListUrlDialog = "add" | "remove";
|
||||
export type StaffListUrlQueryParams = BulkAction &
|
||||
export enum StaffListUrlFiltersEnum {
|
||||
query = "query"
|
||||
}
|
||||
export type StaffListUrlFilters = Filters<StaffListUrlFiltersEnum>;
|
||||
export type StaffListUrlDialog = "add" | "remove" | TabActionDialog;
|
||||
export type StaffListUrlQueryParams = ActiveTab &
|
||||
BulkAction &
|
||||
Dialog<StaffListUrlDialog> &
|
||||
Pagination;
|
||||
Pagination &
|
||||
StaffListUrlFilters;
|
||||
export const staffListUrl = (params?: StaffListUrlQueryParams) =>
|
||||
staffListPath + "?" + stringifyQs(params);
|
||||
|
||||
|
|
|
@ -8,22 +8,36 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData
|
||||
} from "@saleor/components/SaveFilterTabDialog";
|
||||
import { configurationMenuUrl } from "@saleor/configuration";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import StaffAddMemberDialog, {
|
||||
FormData as AddStaffMemberForm
|
||||
} from "../components/StaffAddMemberDialog";
|
||||
import StaffListPage from "../components/StaffListPage";
|
||||
import { TypedStaffMemberAddMutation } from "../mutations";
|
||||
import { TypedStaffListQuery } from "../queries";
|
||||
import { StaffMemberAdd } from "../types/StaffMemberAdd";
|
||||
} from "../../components/StaffAddMemberDialog";
|
||||
import StaffListPage from "../../components/StaffListPage";
|
||||
import { TypedStaffMemberAddMutation } from "../../mutations";
|
||||
import { TypedStaffListQuery } from "../../queries";
|
||||
import { StaffMemberAdd } from "../../types/StaffMemberAdd";
|
||||
import {
|
||||
staffListUrl,
|
||||
StaffListUrlDialog,
|
||||
StaffListUrlFilters,
|
||||
StaffListUrlQueryParams,
|
||||
staffMemberDetailsUrl
|
||||
} from "../urls";
|
||||
} from "../../urls";
|
||||
import {
|
||||
areFiltersApplied,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab
|
||||
} from "./filter";
|
||||
|
||||
interface StaffListProps {
|
||||
params: StaffListUrlQueryParams;
|
||||
|
@ -40,6 +54,24 @@ export const StaffList: React.StatelessComponent<StaffListProps> = ({
|
|||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab =
|
||||
params.activeTab === undefined
|
||||
? areFiltersApplied(params)
|
||||
? tabs.length + 1
|
||||
: 0
|
||||
: parseInt(params.activeTab, 0);
|
||||
|
||||
const changeFilterField = (filter: StaffListUrlFilters) =>
|
||||
navigate(
|
||||
staffListUrl({
|
||||
...getActiveFilters(params),
|
||||
...filter,
|
||||
activeTab: undefined
|
||||
})
|
||||
);
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
staffListUrl({
|
||||
|
@ -50,9 +82,45 @@ export const StaffList: React.StatelessComponent<StaffListProps> = ({
|
|||
true
|
||||
);
|
||||
|
||||
const openModal = (action: StaffListUrlDialog, ids?: string[]) =>
|
||||
navigate(
|
||||
staffListUrl({
|
||||
...params,
|
||||
action,
|
||||
ids
|
||||
})
|
||||
);
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
navigate(
|
||||
staffListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
navigate(staffListUrl());
|
||||
};
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
() => ({
|
||||
...paginationState,
|
||||
filter: getFilterVariables(params)
|
||||
}),
|
||||
[params]
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedStaffListQuery displayLoader variables={paginationState}>
|
||||
<TypedStaffListQuery displayLoader variables={queryVariables}>
|
||||
{({ data, loading }) => {
|
||||
const handleStaffMemberAddSuccess = (data: StaffMemberAdd) => {
|
||||
if (data.staffCreate.errors.length === 0) {
|
||||
|
@ -97,6 +165,14 @@ export const StaffList: React.StatelessComponent<StaffListProps> = ({
|
|||
return (
|
||||
<>
|
||||
<StaffListPage
|
||||
currentTab={currentTab}
|
||||
initialSearch={params.query || ""}
|
||||
onSearchChange={query => changeFilterField({ query })}
|
||||
onAll={() => navigate(staffListUrl())}
|
||||
onTabChange={handleTabChange}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
tabs={tabs.map(tab => tab.name)}
|
||||
disabled={loading || addStaffMemberData.loading}
|
||||
settings={settings}
|
||||
pageInfo={pageInfo}
|
||||
|
@ -126,6 +202,19 @@ export const StaffList: React.StatelessComponent<StaffListProps> = ({
|
|||
onClose={closeModal}
|
||||
onConfirm={handleStaffMemberAdd}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
31
src/staff/views/StaffList/filter.ts
Normal file
31
src/staff/views/StaffList/filter.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { StaffUserInput } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
} from "../../../utils/filters";
|
||||
import {
|
||||
StaffListUrlFilters,
|
||||
StaffListUrlFiltersEnum,
|
||||
StaffListUrlQueryParams
|
||||
} from "../../urls";
|
||||
|
||||
export const STAFF_FILTERS_KEY = "staffFilters";
|
||||
|
||||
export function getFilterVariables(
|
||||
params: StaffListUrlFilters
|
||||
): StaffUserInput {
|
||||
return {
|
||||
search: params.query
|
||||
};
|
||||
}
|
||||
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab
|
||||
} = createFilterTabUtils<StaffListUrlFilters>(STAFF_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||
StaffListUrlQueryParams,
|
||||
StaffListUrlFilters
|
||||
>(StaffListUrlFiltersEnum);
|
2
src/staff/views/StaffList/index.ts
Normal file
2
src/staff/views/StaffList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./StaffList";
|
||||
export * from "./StaffList";
|
File diff suppressed because it is too large
Load diff
|
@ -5,12 +5,19 @@ import AttributeListPage, {
|
|||
AttributeListPageProps
|
||||
} from "@saleor/attributes/components/AttributeListPage";
|
||||
import { attributes } from "@saleor/attributes/fixtures";
|
||||
import { listActionsProps, pageListProps } from "@saleor/fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
tabPageProps
|
||||
} from "@saleor/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const props: AttributeListPageProps = {
|
||||
...pageListProps.default,
|
||||
...listActionsProps,
|
||||
...tabPageProps,
|
||||
...searchPageProps,
|
||||
attributes
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,12 @@ import React from "react";
|
|||
|
||||
import CategoryListPage from "../../../categories/components/CategoryListPage";
|
||||
import { categories } from "../../../categories/fixtures";
|
||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
tabPageProps
|
||||
} from "../../../fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const categoryTableProps = {
|
||||
|
@ -11,6 +16,8 @@ const categoryTableProps = {
|
|||
onAddCategory: undefined,
|
||||
onCategoryClick: () => undefined,
|
||||
...listActionsProps,
|
||||
...tabPageProps,
|
||||
...searchPageProps,
|
||||
...pageListProps.default
|
||||
};
|
||||
|
||||
|
|
|
@ -5,12 +5,19 @@ import CollectionListPage, {
|
|||
CollectionListPageProps
|
||||
} from "../../../collections/components/CollectionListPage";
|
||||
import { collections } from "../../../collections/fixtures";
|
||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
tabPageProps
|
||||
} from "../../../fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const props: CollectionListPageProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...tabPageProps,
|
||||
collections
|
||||
};
|
||||
|
||||
|
@ -19,4 +26,5 @@ storiesOf("Views / Collections / Collection list", module)
|
|||
.add("default", () => <CollectionListPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<CollectionListPage {...props} collections={undefined} disabled={true} />
|
||||
));
|
||||
))
|
||||
.add("no data", () => <CollectionListPage {...props} collections={[]} />);
|
||||
|
|
|
@ -5,12 +5,19 @@ import CustomerListPage, {
|
|||
CustomerListPageProps
|
||||
} from "../../../customers/components/CustomerListPage";
|
||||
import { customerList } from "../../../customers/fixtures";
|
||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
tabPageProps
|
||||
} from "../../../fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const props: CustomerListPageProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...tabPageProps,
|
||||
customers: customerList
|
||||
};
|
||||
|
||||
|
|
|
@ -5,12 +5,19 @@ import SaleListPage, {
|
|||
SaleListPageProps
|
||||
} from "../../../discounts/components/SaleListPage";
|
||||
import { saleList } from "../../../discounts/fixtures";
|
||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
tabPageProps
|
||||
} from "../../../fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const props: SaleListPageProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...tabPageProps,
|
||||
defaultCurrency: "USD",
|
||||
sales: saleList
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue