Add sorting to lists
This commit is contained in:
parent
97abf33fd2
commit
9c688d3aef
153 changed files with 6897 additions and 2853 deletions
|
@ -21,6 +21,7 @@ All notable, unreleased changes to this project will be documented in this file.
|
||||||
- Enforce using "name" property in style hooks - #288 by @dominik-zeglen
|
- Enforce using "name" property in style hooks - #288 by @dominik-zeglen
|
||||||
- Add ability to reset own password - #289 by @dominik-zeglen
|
- Add ability to reset own password - #289 by @dominik-zeglen
|
||||||
- Move mutation state to mutation - #297 by @dominik-zeglen
|
- Move mutation state to mutation - #297 by @dominik-zeglen
|
||||||
|
- Add table sorting - #292 by @dominik-zeglen
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
|
|
193
schema.graphql
193
schema.graphql
|
@ -318,8 +318,8 @@ enum AttributeSortField {
|
||||||
}
|
}
|
||||||
|
|
||||||
input AttributeSortingInput {
|
input AttributeSortingInput {
|
||||||
field: AttributeSortField!
|
|
||||||
direction: OrderDirection!
|
direction: OrderDirection!
|
||||||
|
field: AttributeSortField
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttributeTranslatableContent implements Node {
|
type AttributeTranslatableContent implements Node {
|
||||||
|
@ -570,6 +570,17 @@ input CategoryInput {
|
||||||
backgroundImageAlt: String
|
backgroundImageAlt: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CategorySortField {
|
||||||
|
NAME
|
||||||
|
PRODUCT_COUNT
|
||||||
|
SUBCATEGORY_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
input CategorySortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: CategorySortField
|
||||||
|
}
|
||||||
|
|
||||||
type CategoryTranslatableContent implements Node {
|
type CategoryTranslatableContent implements Node {
|
||||||
seoTitle: String
|
seoTitle: String
|
||||||
seoDescription: String
|
seoDescription: String
|
||||||
|
@ -950,6 +961,17 @@ type CollectionReorderProducts {
|
||||||
productErrors: [ProductError!]
|
productErrors: [ProductError!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CollectionSortField {
|
||||||
|
NAME
|
||||||
|
AVAILABILITY
|
||||||
|
PRODUCT_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
input CollectionSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: CollectionSortField
|
||||||
|
}
|
||||||
|
|
||||||
type CollectionTranslatableContent implements Node {
|
type CollectionTranslatableContent implements Node {
|
||||||
seoTitle: String
|
seoTitle: String
|
||||||
seoDescription: String
|
seoDescription: String
|
||||||
|
@ -1398,6 +1420,8 @@ input DateTimeRangeInput {
|
||||||
|
|
||||||
scalar Decimal
|
scalar Decimal
|
||||||
|
|
||||||
|
union DefaultTranslationItem = ProductTranslatableContent | CollectionTranslatableContent | CategoryTranslatableContent | AttributeTranslatableContent | AttributeValueTranslatableContent | ProductVariantTranslatableContent | PageTranslatableContent | ShippingMethodTranslatableContent | SaleTranslatableContent | VoucherTranslatableContent | MenuItemTranslatableContent
|
||||||
|
|
||||||
type DigitalContent implements Node {
|
type DigitalContent implements Node {
|
||||||
useDefaultSettings: Boolean!
|
useDefaultSettings: Boolean!
|
||||||
automaticFulfillment: Boolean!
|
automaticFulfillment: Boolean!
|
||||||
|
@ -1990,6 +2014,11 @@ input MenuItemMoveInput {
|
||||||
sortOrder: Int
|
sortOrder: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input MenuItemSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: MenuItemsSortField
|
||||||
|
}
|
||||||
|
|
||||||
type MenuItemTranslatableContent implements Node {
|
type MenuItemTranslatableContent implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
|
@ -2014,6 +2043,20 @@ type MenuItemUpdate {
|
||||||
menuItem: MenuItem
|
menuItem: MenuItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MenuItemsSortField {
|
||||||
|
NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MenuSortField {
|
||||||
|
NAME
|
||||||
|
ITEMS_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
input MenuSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: MenuSortField
|
||||||
|
}
|
||||||
|
|
||||||
type MenuUpdate {
|
type MenuUpdate {
|
||||||
errors: [Error!]
|
errors: [Error!]
|
||||||
menuErrors: [MenuError!]
|
menuErrors: [MenuError!]
|
||||||
|
@ -2575,6 +2618,20 @@ type OrderRefund {
|
||||||
orderErrors: [OrderError!]
|
orderErrors: [OrderError!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum OrderSortField {
|
||||||
|
NUMBER
|
||||||
|
CREATION_DATE
|
||||||
|
CUSTOMER
|
||||||
|
PAYMENT
|
||||||
|
FULFILLMENT_STATUS
|
||||||
|
TOTAL
|
||||||
|
}
|
||||||
|
|
||||||
|
input OrderSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: OrderSortField
|
||||||
|
}
|
||||||
|
|
||||||
enum OrderStatus {
|
enum OrderStatus {
|
||||||
DRAFT
|
DRAFT
|
||||||
UNFULFILLED
|
UNFULFILLED
|
||||||
|
@ -2696,6 +2753,19 @@ input PageInput {
|
||||||
seo: SeoInput
|
seo: SeoInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PageSortField {
|
||||||
|
TITLE
|
||||||
|
SLUG
|
||||||
|
VISIBILITY
|
||||||
|
CREATION_DATE
|
||||||
|
PUBLICATION_DATE
|
||||||
|
}
|
||||||
|
|
||||||
|
input PageSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: PageSortField
|
||||||
|
}
|
||||||
|
|
||||||
type PageTranslatableContent implements Node {
|
type PageTranslatableContent implements Node {
|
||||||
seoTitle: String
|
seoTitle: String
|
||||||
seoDescription: String
|
seoDescription: String
|
||||||
|
@ -2892,6 +2962,16 @@ input PluginFilterInput {
|
||||||
search: String
|
search: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PluginSortField {
|
||||||
|
NAME
|
||||||
|
IS_ACTIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
input PluginSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: PluginSortField
|
||||||
|
}
|
||||||
|
|
||||||
type PluginUpdate {
|
type PluginUpdate {
|
||||||
errors: [Error!]
|
errors: [Error!]
|
||||||
plugin: Plugin
|
plugin: Plugin
|
||||||
|
@ -3115,9 +3195,9 @@ input ProductInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
input ProductOrder {
|
input ProductOrder {
|
||||||
field: ProductOrderField
|
|
||||||
attributeId: ID
|
|
||||||
direction: OrderDirection!
|
direction: OrderDirection!
|
||||||
|
attributeId: ID
|
||||||
|
field: ProductOrderField
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ProductOrderField {
|
enum ProductOrderField {
|
||||||
|
@ -3256,6 +3336,17 @@ type ProductTypeReorderAttributes {
|
||||||
productErrors: [ProductError!]
|
productErrors: [ProductError!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ProductTypeSortField {
|
||||||
|
NAME
|
||||||
|
DIGITAL
|
||||||
|
SHIPPING_REQUIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
input ProductTypeSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: ProductTypeSortField
|
||||||
|
}
|
||||||
|
|
||||||
type ProductTypeUpdate {
|
type ProductTypeUpdate {
|
||||||
errors: [Error!]
|
errors: [Error!]
|
||||||
productErrors: [ProductError!]
|
productErrors: [ProductError!]
|
||||||
|
@ -3436,11 +3527,11 @@ type ProductVariantUpdatePrivateMeta {
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
webhook(id: ID!): Webhook
|
webhook(id: ID!): Webhook
|
||||||
webhooks(filter: WebhookFilterInput, before: String, after: String, first: Int, last: Int): WebhookCountableConnection
|
webhooks(sortBy: WebhookSortingInput, filter: WebhookFilterInput, before: String, after: String, first: Int, last: Int): WebhookCountableConnection
|
||||||
webhookEvents: [WebhookEvent]
|
webhookEvents: [WebhookEvent]
|
||||||
webhookSamplePayload(eventType: WebhookEventTypeEnum!): JSONString
|
webhookSamplePayload(eventType: WebhookEventTypeEnum!): JSONString
|
||||||
translations(kind: TranslatableKinds!, before: String, after: String, first: Int, last: Int): TranslatableItemConnection
|
translations(kind: TranslatableKinds!, before: String, after: String, first: Int, last: Int): TranslatableItemConnection
|
||||||
translation(id: ID!, kind: TranslatableKinds!): TranslatableItem
|
translation(id: ID!, kind: TranslatableKinds!): DefaultTranslationItem
|
||||||
shop: Shop
|
shop: Shop
|
||||||
shippingZone(id: ID!): ShippingZone
|
shippingZone(id: ID!): ShippingZone
|
||||||
shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection
|
shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection
|
||||||
|
@ -3448,14 +3539,14 @@ type Query {
|
||||||
digitalContents(before: String, after: String, first: Int, last: Int): DigitalContentCountableConnection
|
digitalContents(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
|
attributes(query: String, inCategory: ID, inCollection: ID, filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection
|
||||||
attribute(id: ID!): Attribute
|
attribute(id: ID!): Attribute
|
||||||
categories(query: String, filter: CategoryFilterInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
categories(query: String, filter: CategoryFilterInput, sortBy: CategorySortingInput, level: Int, before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||||
category(id: ID!): Category
|
category(id: ID!): Category
|
||||||
collection(id: ID!): Collection
|
collection(id: ID!): Collection
|
||||||
collections(filter: CollectionFilterInput, query: String, before: String, after: String, first: Int, last: Int): CollectionCountableConnection
|
collections(filter: CollectionFilterInput, sortBy: CollectionSortingInput, query: String, before: String, after: String, first: Int, last: Int): CollectionCountableConnection
|
||||||
product(id: ID!): Product
|
product(id: ID!): Product
|
||||||
products(filter: ProductFilterInput, attributes: [AttributeScalar], categories: [ID], collections: [ID], sortBy: ProductOrder, stockAvailability: StockAvailability, query: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection
|
products(filter: ProductFilterInput, attributes: [AttributeScalar], categories: [ID], collections: [ID], sortBy: ProductOrder, stockAvailability: StockAvailability, query: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection
|
||||||
productType(id: ID!): ProductType
|
productType(id: ID!): ProductType
|
||||||
productTypes(filter: ProductTypeFilterInput, query: String, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
productTypes(filter: ProductTypeFilterInput, query: String, sortBy: ProductTypeSortingInput, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
||||||
productVariant(id: ID!): ProductVariant
|
productVariant(id: ID!): ProductVariant
|
||||||
productVariants(ids: [ID], before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
productVariants(ids: [ID], before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
||||||
reportProductSales(period: ReportingPeriod!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
reportProductSales(period: ReportingPeriod!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
||||||
|
@ -3463,37 +3554,38 @@ type Query {
|
||||||
payments(before: String, after: String, first: Int, last: Int): PaymentCountableConnection
|
payments(before: String, after: String, first: Int, last: Int): PaymentCountableConnection
|
||||||
paymentClientToken(gateway: String!): String @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.10, use payment gateway config instead in availablePaymentGateways.")
|
paymentClientToken(gateway: String!): String @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.10, use payment gateway config instead in availablePaymentGateways.")
|
||||||
page(id: ID, slug: String): Page
|
page(id: ID, slug: String): Page
|
||||||
pages(query: String, filter: PageFilterInput, before: String, after: String, first: Int, last: Int): PageCountableConnection
|
pages(query: String, sortBy: PageSortingInput, filter: PageFilterInput, before: String, after: String, first: Int, last: Int): PageCountableConnection
|
||||||
homepageEvents(before: String, after: String, first: Int, last: Int): OrderEventCountableConnection
|
homepageEvents(before: String, after: String, first: Int, last: Int): OrderEventCountableConnection
|
||||||
order(id: ID!): Order
|
order(id: ID!): Order
|
||||||
orders(filter: OrderFilterInput, query: String, created: ReportingPeriod, status: OrderStatusFilter, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
orders(sortBy: OrderSortingInput, filter: OrderFilterInput, query: String, created: ReportingPeriod, status: OrderStatusFilter, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
||||||
draftOrders(filter: OrderDraftFilterInput, query: String, created: ReportingPeriod, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
draftOrders(sortBy: OrderSortingInput, filter: OrderDraftFilterInput, query: String, created: ReportingPeriod, before: String, after: String, first: Int, last: Int): OrderCountableConnection
|
||||||
ordersTotal(period: ReportingPeriod): TaxedMoney
|
ordersTotal(period: ReportingPeriod): TaxedMoney
|
||||||
orderByToken(token: UUID!): Order
|
orderByToken(token: UUID!): Order
|
||||||
menu(id: ID, name: String): Menu
|
menu(id: ID, name: String): Menu
|
||||||
menus(query: String, filter: MenuFilterInput, before: String, after: String, first: Int, last: Int): MenuCountableConnection
|
menus(query: String, sortBy: MenuSortingInput, filter: MenuFilterInput, before: String, after: String, first: Int, last: Int): MenuCountableConnection
|
||||||
menuItem(id: ID!): MenuItem
|
menuItem(id: ID!): MenuItem
|
||||||
menuItems(query: String, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
menuItems(query: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
||||||
giftCard(id: ID!): GiftCard
|
giftCard(id: ID!): GiftCard
|
||||||
giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
||||||
plugin(id: ID!): Plugin
|
plugin(id: ID!): Plugin
|
||||||
plugins(filter: PluginFilterInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
||||||
sale(id: ID!): Sale
|
sale(id: ID!): Sale
|
||||||
sales(filter: SaleFilterInput, query: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection
|
sales(filter: SaleFilterInput, sortBy: SaleSortingInput, query: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection
|
||||||
voucher(id: ID!): Voucher
|
voucher(id: ID!): Voucher
|
||||||
vouchers(filter: VoucherFilterInput, query: String, before: String, after: String, first: Int, last: Int): VoucherCountableConnection
|
vouchers(filter: VoucherFilterInput, sortBy: VoucherSortingInput, query: String, before: String, after: String, first: Int, last: Int): VoucherCountableConnection
|
||||||
taxTypes: [TaxType]
|
taxTypes: [TaxType]
|
||||||
checkout(token: UUID): Checkout
|
checkout(token: UUID): Checkout
|
||||||
checkouts(before: String, after: String, first: Int, last: Int): CheckoutCountableConnection
|
checkouts(before: String, after: String, first: Int, last: Int): CheckoutCountableConnection
|
||||||
checkoutLine(id: ID): CheckoutLine
|
checkoutLine(id: ID): CheckoutLine
|
||||||
checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection
|
checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection
|
||||||
addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData
|
addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData
|
||||||
customers(filter: CustomerFilterInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
customers(filter: CustomerFilterInput, sortBy: UserSortingInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||||
me: User
|
me: User
|
||||||
staffUsers(filter: StaffUserInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
staffUsers(filter: StaffUserInput, sortBy: UserSortingInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||||
serviceAccounts(filter: ServiceAccountFilterInput, before: String, after: String, first: Int, last: Int): ServiceAccountCountableConnection
|
serviceAccounts(filter: ServiceAccountFilterInput, sortBy: ServiceAccountSortingInput, before: String, after: String, first: Int, last: Int): ServiceAccountCountableConnection
|
||||||
serviceAccount(id: ID!): ServiceAccount
|
serviceAccount(id: ID!): ServiceAccount
|
||||||
user(id: ID!): User
|
user(id: ID!): User
|
||||||
|
node(id: ID!): Node
|
||||||
_entities(representations: [_Any]): [_Entity]
|
_entities(representations: [_Any]): [_Entity]
|
||||||
_service: _Service
|
_service: _Service
|
||||||
}
|
}
|
||||||
|
@ -3590,6 +3682,19 @@ type SaleRemoveCatalogues {
|
||||||
sale: Sale
|
sale: Sale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SaleSortField {
|
||||||
|
NAME
|
||||||
|
START_DATE
|
||||||
|
END_DATE
|
||||||
|
VALUE
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
input SaleSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: SaleSortField
|
||||||
|
}
|
||||||
|
|
||||||
type SaleTranslatableContent implements Node {
|
type SaleTranslatableContent implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
|
@ -3681,6 +3786,16 @@ input ServiceAccountInput {
|
||||||
permissions: [PermissionEnum]
|
permissions: [PermissionEnum]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ServiceAccountSortField {
|
||||||
|
NAME
|
||||||
|
CREATION_DATE
|
||||||
|
}
|
||||||
|
|
||||||
|
input ServiceAccountSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: ServiceAccountSortField
|
||||||
|
}
|
||||||
|
|
||||||
type ServiceAccountToken implements Node {
|
type ServiceAccountToken implements Node {
|
||||||
name: String
|
name: String
|
||||||
authToken: String
|
authToken: String
|
||||||
|
@ -4109,7 +4224,7 @@ enum TransactionKind {
|
||||||
CONFIRM
|
CONFIRM
|
||||||
}
|
}
|
||||||
|
|
||||||
union TranslatableItem = ProductTranslatableContent | CollectionTranslatableContent | CategoryTranslatableContent | AttributeTranslatableContent | AttributeValueTranslatableContent | ProductVariantTranslatableContent | PageTranslatableContent | ShippingMethodTranslatableContent | SaleTranslatableContent | VoucherTranslatableContent | MenuItemTranslatableContent
|
union TranslatableItem = Product | Category | Collection | Attribute | AttributeValue | ProductVariant | Page | ShippingMethod | Sale | Voucher | MenuItem
|
||||||
|
|
||||||
type TranslatableItemConnection {
|
type TranslatableItemConnection {
|
||||||
pageInfo: PageInfo!
|
pageInfo: PageInfo!
|
||||||
|
@ -4230,6 +4345,18 @@ input UserCreateInput {
|
||||||
redirectUrl: String
|
redirectUrl: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum UserSortField {
|
||||||
|
FIRST_NAME
|
||||||
|
LAST_NAME
|
||||||
|
EMAIL
|
||||||
|
ORDER_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
input UserSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: UserSortField
|
||||||
|
}
|
||||||
|
|
||||||
type UserUpdateMeta {
|
type UserUpdateMeta {
|
||||||
errors: [Error!]
|
errors: [Error!]
|
||||||
accountErrors: [AccountError!]
|
accountErrors: [AccountError!]
|
||||||
|
@ -4369,6 +4496,21 @@ type VoucherRemoveCatalogues {
|
||||||
voucher: Voucher
|
voucher: Voucher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum VoucherSortField {
|
||||||
|
CODE
|
||||||
|
START_DATE
|
||||||
|
END_DATE
|
||||||
|
VALUE
|
||||||
|
TYPE
|
||||||
|
USAGE_LIMIT
|
||||||
|
MINIMUM_SPENT_AMOUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
input VoucherSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: VoucherSortField
|
||||||
|
}
|
||||||
|
|
||||||
type VoucherTranslatableContent implements Node {
|
type VoucherTranslatableContent implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String
|
name: String
|
||||||
|
@ -4475,6 +4617,17 @@ input WebhookFilterInput {
|
||||||
isActive: Boolean
|
isActive: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WebhookSortField {
|
||||||
|
NAME
|
||||||
|
SERVICE_ACCOUNT
|
||||||
|
TARGET_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
input WebhookSortingInput {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: WebhookSortField
|
||||||
|
}
|
||||||
|
|
||||||
type WebhookUpdate {
|
type WebhookUpdate {
|
||||||
errors: [Error!]
|
errors: [Error!]
|
||||||
webhook: Webhook
|
webhook: Webhook
|
||||||
|
|
|
@ -13,10 +13,16 @@ import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { translateBoolean } from "@saleor/intl";
|
import { translateBoolean } from "@saleor/intl";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { AttributeListUrlSortField } from "@saleor/attributes/urls";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
|
import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
|
||||||
|
|
||||||
export interface AttributeListProps extends ListProps, ListActions {
|
export interface AttributeListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<AttributeListUrlSortField> {
|
||||||
attributes: AttributeList_attributes_edges_node[];
|
attributes: AttributeList_attributes_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,19 +30,19 @@ const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
[theme.breakpoints.up("lg")]: {
|
[theme.breakpoints.up("lg")]: {
|
||||||
colFaceted: {
|
colFaceted: {
|
||||||
width: 150
|
width: 180
|
||||||
},
|
},
|
||||||
colName: {
|
colName: {
|
||||||
width: "auto"
|
width: "auto"
|
||||||
},
|
},
|
||||||
colSearchable: {
|
colSearchable: {
|
||||||
width: 150
|
width: 180
|
||||||
},
|
},
|
||||||
colSlug: {
|
colSlug: {
|
||||||
width: 200
|
width: 200
|
||||||
},
|
},
|
||||||
colVisible: {
|
colVisible: {
|
||||||
width: 150
|
width: 180
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colFaceted: {
|
colFaceted: {
|
||||||
|
@ -70,9 +76,11 @@ const AttributeList: React.FC<AttributeListProps> = ({
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar,
|
||||||
|
onSort
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -87,33 +95,77 @@ const AttributeList: React.FC<AttributeListProps> = ({
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colSlug}>
|
<TableCellHeader
|
||||||
|
className={classes.colSlug}
|
||||||
|
direction={
|
||||||
|
sort.sort === AttributeListUrlSortField.slug
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(AttributeListUrlSortField.slug)}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Attribute Code" />
|
<FormattedMessage defaultMessage="Attribute Code" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
className={classes.colName}
|
||||||
|
direction={
|
||||||
|
sort.sort === AttributeListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(AttributeListUrlSortField.name)}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Default Label"
|
defaultMessage="Default Label"
|
||||||
description="attribute's label'"
|
description="attribute's label'"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colVisible}>
|
<TableCellHeader
|
||||||
|
className={classes.colVisible}
|
||||||
|
direction={
|
||||||
|
sort.sort === AttributeListUrlSortField.visible
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="center"
|
||||||
|
onClick={() => onSort(AttributeListUrlSortField.visible)}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Visible"
|
defaultMessage="Visible"
|
||||||
description="attribute is visible"
|
description="attribute is visible"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colSearchable}>
|
<TableCellHeader
|
||||||
|
className={classes.colSearchable}
|
||||||
|
direction={
|
||||||
|
sort.sort === AttributeListUrlSortField.searchable
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="center"
|
||||||
|
onClick={() => onSort(AttributeListUrlSortField.searchable)}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Searchable"
|
defaultMessage="Searchable"
|
||||||
description="attribute can be searched in dashboard"
|
description="attribute can be searched in dashboard"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colFaceted}>
|
<TableCellHeader
|
||||||
|
className={classes.colFaceted}
|
||||||
|
direction={
|
||||||
|
sort.sort === AttributeListUrlSortField.useInFacetedSearch
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="center"
|
||||||
|
onClick={() => onSort(AttributeListUrlSortField.useInFacetedSearch)}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Use in faceted search"
|
defaultMessage="Use in faceted search"
|
||||||
description="attribute can be searched in storefront"
|
description="attribute can be searched in storefront"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -6,13 +6,15 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import AppHeader from "@saleor/components/AppHeader";
|
import AppHeader from "@saleor/components/AppHeader";
|
||||||
import SearchBar from "@saleor/components/SearchBar";
|
import SearchBar from "@saleor/components/SearchBar";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { AttributeListUrlSortField } from "@saleor/attributes/urls";
|
||||||
import Container from "../../../components/Container";
|
import Container from "../../../components/Container";
|
||||||
import PageHeader from "../../../components/PageHeader";
|
import PageHeader from "../../../components/PageHeader";
|
||||||
import {
|
import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "../../../types";
|
} from "../../../types";
|
||||||
import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
|
import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
|
||||||
import AttributeList from "../AttributeList/AttributeList";
|
import AttributeList from "../AttributeList/AttributeList";
|
||||||
|
@ -21,6 +23,7 @@ export interface AttributeListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<AttributeListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
attributes: AttributeList_attributes_edges_node[];
|
attributes: AttributeList_attributes_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
attributeAddPath,
|
attributeAddPath,
|
||||||
|
@ -11,7 +12,8 @@ import {
|
||||||
attributeListPath,
|
attributeListPath,
|
||||||
AttributeListUrlQueryParams,
|
AttributeListUrlQueryParams,
|
||||||
attributePath,
|
attributePath,
|
||||||
AttributeUrlQueryParams
|
AttributeUrlQueryParams,
|
||||||
|
AttributeListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import AttributeCreateComponent from "./views/AttributeCreate";
|
import AttributeCreateComponent from "./views/AttributeCreate";
|
||||||
import AttributeDetailsComponent from "./views/AttributeDetails";
|
import AttributeDetailsComponent from "./views/AttributeDetails";
|
||||||
|
@ -19,7 +21,11 @@ import AttributeListComponent from "./views/AttributeList";
|
||||||
|
|
||||||
const AttributeList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const AttributeList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: AttributeListUrlQueryParams = qs;
|
const params: AttributeListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
AttributeListUrlSortField
|
||||||
|
);
|
||||||
|
|
||||||
return <AttributeListComponent params={params} />;
|
return <AttributeListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { pageInfoFragment, TypedQuery } from "../queries";
|
import { pageInfoFragment, TypedQuery } from "../queries";
|
||||||
import {
|
import {
|
||||||
AttributeDetails,
|
AttributeDetails,
|
||||||
|
@ -59,6 +60,7 @@ const attributeList = gql`
|
||||||
$after: String
|
$after: String
|
||||||
$first: Int
|
$first: Int
|
||||||
$last: Int
|
$last: Int
|
||||||
|
$sort: AttributeSortingInput
|
||||||
) {
|
) {
|
||||||
attributes(
|
attributes(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
@ -68,6 +70,7 @@ const attributeList = gql`
|
||||||
after: $after
|
after: $after
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -85,7 +88,7 @@ const attributeList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const AttributeListQuery = TypedQuery<
|
export const useAttributeListQuery = makeQuery<
|
||||||
AttributeList,
|
AttributeList,
|
||||||
AttributeListVariables
|
AttributeListVariables
|
||||||
>(attributeList);
|
>(attributeList);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { AttributeFilterInput } from "./../../types/globalTypes";
|
import { AttributeFilterInput, AttributeSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: AttributeList
|
// GraphQL query operation: AttributeList
|
||||||
|
@ -57,4 +57,5 @@ export interface AttributeListVariables {
|
||||||
after?: string | null;
|
after?: string | null;
|
||||||
first?: number | null;
|
first?: number | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
|
sort?: AttributeSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
Filters,
|
Filters,
|
||||||
Pagination,
|
Pagination,
|
||||||
SingleAction,
|
SingleAction,
|
||||||
|
Sort,
|
||||||
TabActionDialog
|
TabActionDialog
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
|
@ -18,8 +19,17 @@ export enum AttributeListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type AttributeListUrlFilters = Filters<AttributeListUrlFiltersEnum>;
|
export type AttributeListUrlFilters = Filters<AttributeListUrlFiltersEnum>;
|
||||||
export type AttributeListUrlDialog = "remove" | TabActionDialog;
|
export type AttributeListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
export enum AttributeListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
slug = "slug",
|
||||||
|
visible = "visible",
|
||||||
|
searchable = "searchable",
|
||||||
|
useInFacetedSearch = "use-in-faceted-search"
|
||||||
|
}
|
||||||
|
export type AttributeListUrlSort = Sort<AttributeListUrlSortField>;
|
||||||
export type AttributeListUrlQueryParams = ActiveTab &
|
export type AttributeListUrlQueryParams = ActiveTab &
|
||||||
AttributeListUrlFilters &
|
AttributeListUrlFilters &
|
||||||
|
AttributeListUrlSort &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
Dialog<AttributeListUrlDialog> &
|
Dialog<AttributeListUrlDialog> &
|
||||||
Pagination;
|
Pagination;
|
||||||
|
|
|
@ -21,13 +21,15 @@ import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import { PAGINATE_BY } from "../../../config";
|
import { PAGINATE_BY } from "../../../config";
|
||||||
import useBulkActions from "../../../hooks/useBulkActions";
|
import useBulkActions from "../../../hooks/useBulkActions";
|
||||||
import { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import AttributeBulkDeleteDialog from "../../components/AttributeBulkDeleteDialog";
|
import AttributeBulkDeleteDialog from "../../components/AttributeBulkDeleteDialog";
|
||||||
import AttributeListPage from "../../components/AttributeListPage";
|
import AttributeListPage from "../../components/AttributeListPage";
|
||||||
import { AttributeBulkDeleteMutation } from "../../mutations";
|
import { AttributeBulkDeleteMutation } from "../../mutations";
|
||||||
import { AttributeListQuery } from "../../queries";
|
import { useAttributeListQuery } from "../../queries";
|
||||||
import { AttributeBulkDelete } from "../../types/AttributeBulkDelete";
|
import { AttributeBulkDelete } from "../../types/AttributeBulkDelete";
|
||||||
import {
|
import {
|
||||||
attributeAddUrl,
|
attributeAddUrl,
|
||||||
|
@ -37,6 +39,7 @@ import {
|
||||||
AttributeListUrlQueryParams,
|
AttributeListUrlQueryParams,
|
||||||
attributeUrl
|
attributeUrl
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface AttributeListProps {
|
interface AttributeListProps {
|
||||||
params: AttributeListUrlQueryParams;
|
params: AttributeListUrlQueryParams;
|
||||||
|
@ -51,6 +54,19 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useAttributeListQuery({
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -111,105 +127,93 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
|
||||||
handleTabChange(tabs.length + 1);
|
handleTabChange(tabs.length + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
const queryVariables = React.useMemo(
|
maybe(() => data.attributes.pageInfo),
|
||||||
() => ({
|
paginationState,
|
||||||
...paginationState,
|
params
|
||||||
filter: getFilterVariables(params)
|
|
||||||
}),
|
|
||||||
[params]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleBulkDelete = (data: AttributeBulkDelete) => {
|
||||||
|
if (data.attributeBulkDelete.errors.length === 0) {
|
||||||
|
closeModal();
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Attributes successfully delete",
|
||||||
|
description: "deleted multiple attributes"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, attributeListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AttributeListQuery variables={queryVariables}>
|
<AttributeBulkDeleteMutation onCompleted={handleBulkDelete}>
|
||||||
{({ data, loading, refetch }) => {
|
{(attributeBulkDelete, attributeBulkDeleteOpts) => (
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
<>
|
||||||
maybe(() => data.attributes.pageInfo),
|
<AttributeListPage
|
||||||
paginationState,
|
attributes={maybe(() =>
|
||||||
params
|
data.attributes.edges.map(edge => edge.node)
|
||||||
);
|
|
||||||
|
|
||||||
const handleBulkDelete = (data: AttributeBulkDelete) => {
|
|
||||||
if (data.attributeBulkDelete.errors.length === 0) {
|
|
||||||
closeModal();
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Attributes successfully delete",
|
|
||||||
description: "deleted multiple attributes"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
reset();
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AttributeBulkDeleteMutation onCompleted={handleBulkDelete}>
|
|
||||||
{(attributeBulkDelete, attributeBulkDeleteOpts) => (
|
|
||||||
<>
|
|
||||||
<AttributeListPage
|
|
||||||
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())}
|
|
||||||
onBack={() => navigate(configurationMenuUrl)}
|
|
||||||
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={
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("remove", listElements)}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AttributeBulkDeleteDialog
|
|
||||||
confirmButtonState={attributeBulkDeleteOpts.status}
|
|
||||||
open={
|
|
||||||
params.action === "remove" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onConfirm={() =>
|
|
||||||
attributeBulkDelete({ variables: { ids: params.ids } })
|
|
||||||
}
|
|
||||||
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, "...")}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</AttributeBulkDeleteMutation>
|
currentTab={currentTab}
|
||||||
);
|
disabled={loading || attributeBulkDeleteOpts.loading}
|
||||||
}}
|
initialSearch={params.query || ""}
|
||||||
</AttributeListQuery>
|
isChecked={isSelected}
|
||||||
|
onAdd={() => navigate(attributeAddUrl())}
|
||||||
|
onAll={() => navigate(attributeListUrl())}
|
||||||
|
onBack={() => navigate(configurationMenuUrl)}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onRowClick={id => () => navigate(attributeUrl(id))}
|
||||||
|
onSearchChange={query => changeFilterField({ query })}
|
||||||
|
onSort={handleSort}
|
||||||
|
onTabChange={handleTabChange}
|
||||||
|
onTabDelete={() => openModal("delete-search")}
|
||||||
|
onTabSave={() => openModal("save-search")}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
tabs={tabs.map(tab => tab.name)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
toolbar={
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("remove", listElements)}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AttributeBulkDeleteDialog
|
||||||
|
confirmButtonState={attributeBulkDeleteOpts.status}
|
||||||
|
open={
|
||||||
|
params.action === "remove" && maybe(() => params.ids.length > 0)
|
||||||
|
}
|
||||||
|
onConfirm={() =>
|
||||||
|
attributeBulkDelete({ variables: { ids: params.ids } })
|
||||||
|
}
|
||||||
|
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, "...")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</AttributeBulkDeleteMutation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
AttributeList.displayName = "AttributeList";
|
AttributeList.displayName = "AttributeList";
|
||||||
|
|
26
src/attributes/views/AttributeList/sort.ts
Normal file
26
src/attributes/views/AttributeList/sort.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { AttributeListUrlSortField } from "@saleor/attributes/urls";
|
||||||
|
import { AttributeSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: AttributeListUrlSortField
|
||||||
|
): AttributeSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case AttributeListUrlSortField.name:
|
||||||
|
return AttributeSortField.NAME;
|
||||||
|
case AttributeListUrlSortField.slug:
|
||||||
|
return AttributeSortField.SLUG;
|
||||||
|
case AttributeListUrlSortField.searchable:
|
||||||
|
return AttributeSortField.FILTERABLE_IN_DASHBOARD;
|
||||||
|
case AttributeListUrlSortField.useInFacetedSearch:
|
||||||
|
return AttributeSortField.FILTERABLE_IN_STOREFRONT;
|
||||||
|
case AttributeListUrlSortField.visible:
|
||||||
|
return AttributeSortField.VISIBLE_IN_STOREFRONT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -13,7 +13,10 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { CategoryListUrlSortField } from "@saleor/categories/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -44,7 +47,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "CategoryList" }
|
{ name: "CategoryList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface CategoryListProps extends ListProps, ListActions {
|
interface CategoryListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<CategoryListUrlSortField> {
|
||||||
categories?: CategoryFragment[];
|
categories?: CategoryFragment[];
|
||||||
isRoot: boolean;
|
isRoot: boolean;
|
||||||
onAdd?();
|
onAdd?();
|
||||||
|
@ -57,6 +63,7 @@ const CategoryList: React.FC<CategoryListProps> = props => {
|
||||||
categories,
|
categories,
|
||||||
disabled,
|
disabled,
|
||||||
settings,
|
settings,
|
||||||
|
sort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
isRoot,
|
isRoot,
|
||||||
|
@ -67,7 +74,8 @@ const CategoryList: React.FC<CategoryListProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick
|
onRowClick,
|
||||||
|
onSort
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -82,21 +90,53 @@ const CategoryList: React.FC<CategoryListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
isRoot && sort.sort === CategoryListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
className={classes.colName}
|
||||||
|
disableClick={!isRoot}
|
||||||
|
onClick={() => isRoot && onSort(CategoryListUrlSortField.name)}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Category Name" />
|
<FormattedMessage defaultMessage="Category Name" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colSubcategories}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
isRoot && sort.sort === CategoryListUrlSortField.subcategoryCount
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
className={classes.colSubcategories}
|
||||||
|
disableClick={!isRoot}
|
||||||
|
onClick={() =>
|
||||||
|
isRoot && onSort(CategoryListUrlSortField.subcategoryCount)
|
||||||
|
}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Subcategories"
|
defaultMessage="Subcategories"
|
||||||
description="number of subcategories"
|
description="number of subcategories"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colProducts}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
isRoot && sort.sort === CategoryListUrlSortField.productCount
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
className={classes.colProducts}
|
||||||
|
disableClick={!isRoot}
|
||||||
|
onClick={() =>
|
||||||
|
isRoot && onSort(CategoryListUrlSortField.productCount)
|
||||||
|
}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="No. of Products"
|
defaultMessage="No. of Products"
|
||||||
description="number of products"
|
description="number of products"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -12,14 +12,17 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { CategoryListUrlSortField } from "@saleor/categories/urls";
|
||||||
import CategoryList from "../CategoryList";
|
import CategoryList from "../CategoryList";
|
||||||
|
|
||||||
export interface CategoryTableProps
|
export interface CategoryTableProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<CategoryListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
categories: CategoryFragment[];
|
categories: CategoryFragment[];
|
||||||
}
|
}
|
||||||
|
@ -46,7 +49,8 @@ export const CategoryListPage: React.FC<CategoryTableProps> = ({
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
onUpdateListSettings
|
onUpdateListSettings,
|
||||||
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -94,6 +98,7 @@ export const CategoryListPage: React.FC<CategoryTableProps> = ({
|
||||||
onPreviousPage={onPreviousPage}
|
onPreviousPage={onPreviousPage}
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
onUpdateListSettings={onUpdateListSettings}
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
|
{...listProps}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -205,12 +205,14 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
isRoot={false}
|
isRoot={false}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
sort={undefined}
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={subcategoryListToolbar}
|
toolbar={subcategoryListToolbar}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
onPreviousPage={onPreviousPage}
|
onPreviousPage={onPreviousPage}
|
||||||
onRowClick={onCategoryClick}
|
onRowClick={onCategoryClick}
|
||||||
|
onSort={() => undefined}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -4,13 +4,15 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
categoryAddPath,
|
categoryAddPath,
|
||||||
categoryListPath,
|
categoryListPath,
|
||||||
CategoryListUrlQueryParams,
|
CategoryListUrlQueryParams,
|
||||||
categoryPath,
|
categoryPath,
|
||||||
CategoryUrlQueryParams
|
CategoryUrlQueryParams,
|
||||||
|
CategoryListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import { CategoryCreateView } from "./views/CategoryCreate";
|
import { CategoryCreateView } from "./views/CategoryCreate";
|
||||||
import CategoryDetailsView, { getActiveTab } from "./views/CategoryDetails";
|
import CategoryDetailsView, { getActiveTab } from "./views/CategoryDetails";
|
||||||
|
@ -19,14 +21,15 @@ import CategoryListComponent from "./views/CategoryList";
|
||||||
interface CategoryDetailsRouteParams {
|
interface CategoryDetailsRouteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const CategoryDetails: React.FC<
|
const CategoryDetails: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<CategoryDetailsRouteParams>
|
CategoryDetailsRouteParams
|
||||||
> = ({ location, match }) => {
|
>> = ({ location, match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CategoryUrlQueryParams = {
|
const params: CategoryUrlQueryParams = {
|
||||||
...qs,
|
...qs,
|
||||||
activeTab: getActiveTab(qs.activeTab)
|
activeTab: getActiveTab(qs.activeTab)
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CategoryDetailsView
|
<CategoryDetailsView
|
||||||
id={decodeURIComponent(match.params.id)}
|
id={decodeURIComponent(match.params.id)}
|
||||||
|
@ -38,9 +41,9 @@ const CategoryDetails: React.FC<
|
||||||
interface CategoryCreateRouteParams {
|
interface CategoryCreateRouteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const CategoryCreate: React.FC<
|
const CategoryCreate: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<CategoryCreateRouteParams>
|
CategoryCreateRouteParams
|
||||||
> = ({ match }) => (
|
>> = ({ match }) => (
|
||||||
<CategoryCreateView
|
<CategoryCreateView
|
||||||
parentId={match.params.id ? decodeURIComponent(match.params.id) : undefined}
|
parentId={match.params.id ? decodeURIComponent(match.params.id) : undefined}
|
||||||
/>
|
/>
|
||||||
|
@ -48,7 +51,10 @@ const CategoryCreate: React.FC<
|
||||||
|
|
||||||
const CategoryList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const CategoryList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CategoryListUrlQueryParams = qs;
|
const params: CategoryListUrlQueryParams = {
|
||||||
|
...asSortParams(qs, CategoryListUrlSortField)
|
||||||
|
};
|
||||||
|
|
||||||
return <CategoryListComponent params={params} />;
|
return <CategoryListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ export const rootCategories = gql`
|
||||||
$last: Int
|
$last: Int
|
||||||
$before: String
|
$before: String
|
||||||
$filter: CategoryFilterInput
|
$filter: CategoryFilterInput
|
||||||
|
$sort: CategorySortingInput
|
||||||
) {
|
) {
|
||||||
categories(
|
categories(
|
||||||
level: 0
|
level: 0
|
||||||
|
@ -54,6 +55,7 @@ export const rootCategories = gql`
|
||||||
last: $last
|
last: $last
|
||||||
before: $before
|
before: $before
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { CategoryFilterInput } from "./../../types/globalTypes";
|
import { CategoryFilterInput, CategorySortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: RootCategories
|
// GraphQL query operation: RootCategories
|
||||||
|
@ -55,4 +55,5 @@ export interface RootCategoriesVariables {
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
filter?: CategoryFilterInput | null;
|
filter?: CategoryFilterInput | null;
|
||||||
|
sort?: CategorySortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
Filters,
|
Filters,
|
||||||
Pagination,
|
Pagination,
|
||||||
TabActionDialog
|
TabActionDialog,
|
||||||
|
Sort
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { CategoryPageTab } from "./components/CategoryUpdatePage";
|
import { CategoryPageTab } from "./components/CategoryUpdatePage";
|
||||||
|
|
||||||
|
@ -19,9 +20,16 @@ export enum CategoryListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type CategoryListUrlFilters = Filters<CategoryListUrlFiltersEnum>;
|
export type CategoryListUrlFilters = Filters<CategoryListUrlFiltersEnum>;
|
||||||
export type CategoryListUrlDialog = "delete" | TabActionDialog;
|
export type CategoryListUrlDialog = "delete" | TabActionDialog;
|
||||||
|
export enum CategoryListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
productCount = "products",
|
||||||
|
subcategoryCount = "subcategories"
|
||||||
|
}
|
||||||
|
export type CategoryListUrlSort = Sort<CategoryListUrlSortField>;
|
||||||
export type CategoryListUrlQueryParams = ActiveTab &
|
export type CategoryListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
CategoryListUrlFilters &
|
CategoryListUrlFilters &
|
||||||
|
CategoryListUrlSort &
|
||||||
Dialog<CategoryListUrlDialog> &
|
Dialog<CategoryListUrlDialog> &
|
||||||
Pagination;
|
Pagination;
|
||||||
export const categoryListUrl = (params?: CategoryListUrlQueryParams) =>
|
export const categoryListUrl = (params?: CategoryListUrlQueryParams) =>
|
||||||
|
|
|
@ -17,6 +17,8 @@ import usePaginator, {
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
|
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
|
||||||
import { useCategoryBulkDeleteMutation } from "../../mutations";
|
import { useCategoryBulkDeleteMutation } from "../../mutations";
|
||||||
import { useRootCategoriesQuery } from "../../queries";
|
import { useRootCategoriesQuery } from "../../queries";
|
||||||
|
@ -37,6 +39,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface CategoryListProps {
|
interface CategoryListProps {
|
||||||
params: CategoryListUrlQueryParams;
|
params: CategoryListUrlQueryParams;
|
||||||
|
@ -57,7 +60,8 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
filter: getFilterVariables(params)
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
}),
|
}),
|
||||||
[params]
|
[params]
|
||||||
);
|
);
|
||||||
|
@ -148,6 +152,8 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
||||||
onCompleted: handleCategoryBulkDelete
|
onCompleted: handleCategoryBulkDelete
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, categoryListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CategoryListPage
|
<CategoryListPage
|
||||||
|
@ -164,8 +170,10 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
||||||
onTabSave={() => openModal("save-search")}
|
onTabSave={() => openModal("save-search")}
|
||||||
tabs={tabs.map(tab => tab.name)}
|
tabs={tabs.map(tab => tab.name)}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
|
sort={getSortParams(params)}
|
||||||
onAdd={() => navigate(categoryAddUrl())}
|
onAdd={() => navigate(categoryAddUrl())}
|
||||||
onRowClick={id => () => navigate(categoryUrl(id))}
|
onRowClick={id => () => navigate(categoryUrl(id))}
|
||||||
|
onSort={handleSort}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onNextPage={loadNextPage}
|
onNextPage={loadNextPage}
|
||||||
onPreviousPage={loadPreviousPage}
|
onPreviousPage={loadPreviousPage}
|
||||||
|
|
22
src/categories/views/CategoryList/sort.ts
Normal file
22
src/categories/views/CategoryList/sort.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { CategoryListUrlSortField } from "@saleor/categories/urls";
|
||||||
|
import { CategorySortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: CategoryListUrlSortField
|
||||||
|
): CategorySortField {
|
||||||
|
switch (sort) {
|
||||||
|
case CategoryListUrlSortField.name:
|
||||||
|
return CategorySortField.NAME;
|
||||||
|
case CategoryListUrlSortField.productCount:
|
||||||
|
return CategorySortField.PRODUCT_COUNT;
|
||||||
|
case CategoryListUrlSortField.subcategoryCount:
|
||||||
|
return CategorySortField.SUBCATEGORY_COUNT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -13,7 +13,10 @@ import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -41,7 +44,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "CollectionList" }
|
{ name: "CollectionList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface CollectionListProps extends ListProps, ListActions {
|
interface CollectionListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<CollectionListUrlSortField> {
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +58,12 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
collections,
|
collections,
|
||||||
disabled,
|
disabled,
|
||||||
settings,
|
settings,
|
||||||
|
sort,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
@ -77,18 +85,43 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
<FormattedMessage defaultMessage="Category Name" />
|
direction={
|
||||||
</TableCell>
|
sort.sort === CollectionListUrlSortField.name
|
||||||
<TableCell className={classes.colProducts}>
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(CollectionListUrlSortField.name)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
|
<FormattedMessage defaultMessage="Collection Name" />
|
||||||
|
</TableCellHeader>
|
||||||
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === CollectionListUrlSortField.productCount
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(CollectionListUrlSortField.productCount)}
|
||||||
|
className={classes.colProducts}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="No. of Products" />
|
<FormattedMessage defaultMessage="No. of Products" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colAvailability}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === CollectionListUrlSortField.available
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(CollectionListUrlSortField.available)}
|
||||||
|
className={classes.colAvailability}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Availability"
|
defaultMessage="Availability"
|
||||||
description="collection availability"
|
description="collection availability"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -11,8 +11,10 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
import CollectionList from "../CollectionList/CollectionList";
|
import CollectionList from "../CollectionList/CollectionList";
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ export interface CollectionListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<CollectionListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,15 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
collectionAddPath,
|
collectionAddPath,
|
||||||
collectionListPath,
|
collectionListPath,
|
||||||
CollectionListUrlQueryParams,
|
CollectionListUrlQueryParams,
|
||||||
collectionPath,
|
collectionPath,
|
||||||
CollectionUrlQueryParams
|
CollectionUrlQueryParams,
|
||||||
|
CollectionListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import CollectionCreate from "./views/CollectionCreate";
|
import CollectionCreate from "./views/CollectionCreate";
|
||||||
import CollectionDetailsView from "./views/CollectionDetails";
|
import CollectionDetailsView from "./views/CollectionDetails";
|
||||||
|
@ -18,16 +20,19 @@ import CollectionListView from "./views/CollectionList";
|
||||||
|
|
||||||
const CollectionList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const CollectionList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CollectionListUrlQueryParams = qs;
|
const params: CollectionListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
CollectionListUrlSortField
|
||||||
|
);
|
||||||
return <CollectionListView params={params} />;
|
return <CollectionListView params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface CollectionDetailsRouteProps {
|
interface CollectionDetailsRouteProps {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const CollectionDetails: React.FC<
|
const CollectionDetails: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<CollectionDetailsRouteProps>
|
CollectionDetailsRouteProps
|
||||||
> = ({ location, match }) => {
|
>> = ({ location, match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CollectionUrlQueryParams = qs;
|
const params: CollectionUrlQueryParams = qs;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { TypedQuery } from "../queries";
|
import { TypedQuery } from "../queries";
|
||||||
import {
|
import {
|
||||||
CollectionDetails,
|
CollectionDetails,
|
||||||
|
@ -61,6 +62,7 @@ export const collectionList = gql`
|
||||||
$last: Int
|
$last: Int
|
||||||
$before: String
|
$before: String
|
||||||
$filter: CollectionFilterInput
|
$filter: CollectionFilterInput
|
||||||
|
$sort: CollectionSortingInput
|
||||||
) {
|
) {
|
||||||
collections(
|
collections(
|
||||||
first: $first
|
first: $first
|
||||||
|
@ -68,6 +70,7 @@ export const collectionList = gql`
|
||||||
before: $before
|
before: $before
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -86,7 +89,7 @@ export const collectionList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedCollectionListQuery = TypedQuery<
|
export const useCollectionListQuery = makeQuery<
|
||||||
CollectionList,
|
CollectionList,
|
||||||
CollectionListVariables
|
CollectionListVariables
|
||||||
>(collectionList);
|
>(collectionList);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { CollectionFilterInput } from "./../../types/globalTypes";
|
import { CollectionFilterInput, CollectionSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: CollectionList
|
// GraphQL query operation: CollectionList
|
||||||
|
@ -50,4 +50,5 @@ export interface CollectionListVariables {
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
filter?: CollectionFilterInput | null;
|
filter?: CollectionFilterInput | null;
|
||||||
|
sort?: CollectionSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
Filters,
|
Filters,
|
||||||
Pagination,
|
Pagination,
|
||||||
TabActionDialog
|
TabActionDialog,
|
||||||
|
Sort
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
const collectionSectionUrl = "/collections/";
|
const collectionSectionUrl = "/collections/";
|
||||||
|
@ -22,9 +23,16 @@ export type CollectionListUrlDialog =
|
||||||
| "unpublish"
|
| "unpublish"
|
||||||
| "remove"
|
| "remove"
|
||||||
| TabActionDialog;
|
| TabActionDialog;
|
||||||
|
export enum CollectionListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
available = "available",
|
||||||
|
productCount = "products"
|
||||||
|
}
|
||||||
|
export type CollectionListUrlSort = Sort<CollectionListUrlSortField>;
|
||||||
export type CollectionListUrlQueryParams = ActiveTab &
|
export type CollectionListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
CollectionListUrlFilters &
|
CollectionListUrlFilters &
|
||||||
|
CollectionListUrlSort &
|
||||||
Dialog<CollectionListUrlDialog> &
|
Dialog<CollectionListUrlDialog> &
|
||||||
Pagination;
|
Pagination;
|
||||||
export const collectionListUrl = (params?: CollectionListUrlQueryParams) =>
|
export const collectionListUrl = (params?: CollectionListUrlQueryParams) =>
|
||||||
|
|
|
@ -20,12 +20,14 @@ import usePaginator, {
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
|
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
|
||||||
import {
|
import {
|
||||||
TypedCollectionBulkDelete,
|
TypedCollectionBulkDelete,
|
||||||
TypedCollectionBulkPublish
|
TypedCollectionBulkPublish
|
||||||
} from "../../mutations";
|
} from "../../mutations";
|
||||||
import { TypedCollectionListQuery } from "../../queries";
|
import { useCollectionListQuery } from "../../queries";
|
||||||
import { CollectionBulkDelete } from "../../types/CollectionBulkDelete";
|
import { CollectionBulkDelete } from "../../types/CollectionBulkDelete";
|
||||||
import { CollectionBulkPublish } from "../../types/CollectionBulkPublish";
|
import { CollectionBulkPublish } from "../../types/CollectionBulkPublish";
|
||||||
import {
|
import {
|
||||||
|
@ -44,6 +46,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface CollectionListProps {
|
interface CollectionListProps {
|
||||||
params: CollectionListUrlQueryParams;
|
params: CollectionListUrlQueryParams;
|
||||||
|
@ -61,6 +64,20 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useCollectionListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -121,226 +138,213 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
handleTabChange(tabs.length + 1);
|
handleTabChange(tabs.length + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
const queryVariables = React.useMemo(
|
maybe(() => data.collections.pageInfo),
|
||||||
() => ({
|
paginationState,
|
||||||
...paginationState,
|
params
|
||||||
filter: getFilterVariables(params)
|
|
||||||
}),
|
|
||||||
[params]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCollectionBulkDelete = (data: CollectionBulkDelete) => {
|
||||||
|
if (data.collectionBulkDelete.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
reset();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCollectionBulkPublish = (data: CollectionBulkPublish) => {
|
||||||
|
if (data.collectionBulkPublish.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
reset();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, collectionListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedCollectionListQuery displayLoader variables={queryVariables}>
|
<TypedCollectionBulkDelete onCompleted={handleCollectionBulkDelete}>
|
||||||
{({ data, loading, refetch }) => {
|
{(collectionBulkDelete, collectionBulkDeleteOpts) => (
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
<TypedCollectionBulkPublish onCompleted={handleCollectionBulkPublish}>
|
||||||
maybe(() => data.collections.pageInfo),
|
{(collectionBulkPublish, collectionBulkPublishOpts) => (
|
||||||
paginationState,
|
<>
|
||||||
params
|
<CollectionListPage
|
||||||
);
|
currentTab={currentTab}
|
||||||
|
initialSearch={params.query || ""}
|
||||||
const handleCollectionBulkDelete = (data: CollectionBulkDelete) => {
|
onSearchChange={query => changeFilterField({ query })}
|
||||||
if (data.collectionBulkDelete.errors.length === 0) {
|
onAdd={() => navigate(collectionAddUrl)}
|
||||||
notify({
|
onAll={() => navigate(collectionListUrl())}
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
onTabChange={handleTabChange}
|
||||||
});
|
onTabDelete={() => openModal("delete-search")}
|
||||||
refetch();
|
onTabSave={() => openModal("save-search")}
|
||||||
reset();
|
tabs={tabs.map(tab => tab.name)}
|
||||||
closeModal();
|
disabled={loading}
|
||||||
}
|
collections={maybe(() =>
|
||||||
};
|
data.collections.edges.map(edge => edge.node)
|
||||||
|
|
||||||
const handleCollectionBulkPublish = (data: CollectionBulkPublish) => {
|
|
||||||
if (data.collectionBulkPublish.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
reset();
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TypedCollectionBulkDelete onCompleted={handleCollectionBulkDelete}>
|
|
||||||
{(collectionBulkDelete, collectionBulkDeleteOpts) => (
|
|
||||||
<TypedCollectionBulkPublish
|
|
||||||
onCompleted={handleCollectionBulkPublish}
|
|
||||||
>
|
|
||||||
{(collectionBulkPublish, collectionBulkPublishOpts) => (
|
|
||||||
<>
|
|
||||||
<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)
|
|
||||||
)}
|
|
||||||
settings={settings}
|
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
onRowClick={id => () => navigate(collectionUrl(id))}
|
|
||||||
toolbar={
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("unpublish", listElements)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Unpublish"
|
|
||||||
description="unpublish collections"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("publish", listElements)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Publish"
|
|
||||||
description="publish collections"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("remove", listElements)}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
open={
|
|
||||||
params.action === "publish" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={collectionBulkPublishOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
collectionBulkPublish({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids,
|
|
||||||
isPublished: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="default"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Publish collections",
|
|
||||||
description: "dialog title"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to publish {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
|
||||||
</ActionDialog>
|
|
||||||
<ActionDialog
|
|
||||||
open={
|
|
||||||
params.action === "unpublish" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={collectionBulkPublishOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
collectionBulkPublish({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids,
|
|
||||||
isPublished: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="default"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Unpublish collections",
|
|
||||||
description: "dialog title"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to unpublish {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
|
||||||
</ActionDialog>
|
|
||||||
<ActionDialog
|
|
||||||
open={
|
|
||||||
params.action === "remove" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={collectionBulkDeleteOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
collectionBulkDelete({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="delete"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete collections",
|
|
||||||
description: "dialog title"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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, "...")}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</TypedCollectionBulkPublish>
|
settings={settings}
|
||||||
)}
|
onNextPage={loadNextPage}
|
||||||
</TypedCollectionBulkDelete>
|
onPreviousPage={loadPreviousPage}
|
||||||
);
|
onSort={handleSort}
|
||||||
}}
|
onUpdateListSettings={updateListSettings}
|
||||||
</TypedCollectionListQuery>
|
pageInfo={pageInfo}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
onRowClick={id => () => navigate(collectionUrl(id))}
|
||||||
|
toolbar={
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("unpublish", listElements)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Unpublish"
|
||||||
|
description="unpublish collections"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("publish", listElements)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Publish"
|
||||||
|
description="publish collections"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("remove", listElements)}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
/>
|
||||||
|
<ActionDialog
|
||||||
|
open={
|
||||||
|
params.action === "publish" &&
|
||||||
|
maybe(() => params.ids.length > 0)
|
||||||
|
}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={collectionBulkPublishOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
collectionBulkPublish({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids,
|
||||||
|
isPublished: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="default"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Publish collections",
|
||||||
|
description: "dialog title"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to publish {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
</ActionDialog>
|
||||||
|
<ActionDialog
|
||||||
|
open={
|
||||||
|
params.action === "unpublish" &&
|
||||||
|
maybe(() => params.ids.length > 0)
|
||||||
|
}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={collectionBulkPublishOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
collectionBulkPublish({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids,
|
||||||
|
isPublished: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="default"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Unpublish collections",
|
||||||
|
description: "dialog title"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to unpublish {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
</ActionDialog>
|
||||||
|
<ActionDialog
|
||||||
|
open={
|
||||||
|
params.action === "remove" &&
|
||||||
|
maybe(() => params.ids.length > 0)
|
||||||
|
}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={collectionBulkDeleteOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
collectionBulkDelete({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="delete"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete collections",
|
||||||
|
description: "dialog title"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this collection} other{{displayQuantity} collections}}?"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</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, "...")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TypedCollectionBulkPublish>
|
||||||
|
)}
|
||||||
|
</TypedCollectionBulkDelete>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default CollectionList;
|
export default CollectionList;
|
||||||
|
|
22
src/collections/views/CollectionList/sort.ts
Normal file
22
src/collections/views/CollectionList/sort.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
|
import { CollectionSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: CollectionListUrlSortField
|
||||||
|
): CollectionSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case CollectionListUrlSortField.name:
|
||||||
|
return CollectionSortField.NAME;
|
||||||
|
case CollectionListUrlSortField.available:
|
||||||
|
return CollectionSortField.AVAILABILITY;
|
||||||
|
case CollectionListUrlSortField.productCount:
|
||||||
|
return CollectionSortField.PRODUCT_COUNT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -11,7 +11,7 @@ import { sectionNames } from "@saleor/intl";
|
||||||
import { menuListUrl } from "@saleor/navigation/urls";
|
import { menuListUrl } from "@saleor/navigation/urls";
|
||||||
import { orderDraftListUrl, orderListUrl } from "@saleor/orders/urls";
|
import { orderDraftListUrl, orderListUrl } from "@saleor/orders/urls";
|
||||||
import { pageListUrl } from "@saleor/pages/urls";
|
import { pageListUrl } from "@saleor/pages/urls";
|
||||||
import { pluginsListUrl } from "@saleor/plugins/urls";
|
import { pluginListUrl } from "@saleor/plugins/urls";
|
||||||
import { productListUrl } from "@saleor/products/urls";
|
import { productListUrl } from "@saleor/products/urls";
|
||||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||||
import { serviceListUrl } from "@saleor/services/urls";
|
import { serviceListUrl } from "@saleor/services/urls";
|
||||||
|
@ -71,7 +71,7 @@ function searchInViews(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(sectionNames.plugins),
|
label: intl.formatMessage(sectionNames.plugins),
|
||||||
url: pluginsListUrl()
|
url: pluginListUrl()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(sectionNames.productTypes),
|
label: intl.formatMessage(sectionNames.productTypes),
|
||||||
|
|
|
@ -16,6 +16,11 @@ const useStyles = makeStyles(
|
||||||
arrowUp: {
|
arrowUp: {
|
||||||
transform: "rotate(180deg)"
|
transform: "rotate(180deg)"
|
||||||
},
|
},
|
||||||
|
disableClick: {
|
||||||
|
"&&": {
|
||||||
|
cursor: "unset"
|
||||||
|
}
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
display: "inline-block"
|
display: "inline-block"
|
||||||
|
@ -48,6 +53,7 @@ export type TableCellHeaderArrowPosition = "left" | "right";
|
||||||
export interface TableCellHeaderProps extends TableCellProps {
|
export interface TableCellHeaderProps extends TableCellProps {
|
||||||
arrowPosition?: TableCellHeaderArrowPosition;
|
arrowPosition?: TableCellHeaderArrowPosition;
|
||||||
direction?: TableCellHeaderArrowDirection;
|
direction?: TableCellHeaderArrowDirection;
|
||||||
|
disableClick?: boolean;
|
||||||
textAlign?: "left" | "center" | "right";
|
textAlign?: "left" | "center" | "right";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,12 +64,18 @@ const TableCellHeader: React.FC<TableCellHeaderProps> = props => {
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
direction,
|
direction,
|
||||||
|
disableClick,
|
||||||
textAlign,
|
textAlign,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCell {...rest} className={classNames(classes.root, className)}>
|
<TableCell
|
||||||
|
{...rest}
|
||||||
|
className={classNames(classes.root, className, {
|
||||||
|
[classes.disableClick]: disableClick
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(classes.labelContainer, {
|
className={classNames(classes.labelContainer, {
|
||||||
[classes.labelContainerActive]: !!direction && !!arrowPosition,
|
[classes.labelContainerActive]: !!direction && !!arrowPosition,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { sectionNames } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { menuListUrl } from "@saleor/navigation/urls";
|
import { menuListUrl } from "@saleor/navigation/urls";
|
||||||
import { pageListUrl } from "@saleor/pages/urls";
|
import { pageListUrl } from "@saleor/pages/urls";
|
||||||
import { pluginsListUrl } from "@saleor/plugins/urls";
|
import { pluginListUrl } from "@saleor/plugins/urls";
|
||||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||||
import { serviceListUrl } from "@saleor/services/urls";
|
import { serviceListUrl } from "@saleor/services/urls";
|
||||||
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
||||||
|
@ -153,7 +153,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||||
),
|
),
|
||||||
permission: PermissionEnum.MANAGE_PLUGINS,
|
permission: PermissionEnum.MANAGE_PLUGINS,
|
||||||
title: intl.formatMessage(sectionNames.plugins),
|
title: intl.formatMessage(sectionNames.plugins),
|
||||||
url: pluginsListUrl()
|
url: pluginListUrl()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: intl.formatMessage({
|
description: intl.formatMessage({
|
||||||
|
|
|
@ -12,7 +12,10 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { getUserName, maybe, renderCollection } from "@saleor/misc";
|
import { getUserName, maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { CustomerListUrlSortField } from "@saleor/customers/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -38,7 +41,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "CustomerList" }
|
{ name: "CustomerList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface CustomerListProps extends ListProps, ListActions {
|
export interface CustomerListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<CustomerListUrlSortField> {
|
||||||
customers: ListCustomers_customers_edges_node[];
|
customers: ListCustomers_customers_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +60,12 @@ const CustomerList: React.FC<CustomerListProps> = props => {
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
toolbar,
|
toolbar,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
isChecked
|
isChecked
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -73,15 +81,41 @@ const CustomerList: React.FC<CustomerListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === CustomerListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(CustomerListUrlSortField.name)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Customer Name" />
|
<FormattedMessage defaultMessage="Customer Name" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colEmail}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === CustomerListUrlSortField.email
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(CustomerListUrlSortField.email)}
|
||||||
|
className={classes.colEmail}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Customer Email" />
|
<FormattedMessage defaultMessage="Customer Email" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colOrders}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === CustomerListUrlSortField.orders
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="center"
|
||||||
|
onClick={() => onSort(CustomerListUrlSortField.orders)}
|
||||||
|
className={classes.colOrders}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="No. of Orders" />
|
<FormattedMessage defaultMessage="No. of Orders" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -11,8 +11,10 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { CustomerListUrlSortField } from "@saleor/customers/urls";
|
||||||
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
||||||
import CustomerList from "../CustomerList/CustomerList";
|
import CustomerList from "../CustomerList/CustomerList";
|
||||||
|
|
||||||
|
@ -20,14 +22,13 @@ export interface CustomerListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<CustomerListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
customers: ListCustomers_customers_edges_node[];
|
customers: ListCustomers_customers_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomerListPage: React.FC<CustomerListPageProps> = ({
|
const CustomerListPage: React.FC<CustomerListPageProps> = ({
|
||||||
currentTab,
|
currentTab,
|
||||||
customers,
|
|
||||||
disabled,
|
|
||||||
initialSearch,
|
initialSearch,
|
||||||
onAdd,
|
onAdd,
|
||||||
onAll,
|
onAll,
|
||||||
|
@ -43,12 +44,7 @@ const CustomerListPage: React.FC<CustomerListPageProps> = ({
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.customers)}>
|
<PageHeader title={intl.formatMessage(sectionNames.customers)}>
|
||||||
<Button
|
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||||
color="primary"
|
|
||||||
variant="contained"
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={onAdd}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Create customer"
|
defaultMessage="Create customer"
|
||||||
description="button"
|
description="button"
|
||||||
|
@ -73,11 +69,7 @@ const CustomerListPage: React.FC<CustomerListPageProps> = ({
|
||||||
onTabDelete={onTabDelete}
|
onTabDelete={onTabDelete}
|
||||||
onTabSave={onTabSave}
|
onTabSave={onTabSave}
|
||||||
/>
|
/>
|
||||||
<CustomerList
|
<CustomerList {...customerListProps} />
|
||||||
customers={customers}
|
|
||||||
disabled={disabled}
|
|
||||||
{...customerListProps}
|
|
||||||
/>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
customerAddPath,
|
customerAddPath,
|
||||||
|
@ -12,7 +13,8 @@ import {
|
||||||
customerListPath,
|
customerListPath,
|
||||||
CustomerListUrlQueryParams,
|
CustomerListUrlQueryParams,
|
||||||
customerPath,
|
customerPath,
|
||||||
CustomerUrlQueryParams
|
CustomerUrlQueryParams,
|
||||||
|
CustomerListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import CustomerAddressesViewComponent from "./views/CustomerAddresses";
|
import CustomerAddressesViewComponent from "./views/CustomerAddresses";
|
||||||
import CustomerCreateView from "./views/CustomerCreate";
|
import CustomerCreateView from "./views/CustomerCreate";
|
||||||
|
@ -21,16 +23,20 @@ import CustomerListViewComponent from "./views/CustomerList";
|
||||||
|
|
||||||
const CustomerListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const CustomerListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CustomerListUrlQueryParams = qs;
|
const params: CustomerListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
CustomerListUrlSortField
|
||||||
|
);
|
||||||
|
|
||||||
return <CustomerListViewComponent params={params} />;
|
return <CustomerListViewComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface CustomerDetailsRouteParams {
|
interface CustomerDetailsRouteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const CustomerDetailsView: React.FC<
|
const CustomerDetailsView: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<CustomerDetailsRouteParams>
|
CustomerDetailsRouteParams
|
||||||
> = ({ location, match }) => {
|
>> = ({ location, match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CustomerUrlQueryParams = qs;
|
const params: CustomerUrlQueryParams = qs;
|
||||||
|
|
||||||
|
@ -45,9 +51,9 @@ const CustomerDetailsView: React.FC<
|
||||||
interface CustomerAddressesRouteParams {
|
interface CustomerAddressesRouteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const CustomerAddressesView: React.FC<
|
const CustomerAddressesView: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<CustomerAddressesRouteParams>
|
CustomerAddressesRouteParams
|
||||||
> = ({ match }) => {
|
>> = ({ match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: CustomerAddressesUrlQueryParams = qs;
|
const params: CustomerAddressesUrlQueryParams = qs;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { fragmentAddress } from "../orders/queries";
|
import { fragmentAddress } from "../orders/queries";
|
||||||
import { TypedQuery } from "../queries";
|
import { TypedQuery } from "../queries";
|
||||||
import {
|
import {
|
||||||
|
@ -65,6 +66,7 @@ const customerList = gql`
|
||||||
$first: Int
|
$first: Int
|
||||||
$last: Int
|
$last: Int
|
||||||
$filter: CustomerFilterInput
|
$filter: CustomerFilterInput
|
||||||
|
$sort: UserSortingInput
|
||||||
) {
|
) {
|
||||||
customers(
|
customers(
|
||||||
after: $after
|
after: $after
|
||||||
|
@ -72,6 +74,7 @@ const customerList = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -90,7 +93,7 @@ const customerList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedCustomerListQuery = TypedQuery<
|
export const useCustomerListQuery = makeQuery<
|
||||||
ListCustomers,
|
ListCustomers,
|
||||||
ListCustomersVariables
|
ListCustomersVariables
|
||||||
>(customerList);
|
>(customerList);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { CustomerFilterInput } from "./../../types/globalTypes";
|
import { CustomerFilterInput, UserSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: ListCustomers
|
// GraphQL query operation: ListCustomers
|
||||||
|
@ -51,4 +51,5 @@ export interface ListCustomersVariables {
|
||||||
first?: number | null;
|
first?: number | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
filter?: CustomerFilterInput | null;
|
filter?: CustomerFilterInput | null;
|
||||||
|
sort?: UserSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ import {
|
||||||
Filters,
|
Filters,
|
||||||
Pagination,
|
Pagination,
|
||||||
SingleAction,
|
SingleAction,
|
||||||
TabActionDialog
|
TabActionDialog,
|
||||||
|
Sort
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
export const customerSection = "/customers/";
|
export const customerSection = "/customers/";
|
||||||
|
@ -19,9 +20,16 @@ export enum CustomerListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type CustomerListUrlFilters = Filters<CustomerListUrlFiltersEnum>;
|
export type CustomerListUrlFilters = Filters<CustomerListUrlFiltersEnum>;
|
||||||
export type CustomerListUrlDialog = "remove" | TabActionDialog;
|
export type CustomerListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
export enum CustomerListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
email = "email",
|
||||||
|
orders = "orders"
|
||||||
|
}
|
||||||
|
export type CustomerListUrlSort = Sort<CustomerListUrlSortField>;
|
||||||
export type CustomerListUrlQueryParams = ActiveTab &
|
export type CustomerListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
CustomerListUrlFilters &
|
CustomerListUrlFilters &
|
||||||
|
CustomerListUrlSort &
|
||||||
Dialog<CustomerListUrlDialog> &
|
Dialog<CustomerListUrlDialog> &
|
||||||
Pagination;
|
Pagination;
|
||||||
export const customerListUrl = (params?: CustomerListUrlQueryParams) =>
|
export const customerListUrl = (params?: CustomerListUrlQueryParams) =>
|
||||||
|
|
|
@ -19,9 +19,11 @@ import usePaginator, {
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import CustomerListPage from "../../components/CustomerListPage";
|
import CustomerListPage from "../../components/CustomerListPage";
|
||||||
import { TypedBulkRemoveCustomers } from "../../mutations";
|
import { TypedBulkRemoveCustomers } from "../../mutations";
|
||||||
import { TypedCustomerListQuery } from "../../queries";
|
import { useCustomerListQuery } from "../../queries";
|
||||||
import { BulkRemoveCustomers } from "../../types/BulkRemoveCustomers";
|
import { BulkRemoveCustomers } from "../../types/BulkRemoveCustomers";
|
||||||
import {
|
import {
|
||||||
customerAddUrl,
|
customerAddUrl,
|
||||||
|
@ -39,6 +41,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface CustomerListProps {
|
interface CustomerListProps {
|
||||||
params: CustomerListUrlQueryParams;
|
params: CustomerListUrlQueryParams;
|
||||||
|
@ -56,6 +59,20 @@ export const CustomerList: React.FC<CustomerListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useCustomerListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -116,130 +133,116 @@ export const CustomerList: React.FC<CustomerListProps> = ({ params }) => {
|
||||||
handleTabChange(tabs.length + 1);
|
handleTabChange(tabs.length + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
const queryVariables = React.useMemo(
|
maybe(() => data.customers.pageInfo),
|
||||||
() => ({
|
paginationState,
|
||||||
...paginationState,
|
params
|
||||||
filter: getFilterVariables(params)
|
|
||||||
}),
|
|
||||||
[params]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleBulkCustomerDelete = (data: BulkRemoveCustomers) => {
|
||||||
|
if (data.customerBulkDelete.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, customerListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedCustomerListQuery displayLoader variables={queryVariables}>
|
<TypedBulkRemoveCustomers onCompleted={handleBulkCustomerDelete}>
|
||||||
{({ data, loading, refetch }) => {
|
{(bulkRemoveCustomers, bulkRemoveCustomersOpts) => (
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
<>
|
||||||
maybe(() => data.customers.pageInfo),
|
<CustomerListPage
|
||||||
paginationState,
|
currentTab={currentTab}
|
||||||
params
|
initialSearch={params.query || ""}
|
||||||
);
|
onSearchChange={query => changeFilterField({ query })}
|
||||||
|
onAll={() => navigate(customerListUrl())}
|
||||||
const handleBulkCustomerDelete = (data: BulkRemoveCustomers) => {
|
onTabChange={handleTabChange}
|
||||||
if (data.customerBulkDelete.errors.length === 0) {
|
onTabDelete={() => openModal("delete-search")}
|
||||||
notify({
|
onTabSave={() => openModal("save-search")}
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
tabs={tabs.map(tab => tab.name)}
|
||||||
});
|
customers={maybe(() => data.customers.edges.map(edge => edge.node))}
|
||||||
reset();
|
settings={settings}
|
||||||
refetch();
|
disabled={loading}
|
||||||
closeModal();
|
pageInfo={pageInfo}
|
||||||
}
|
onAdd={() => navigate(customerAddUrl)}
|
||||||
};
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
return (
|
onUpdateListSettings={updateListSettings}
|
||||||
<TypedBulkRemoveCustomers onCompleted={handleBulkCustomerDelete}>
|
onRowClick={id => () => navigate(customerUrl(id))}
|
||||||
{(bulkRemoveCustomers, bulkRemoveCustomersOpts) => (
|
onSort={handleSort}
|
||||||
<>
|
toolbar={
|
||||||
<CustomerListPage
|
<IconButton
|
||||||
currentTab={currentTab}
|
color="primary"
|
||||||
initialSearch={params.query || ""}
|
onClick={() =>
|
||||||
onSearchChange={query => changeFilterField({ query })}
|
navigate(
|
||||||
onAll={() => navigate(customerListUrl())}
|
customerListUrl({
|
||||||
onTabChange={handleTabChange}
|
action: "remove",
|
||||||
onTabDelete={() => openModal("delete-search")}
|
ids: listElements
|
||||||
onTabSave={() => openModal("save-search")}
|
|
||||||
tabs={tabs.map(tab => tab.name)}
|
|
||||||
customers={maybe(() =>
|
|
||||||
data.customers.edges.map(edge => edge.node)
|
|
||||||
)}
|
|
||||||
settings={settings}
|
|
||||||
disabled={loading}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
onAdd={() => navigate(customerAddUrl)}
|
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
onRowClick={id => () => navigate(customerUrl(id))}
|
|
||||||
toolbar={
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
customerListUrl({
|
|
||||||
action: "remove",
|
|
||||||
ids: listElements
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
open={
|
|
||||||
params.action === "remove" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={bulkRemoveCustomersOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
bulkRemoveCustomers({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
)
|
||||||
variant="delete"
|
}
|
||||||
title={intl.formatMessage({
|
>
|
||||||
defaultMessage: "Delete Customers",
|
<DeleteIcon />
|
||||||
description: "dialog header"
|
</IconButton>
|
||||||
})}
|
}
|
||||||
>
|
isChecked={isSelected}
|
||||||
<DialogContentText>
|
selected={listElements.length}
|
||||||
<FormattedMessage
|
sort={getSortParams(params)}
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this customer} other{{displayQuantity} customers}}?"
|
toggle={toggle}
|
||||||
values={{
|
toggleAll={toggleAll}
|
||||||
counter: maybe(() => params.ids.length),
|
/>
|
||||||
displayQuantity: (
|
<ActionDialog
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
open={
|
||||||
)
|
params.action === "remove" && maybe(() => params.ids.length > 0)
|
||||||
}}
|
}
|
||||||
/>
|
onClose={closeModal}
|
||||||
</DialogContentText>
|
confirmButtonState={bulkRemoveCustomersOpts.status}
|
||||||
</ActionDialog>
|
onConfirm={() =>
|
||||||
<SaveFilterTabDialog
|
bulkRemoveCustomers({
|
||||||
open={params.action === "save-search"}
|
variables: {
|
||||||
confirmButtonState="default"
|
ids: params.ids
|
||||||
onClose={closeModal}
|
}
|
||||||
onSubmit={handleTabSave}
|
})
|
||||||
/>
|
}
|
||||||
<DeleteFilterTabDialog
|
variant="delete"
|
||||||
open={params.action === "delete-search"}
|
title={intl.formatMessage({
|
||||||
confirmButtonState="default"
|
defaultMessage: "Delete Customers",
|
||||||
onClose={closeModal}
|
description: "dialog header"
|
||||||
onSubmit={handleTabDelete}
|
})}
|
||||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
>
|
||||||
/>
|
<DialogContentText>
|
||||||
</>
|
<FormattedMessage
|
||||||
)}
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this customer} other{{displayQuantity} customers}}?"
|
||||||
</TypedBulkRemoveCustomers>
|
values={{
|
||||||
);
|
counter: maybe(() => params.ids.length),
|
||||||
}}
|
displayQuantity: (
|
||||||
</TypedCustomerListQuery>
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</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, "...")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TypedBulkRemoveCustomers>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default CustomerList;
|
export default CustomerList;
|
||||||
|
|
22
src/customers/views/CustomerList/sort.ts
Normal file
22
src/customers/views/CustomerList/sort.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { CustomerListUrlSortField } from "@saleor/customers/urls";
|
||||||
|
import { UserSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: CustomerListUrlSortField
|
||||||
|
): UserSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case CustomerListUrlSortField.email:
|
||||||
|
return UserSortField.EMAIL;
|
||||||
|
case CustomerListUrlSortField.name:
|
||||||
|
return UserSortField.LAST_NAME;
|
||||||
|
case CustomerListUrlSortField.orders:
|
||||||
|
return UserSortField.ORDER_COUNT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -15,11 +15,17 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
import { SaleType } from "@saleor/types/globalTypes";
|
import { SaleType } from "@saleor/types/globalTypes";
|
||||||
|
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||||
|
|
||||||
export interface SaleListProps extends ListProps, ListActions {
|
export interface SaleListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<SaleListUrlSortField> {
|
||||||
defaultCurrency: string;
|
defaultCurrency: string;
|
||||||
sales: SaleList_sales_edges_node[];
|
sales: SaleList_sales_edges_node[];
|
||||||
}
|
}
|
||||||
|
@ -68,10 +74,12 @@ const SaleList: React.FC<SaleListProps> = props => {
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
sales,
|
sales,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -89,21 +97,57 @@ const SaleList: React.FC<SaleListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === SaleListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(SaleListUrlSortField.name)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Name" description="sale name" />
|
<FormattedMessage defaultMessage="Name" description="sale name" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colStart}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === SaleListUrlSortField.startDate
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(SaleListUrlSortField.startDate)}
|
||||||
|
className={classes.colStart}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Starts"
|
defaultMessage="Starts"
|
||||||
description="sale start date"
|
description="sale start date"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colEnd}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === SaleListUrlSortField.endDate
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(SaleListUrlSortField.endDate)}
|
||||||
|
className={classes.colEnd}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Ends" description="sale end date" />
|
<FormattedMessage defaultMessage="Ends" description="sale end date" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colValue}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === SaleListUrlSortField.value
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(SaleListUrlSortField.value)}
|
||||||
|
className={classes.colValue}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Value" description="sale value" />
|
<FormattedMessage defaultMessage="Value" description="sale value" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -11,8 +11,10 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||||
import SaleList from "../SaleList";
|
import SaleList from "../SaleList";
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ export interface SaleListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<SaleListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
defaultCurrency: string;
|
defaultCurrency: string;
|
||||||
sales: SaleList_sales_edges_node[];
|
sales: SaleList_sales_edges_node[];
|
||||||
|
|
|
@ -15,11 +15,17 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
import { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
|
import { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
|
||||||
|
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||||
|
|
||||||
export interface VoucherListProps extends ListProps, ListActions {
|
export interface VoucherListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<VoucherListUrlSortField> {
|
||||||
defaultCurrency: string;
|
defaultCurrency: string;
|
||||||
vouchers: VoucherList_vouchers_edges_node[];
|
vouchers: VoucherList_vouchers_edges_node[];
|
||||||
}
|
}
|
||||||
|
@ -83,10 +89,12 @@ const VoucherList: React.FC<VoucherListProps> = props => {
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
vouchers,
|
vouchers,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -104,36 +112,85 @@ const VoucherList: React.FC<VoucherListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.code
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.code)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Code" description="voucher code" />
|
<FormattedMessage defaultMessage="Code" description="voucher code" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colMinSpent}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.minSpent
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.minSpent)}
|
||||||
|
className={classes.colMinSpent}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Min. Spent"
|
defaultMessage="Min. Spent"
|
||||||
description="minimum amount of spent money to activate voucher"
|
description="minimum amount of spent money to activate voucher"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colStart}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.startDate
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.startDate)}
|
||||||
|
className={classes.colStart}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Starts"
|
defaultMessage="Starts"
|
||||||
description="voucher is active from date"
|
description="voucher is active from date"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colEnd}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.endDate
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.endDate)}
|
||||||
|
className={classes.colEnd}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Ends"
|
defaultMessage="Ends"
|
||||||
description="voucher is active until date"
|
description="voucher is active until date"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colValue}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.value
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.value)}
|
||||||
|
className={classes.colValue}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Value"
|
defaultMessage="Value"
|
||||||
description="voucher value"
|
description="voucher value"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colUses}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === VoucherListUrlSortField.limit
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(VoucherListUrlSortField.limit)}
|
||||||
|
className={classes.colUses}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Uses" description="voucher uses" />
|
<FormattedMessage defaultMessage="Uses" description="voucher uses" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -11,8 +11,10 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||||
import VoucherList from "../VoucherList";
|
import VoucherList from "../VoucherList";
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ export interface VoucherListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<VoucherListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
defaultCurrency: string;
|
defaultCurrency: string;
|
||||||
vouchers: VoucherList_vouchers_edges_node[];
|
vouchers: VoucherList_vouchers_edges_node[];
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import { saleDetailsPageTab } from "./components/SaleDetailsPage";
|
import { saleDetailsPageTab } from "./components/SaleDetailsPage";
|
||||||
import { voucherDetailsPageTab } from "./components/VoucherDetailsPage";
|
import { voucherDetailsPageTab } from "./components/VoucherDetailsPage";
|
||||||
|
@ -17,7 +18,9 @@ import {
|
||||||
voucherListPath,
|
voucherListPath,
|
||||||
VoucherListUrlQueryParams,
|
VoucherListUrlQueryParams,
|
||||||
voucherPath,
|
voucherPath,
|
||||||
VoucherUrlQueryParams
|
VoucherUrlQueryParams,
|
||||||
|
SaleListUrlSortField,
|
||||||
|
VoucherListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import SaleCreateView from "./views/SaleCreate";
|
import SaleCreateView from "./views/SaleCreate";
|
||||||
import SaleDetailsViewComponent from "./views/SaleDetails";
|
import SaleDetailsViewComponent from "./views/SaleDetails";
|
||||||
|
@ -28,7 +31,7 @@ import VoucherListViewComponent from "./views/VoucherList";
|
||||||
|
|
||||||
const SaleListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const SaleListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: SaleListUrlQueryParams = qs;
|
const params: SaleListUrlQueryParams = asSortParams(qs, SaleListUrlSortField);
|
||||||
return <SaleListViewComponent params={params} />;
|
return <SaleListViewComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +44,7 @@ const SaleDetailsView: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
...qs,
|
...qs,
|
||||||
activeTab: saleDetailsPageTab(activeTab)
|
activeTab: saleDetailsPageTab(activeTab)
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SaleDetailsViewComponent
|
<SaleDetailsViewComponent
|
||||||
id={decodeURIComponent(match.params.id)}
|
id={decodeURIComponent(match.params.id)}
|
||||||
|
@ -51,7 +55,11 @@ const SaleDetailsView: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
|
|
||||||
const VoucherListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const VoucherListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: VoucherListUrlQueryParams = qs;
|
const params: VoucherListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
VoucherListUrlSortField,
|
||||||
|
VoucherListUrlSortField.code
|
||||||
|
);
|
||||||
return <VoucherListViewComponent params={params} />;
|
return <VoucherListViewComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { pageInfoFragment, TypedQuery } from "../queries";
|
import { pageInfoFragment, TypedQuery } from "../queries";
|
||||||
import { SaleDetails, SaleDetailsVariables } from "./types/SaleDetails";
|
import { SaleDetails, SaleDetailsVariables } from "./types/SaleDetails";
|
||||||
import { SaleList, SaleListVariables } from "./types/SaleList";
|
import { SaleList, SaleListVariables } from "./types/SaleList";
|
||||||
|
@ -172,6 +173,7 @@ export const saleList = gql`
|
||||||
$first: Int
|
$first: Int
|
||||||
$last: Int
|
$last: Int
|
||||||
$filter: SaleFilterInput
|
$filter: SaleFilterInput
|
||||||
|
$sort: SaleSortingInput
|
||||||
) {
|
) {
|
||||||
sales(
|
sales(
|
||||||
after: $after
|
after: $after
|
||||||
|
@ -179,6 +181,7 @@ export const saleList = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -191,7 +194,9 @@ export const saleList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedSaleList = TypedQuery<SaleList, SaleListVariables>(saleList);
|
export const useSaleListQuery = makeQuery<SaleList, SaleListVariables>(
|
||||||
|
saleList
|
||||||
|
);
|
||||||
|
|
||||||
export const voucherList = gql`
|
export const voucherList = gql`
|
||||||
${pageInfoFragment}
|
${pageInfoFragment}
|
||||||
|
@ -202,6 +207,7 @@ export const voucherList = gql`
|
||||||
$first: Int
|
$first: Int
|
||||||
$last: Int
|
$last: Int
|
||||||
$filter: VoucherFilterInput
|
$filter: VoucherFilterInput
|
||||||
|
$sort: VoucherSortingInput
|
||||||
) {
|
) {
|
||||||
vouchers(
|
vouchers(
|
||||||
after: $after
|
after: $after
|
||||||
|
@ -209,6 +215,7 @@ export const voucherList = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -221,7 +228,7 @@ export const voucherList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedVoucherList = TypedQuery<VoucherList, VoucherListVariables>(
|
export const useVoucherListQuery = makeQuery<VoucherList, VoucherListVariables>(
|
||||||
voucherList
|
voucherList
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { SaleFilterInput, SaleType } from "./../../types/globalTypes";
|
import { SaleFilterInput, SaleSortingInput, SaleType } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: SaleList
|
// GraphQL query operation: SaleList
|
||||||
|
@ -47,4 +47,5 @@ export interface SaleListVariables {
|
||||||
first?: number | null;
|
first?: number | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
filter?: SaleFilterInput | null;
|
filter?: SaleFilterInput | null;
|
||||||
|
sort?: SaleSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherFilterInput, DiscountValueTypeEnum } from "./../../types/globalTypes";
|
import { VoucherFilterInput, VoucherSortingInput, DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: VoucherList
|
// GraphQL query operation: VoucherList
|
||||||
|
@ -63,4 +63,5 @@ export interface VoucherListVariables {
|
||||||
first?: number | null;
|
first?: number | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
filter?: VoucherFilterInput | null;
|
filter?: VoucherFilterInput | null;
|
||||||
|
sort?: VoucherSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
Filters,
|
Filters,
|
||||||
Pagination,
|
Pagination,
|
||||||
TabActionDialog
|
TabActionDialog,
|
||||||
|
Sort
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { SaleDetailsPageTab } from "./components/SaleDetailsPage";
|
import { SaleDetailsPageTab } from "./components/SaleDetailsPage";
|
||||||
import { VoucherDetailsPageTab } from "./components/VoucherDetailsPage";
|
import { VoucherDetailsPageTab } from "./components/VoucherDetailsPage";
|
||||||
|
@ -21,11 +22,20 @@ export enum SaleListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum>;
|
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum>;
|
||||||
export type SaleListUrlDialog = "remove" | TabActionDialog;
|
export type SaleListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
export enum SaleListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
endDate = "end-date",
|
||||||
|
startDate = "start-date",
|
||||||
|
type = "type",
|
||||||
|
value = "value"
|
||||||
|
}
|
||||||
|
export type SaleListUrlSort = Sort<SaleListUrlSortField>;
|
||||||
export type SaleListUrlQueryParams = ActiveTab &
|
export type SaleListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
Dialog<SaleListUrlDialog> &
|
Dialog<SaleListUrlDialog> &
|
||||||
Pagination &
|
Pagination &
|
||||||
SaleListUrlFilters;
|
SaleListUrlFilters &
|
||||||
|
SaleListUrlSort;
|
||||||
export const saleListUrl = (params?: SaleListUrlQueryParams) =>
|
export const saleListUrl = (params?: SaleListUrlQueryParams) =>
|
||||||
saleListPath + "?" + stringifyQs(params);
|
saleListPath + "?" + stringifyQs(params);
|
||||||
export const salePath = (id: string) => urlJoin(saleSection, id);
|
export const salePath = (id: string) => urlJoin(saleSection, id);
|
||||||
|
@ -53,11 +63,22 @@ export enum VoucherListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum>;
|
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum>;
|
||||||
export type VoucherListUrlDialog = "remove" | TabActionDialog;
|
export type VoucherListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
export enum VoucherListUrlSortField {
|
||||||
|
code = "code",
|
||||||
|
endDate = "end-date",
|
||||||
|
limit = "limit",
|
||||||
|
minSpent = "min-spent",
|
||||||
|
startDate = "start-date",
|
||||||
|
type = "type",
|
||||||
|
value = "value"
|
||||||
|
}
|
||||||
|
export type VoucherListUrlSort = Sort<VoucherListUrlSortField>;
|
||||||
export type VoucherListUrlQueryParams = ActiveTab &
|
export type VoucherListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
Dialog<VoucherListUrlDialog> &
|
Dialog<VoucherListUrlDialog> &
|
||||||
Pagination &
|
Pagination &
|
||||||
VoucherListUrlFilters;
|
VoucherListUrlFilters &
|
||||||
|
VoucherListUrlSort;
|
||||||
export const voucherListUrl = (params?: VoucherListUrlQueryParams) =>
|
export const voucherListUrl = (params?: VoucherListUrlQueryParams) =>
|
||||||
voucherListPath + "?" + stringifyQs(params);
|
voucherListPath + "?" + stringifyQs(params);
|
||||||
export const voucherPath = (id: string) => urlJoin(voucherSection, id);
|
export const voucherPath = (id: string) => urlJoin(voucherSection, id);
|
||||||
|
|
|
@ -21,9 +21,11 @@ import useShop from "@saleor/hooks/useShop";
|
||||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import SaleListPage from "../../components/SaleListPage";
|
import SaleListPage from "../../components/SaleListPage";
|
||||||
import { TypedSaleBulkDelete } from "../../mutations";
|
import { TypedSaleBulkDelete } from "../../mutations";
|
||||||
import { TypedSaleList } from "../../queries";
|
import { useSaleListQuery } from "../../queries";
|
||||||
import { SaleBulkDelete } from "../../types/SaleBulkDelete";
|
import { SaleBulkDelete } from "../../types/SaleBulkDelete";
|
||||||
import {
|
import {
|
||||||
saleAddUrl,
|
saleAddUrl,
|
||||||
|
@ -41,6 +43,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface SaleListProps {
|
interface SaleListProps {
|
||||||
params: SaleListUrlQueryParams;
|
params: SaleListUrlQueryParams;
|
||||||
|
@ -59,6 +62,20 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useSaleListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -119,135 +136,122 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
|
||||||
handleTabChange(tabs.length + 1);
|
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);
|
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
|
||||||
|
|
||||||
return (
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
<TypedSaleList displayLoader variables={queryVariables}>
|
maybe(() => data.sales.pageInfo),
|
||||||
{({ data, loading, refetch }) => {
|
paginationState,
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
params
|
||||||
maybe(() => data.sales.pageInfo),
|
);
|
||||||
paginationState,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSaleBulkDelete = (data: SaleBulkDelete) => {
|
const handleSaleBulkDelete = (data: SaleBulkDelete) => {
|
||||||
if (data.saleBulkDelete.errors.length === 0) {
|
if (data.saleBulkDelete.errors.length === 0) {
|
||||||
notify({
|
notify({
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
});
|
});
|
||||||
reset();
|
reset();
|
||||||
closeModal();
|
closeModal();
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, saleListUrl, params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TypedSaleBulkDelete onCompleted={handleSaleBulkDelete}>
|
||||||
|
{(saleBulkDelete, saleBulkDeleteOpts) => {
|
||||||
|
const onSaleBulkDelete = () =>
|
||||||
|
saleBulkDelete({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedSaleBulkDelete onCompleted={handleSaleBulkDelete}>
|
<>
|
||||||
{(saleBulkDelete, saleBulkDeleteOpts) => {
|
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
|
||||||
const onSaleBulkDelete = () =>
|
<SaleListPage
|
||||||
saleBulkDelete({
|
currentTab={currentTab}
|
||||||
variables: {
|
initialSearch={params.query || ""}
|
||||||
ids: params.ids
|
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}
|
||||||
|
disabled={loading}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
onAdd={() => navigate(saleAddUrl)}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onSort={handleSort}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
onRowClick={id => () => navigate(saleUrl(id))}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
toolbar={
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
saleListUrl({
|
||||||
|
action: "remove",
|
||||||
|
ids: listElements
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
>
|
||||||
|
<DeleteIcon />
|
||||||
return (
|
</IconButton>
|
||||||
<>
|
}
|
||||||
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
|
/>
|
||||||
<SaleListPage
|
<ActionDialog
|
||||||
currentTab={currentTab}
|
confirmButtonState={saleBulkDeleteOpts.status}
|
||||||
initialSearch={params.query || ""}
|
onClose={closeModal}
|
||||||
onSearchChange={query => changeFilterField({ query })}
|
onConfirm={onSaleBulkDelete}
|
||||||
onAll={() => navigate(saleListUrl())}
|
open={params.action === "remove" && canOpenBulkActionDialog}
|
||||||
onTabChange={handleTabChange}
|
title={intl.formatMessage({
|
||||||
onTabDelete={() => openModal("delete-search")}
|
defaultMessage: "Delete Sales",
|
||||||
onTabSave={() => openModal("save-search")}
|
description: "dialog header"
|
||||||
tabs={tabs.map(tab => tab.name)}
|
})}
|
||||||
defaultCurrency={maybe(() => shop.defaultCurrency)}
|
variant="delete"
|
||||||
sales={maybe(() => data.sales.edges.map(edge => edge.node))}
|
>
|
||||||
settings={settings}
|
{canOpenBulkActionDialog && (
|
||||||
disabled={loading}
|
<DialogContentText>
|
||||||
pageInfo={pageInfo}
|
<FormattedMessage
|
||||||
onAdd={() => navigate(saleAddUrl)}
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this sale} other{{displayQuantity} sales}}?"
|
||||||
onNextPage={loadNextPage}
|
description="dialog content"
|
||||||
onPreviousPage={loadPreviousPage}
|
values={{
|
||||||
onUpdateListSettings={updateListSettings}
|
counter: params.ids.length,
|
||||||
onRowClick={id => () => navigate(saleUrl(id))}
|
displayQuantity: <strong>{params.ids.length}</strong>
|
||||||
isChecked={isSelected}
|
}}
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
saleListUrl({
|
|
||||||
action: "remove",
|
|
||||||
ids: listElements
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
</DialogContentText>
|
||||||
confirmButtonState={saleBulkDeleteOpts.status}
|
)}
|
||||||
onClose={closeModal}
|
</ActionDialog>
|
||||||
onConfirm={onSaleBulkDelete}
|
<SaveFilterTabDialog
|
||||||
open={params.action === "remove" && canOpenBulkActionDialog}
|
open={params.action === "save-search"}
|
||||||
title={intl.formatMessage({
|
confirmButtonState="default"
|
||||||
defaultMessage: "Delete Sales",
|
onClose={closeModal}
|
||||||
description: "dialog header"
|
onSubmit={handleTabSave}
|
||||||
})}
|
/>
|
||||||
variant="delete"
|
<DeleteFilterTabDialog
|
||||||
>
|
open={params.action === "delete-search"}
|
||||||
{canOpenBulkActionDialog && (
|
confirmButtonState="default"
|
||||||
<DialogContentText>
|
onClose={closeModal}
|
||||||
<FormattedMessage
|
onSubmit={handleTabDelete}
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this sale} other{{displayQuantity} sales}}?"
|
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||||
description="dialog content"
|
/>
|
||||||
values={{
|
</>
|
||||||
counter: params.ids.length,
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{params.ids.length}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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, "...")}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</TypedSaleBulkDelete>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</TypedSaleList>
|
</TypedSaleBulkDelete>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default SaleList;
|
export default SaleList;
|
||||||
|
|
24
src/discounts/views/SaleList/sort.ts
Normal file
24
src/discounts/views/SaleList/sort.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
||||||
|
import { SaleSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(sort: SaleListUrlSortField): SaleSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case SaleListUrlSortField.name:
|
||||||
|
return SaleSortField.NAME;
|
||||||
|
case SaleListUrlSortField.startDate:
|
||||||
|
return SaleSortField.START_DATE;
|
||||||
|
case SaleListUrlSortField.endDate:
|
||||||
|
return SaleSortField.END_DATE;
|
||||||
|
case SaleListUrlSortField.type:
|
||||||
|
return SaleSortField.TYPE;
|
||||||
|
case SaleListUrlSortField.value:
|
||||||
|
return SaleSortField.VALUE;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -21,9 +21,11 @@ import useShop from "@saleor/hooks/useShop";
|
||||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import VoucherListPage from "../../components/VoucherListPage";
|
import VoucherListPage from "../../components/VoucherListPage";
|
||||||
import { TypedVoucherBulkDelete } from "../../mutations";
|
import { TypedVoucherBulkDelete } from "../../mutations";
|
||||||
import { TypedVoucherList } from "../../queries";
|
import { useVoucherListQuery } from "../../queries";
|
||||||
import { VoucherBulkDelete } from "../../types/VoucherBulkDelete";
|
import { VoucherBulkDelete } from "../../types/VoucherBulkDelete";
|
||||||
import {
|
import {
|
||||||
voucherAddUrl,
|
voucherAddUrl,
|
||||||
|
@ -41,6 +43,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface VoucherListProps {
|
interface VoucherListProps {
|
||||||
params: VoucherListUrlQueryParams;
|
params: VoucherListUrlQueryParams;
|
||||||
|
@ -59,6 +62,20 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useVoucherListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -119,139 +136,122 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
|
||||||
handleTabChange(tabs.length + 1);
|
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);
|
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
|
||||||
|
|
||||||
return (
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
<TypedVoucherList displayLoader variables={queryVariables}>
|
maybe(() => data.vouchers.pageInfo),
|
||||||
{({ data, loading, refetch }) => {
|
paginationState,
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
params
|
||||||
maybe(() => data.vouchers.pageInfo),
|
);
|
||||||
paginationState,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleVoucherBulkDelete = (data: VoucherBulkDelete) => {
|
const handleVoucherBulkDelete = (data: VoucherBulkDelete) => {
|
||||||
if (data.voucherBulkDelete.errors.length === 0) {
|
if (data.voucherBulkDelete.errors.length === 0) {
|
||||||
notify({
|
notify({
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
});
|
});
|
||||||
reset();
|
reset();
|
||||||
closeModal();
|
closeModal();
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, voucherListUrl, params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TypedVoucherBulkDelete onCompleted={handleVoucherBulkDelete}>
|
||||||
|
{(voucherBulkDelete, voucherBulkDeleteOpts) => {
|
||||||
|
const onVoucherBulkDelete = () =>
|
||||||
|
voucherBulkDelete({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedVoucherBulkDelete onCompleted={handleVoucherBulkDelete}>
|
<>
|
||||||
{(voucherBulkDelete, voucherBulkDeleteOpts) => {
|
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
|
||||||
const onVoucherBulkDelete = () =>
|
<VoucherListPage
|
||||||
voucherBulkDelete({
|
currentTab={currentTab}
|
||||||
variables: {
|
initialSearch={params.query || ""}
|
||||||
ids: params.ids
|
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(() => data.vouchers.edges.map(edge => edge.node))}
|
||||||
|
disabled={loading}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
onAdd={() => navigate(voucherAddUrl)}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
onRowClick={id => () => navigate(voucherUrl(id))}
|
||||||
|
onSort={handleSort}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
toolbar={
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
voucherListUrl({
|
||||||
|
action: "remove",
|
||||||
|
ids: listElements
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
>
|
||||||
|
<DeleteIcon />
|
||||||
return (
|
</IconButton>
|
||||||
<>
|
}
|
||||||
<WindowTitle
|
/>
|
||||||
title={intl.formatMessage(sectionNames.vouchers)}
|
<ActionDialog
|
||||||
|
confirmButtonState={voucherBulkDeleteOpts.status}
|
||||||
|
onClose={closeModal}
|
||||||
|
onConfirm={onVoucherBulkDelete}
|
||||||
|
open={params.action === "remove" && canOpenBulkActionDialog}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Vouchers",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
|
variant="delete"
|
||||||
|
>
|
||||||
|
{canOpenBulkActionDialog && (
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this voucher} other{{displayQuantity} vouchers}}?"
|
||||||
|
description="dialog content"
|
||||||
|
values={{
|
||||||
|
counter: params.ids.length,
|
||||||
|
displayQuantity: <strong>{params.ids.length}</strong>
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<VoucherListPage
|
</DialogContentText>
|
||||||
currentTab={currentTab}
|
)}
|
||||||
initialSearch={params.query || ""}
|
</ActionDialog>
|
||||||
onSearchChange={query => changeFilterField({ query })}
|
<SaveFilterTabDialog
|
||||||
onAll={() => navigate(voucherListUrl())}
|
open={params.action === "save-search"}
|
||||||
onTabChange={handleTabChange}
|
confirmButtonState="default"
|
||||||
onTabDelete={() => openModal("delete-search")}
|
onClose={closeModal}
|
||||||
onTabSave={() => openModal("save-search")}
|
onSubmit={handleTabSave}
|
||||||
tabs={tabs.map(tab => tab.name)}
|
/>
|
||||||
defaultCurrency={maybe(() => shop.defaultCurrency)}
|
<DeleteFilterTabDialog
|
||||||
settings={settings}
|
open={params.action === "delete-search"}
|
||||||
vouchers={maybe(() =>
|
confirmButtonState="default"
|
||||||
data.vouchers.edges.map(edge => edge.node)
|
onClose={closeModal}
|
||||||
)}
|
onSubmit={handleTabDelete}
|
||||||
disabled={loading}
|
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||||
pageInfo={pageInfo}
|
/>
|
||||||
onAdd={() => navigate(voucherAddUrl)}
|
</>
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
onRowClick={id => () => navigate(voucherUrl(id))}
|
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
voucherListUrl({
|
|
||||||
action: "remove",
|
|
||||||
ids: listElements
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
confirmButtonState={voucherBulkDeleteOpts.status}
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={onVoucherBulkDelete}
|
|
||||||
open={params.action === "remove" && canOpenBulkActionDialog}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete Vouchers",
|
|
||||||
description: "dialog header"
|
|
||||||
})}
|
|
||||||
variant="delete"
|
|
||||||
>
|
|
||||||
{canOpenBulkActionDialog && (
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this voucher} other{{displayQuantity} vouchers}}?"
|
|
||||||
description="dialog content"
|
|
||||||
values={{
|
|
||||||
counter: params.ids.length,
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{params.ids.length}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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, "...")}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</TypedVoucherBulkDelete>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</TypedVoucherList>
|
</TypedVoucherBulkDelete>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default VoucherList;
|
export default VoucherList;
|
||||||
|
|
30
src/discounts/views/VoucherList/sort.ts
Normal file
30
src/discounts/views/VoucherList/sort.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
||||||
|
import { VoucherSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: VoucherListUrlSortField
|
||||||
|
): VoucherSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case VoucherListUrlSortField.code:
|
||||||
|
return VoucherSortField.CODE;
|
||||||
|
case VoucherListUrlSortField.endDate:
|
||||||
|
return VoucherSortField.END_DATE;
|
||||||
|
case VoucherListUrlSortField.minSpent:
|
||||||
|
return VoucherSortField.MINIMUM_SPENT_AMOUNT;
|
||||||
|
case VoucherListUrlSortField.limit:
|
||||||
|
return VoucherSortField.USAGE_LIMIT;
|
||||||
|
case VoucherListUrlSortField.startDate:
|
||||||
|
return VoucherSortField.START_DATE;
|
||||||
|
case VoucherListUrlSortField.type:
|
||||||
|
return VoucherSortField.TYPE;
|
||||||
|
case VoucherListUrlSortField.value:
|
||||||
|
return VoucherSortField.VALUE;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
17
src/misc.ts
17
src/misc.ts
|
@ -348,9 +348,22 @@ export function findInEnum<TEnum extends object>(
|
||||||
throw new Error(`Key ${needle} not found in enum`);
|
throw new Error(`Key ${needle} not found in enum`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseBoolean(a: string): boolean {
|
export function findValueInEnum<TEnum extends object>(
|
||||||
|
needle: string,
|
||||||
|
haystack: TEnum
|
||||||
|
) {
|
||||||
|
const match = Object.entries(haystack).find(([_, value]) => value === needle);
|
||||||
|
|
||||||
|
if (!!match) {
|
||||||
|
return match[1] as TEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Value ${needle} not found in enum`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseBoolean(a: string, defaultValue: boolean): boolean {
|
||||||
if (a === undefined) {
|
if (a === undefined) {
|
||||||
return true;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
return a === "true";
|
return a === "true";
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,16 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { MenuListUrlSortField } from "@saleor/navigation/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { MenuList_menus_edges_node } from "../../types/MenuList";
|
import { MenuList_menus_edges_node } from "../../types/MenuList";
|
||||||
|
|
||||||
export interface MenuListProps extends ListProps, ListActions {
|
export interface MenuListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<MenuListUrlSortField> {
|
||||||
menus: MenuList_menus_edges_node[];
|
menus: MenuList_menus_edges_node[];
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -57,8 +63,10 @@ const MenuList: React.FC<MenuListProps> = props => {
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -77,19 +85,37 @@ const MenuList: React.FC<MenuListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colTitle}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === MenuListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(MenuListUrlSortField.name)}
|
||||||
|
className={classes.colTitle}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Menu Title"
|
defaultMessage="Menu Title"
|
||||||
id="menuListMenutitle"
|
id="menuListMenutitle"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colItems}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === MenuListUrlSortField.items
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(MenuListUrlSortField.items)}
|
||||||
|
className={classes.colItems}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Items"
|
defaultMessage="Items"
|
||||||
description="number of menu items"
|
description="number of menu items"
|
||||||
id="menuListItems"
|
id="menuListItems"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell />
|
<TableCell />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
|
|
|
@ -7,18 +7,21 @@ import AppHeader from "@saleor/components/AppHeader";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { ListActions, PageListProps } from "@saleor/types";
|
import { ListActions, PageListProps, SortPage } from "@saleor/types";
|
||||||
|
import { MenuListUrlSortField } from "@saleor/navigation/urls";
|
||||||
import { MenuList_menus_edges_node } from "../../types/MenuList";
|
import { MenuList_menus_edges_node } from "../../types/MenuList";
|
||||||
import MenuList from "../MenuList";
|
import MenuList from "../MenuList";
|
||||||
|
|
||||||
export interface MenuListPageProps extends PageListProps, ListActions {
|
export interface MenuListPageProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<MenuListUrlSortField> {
|
||||||
menus: MenuList_menus_edges_node[];
|
menus: MenuList_menus_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuListPage: React.FC<MenuListPageProps> = ({
|
const MenuListPage: React.FC<MenuListPageProps> = ({
|
||||||
disabled,
|
|
||||||
onAdd,
|
onAdd,
|
||||||
onBack,
|
onBack,
|
||||||
...listProps
|
...listProps
|
||||||
|
@ -30,12 +33,7 @@ const MenuListPage: React.FC<MenuListPageProps> = ({
|
||||||
{intl.formatMessage(sectionNames.configuration)}
|
{intl.formatMessage(sectionNames.configuration)}
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.navigation)}>
|
<PageHeader title={intl.formatMessage(sectionNames.navigation)}>
|
||||||
<Button
|
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||||
color="primary"
|
|
||||||
disabled={disabled}
|
|
||||||
variant="contained"
|
|
||||||
onClick={onAdd}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Create Menu"
|
defaultMessage="Create Menu"
|
||||||
description="button"
|
description="button"
|
||||||
|
@ -43,7 +41,7 @@ const MenuListPage: React.FC<MenuListPageProps> = ({
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<MenuList disabled={disabled} {...listProps} />
|
<MenuList {...listProps} />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,13 +2,21 @@ import { parse as parseQs } from "qs";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { menuListPath, menuPath } from "./urls";
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
|
import {
|
||||||
|
menuListPath,
|
||||||
|
menuPath,
|
||||||
|
MenuListUrlQueryParams,
|
||||||
|
MenuListUrlSortField
|
||||||
|
} from "./urls";
|
||||||
import MenuDetailsComponent from "./views/MenuDetails";
|
import MenuDetailsComponent from "./views/MenuDetails";
|
||||||
import MenuListComponent from "./views/MenuList";
|
import MenuListComponent from "./views/MenuList";
|
||||||
|
|
||||||
const MenuList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const MenuList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
return <MenuListComponent params={qs} />;
|
const params: MenuListUrlQueryParams = asSortParams(qs, MenuListUrlSortField);
|
||||||
|
|
||||||
|
return <MenuListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MenuDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
const MenuDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
|
@ -16,6 +24,7 @@ const MenuDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
match
|
match
|
||||||
}) => {
|
}) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuDetailsComponent
|
<MenuDetailsComponent
|
||||||
id={decodeURIComponent(match.params.id)}
|
id={decodeURIComponent(match.params.id)}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { pageInfoFragment, TypedQuery } from "../queries";
|
import { pageInfoFragment, TypedQuery } from "../queries";
|
||||||
import { MenuDetails, MenuDetailsVariables } from "./types/MenuDetails";
|
import { MenuDetails, MenuDetailsVariables } from "./types/MenuDetails";
|
||||||
import { MenuList, MenuListVariables } from "./types/MenuList";
|
import { MenuList, MenuListVariables } from "./types/MenuList";
|
||||||
|
@ -75,8 +76,20 @@ export const menuDetailsFragment = gql`
|
||||||
const menuList = gql`
|
const menuList = gql`
|
||||||
${menuFragment}
|
${menuFragment}
|
||||||
${pageInfoFragment}
|
${pageInfoFragment}
|
||||||
query MenuList($first: Int, $after: String, $last: Int, $before: String) {
|
query MenuList(
|
||||||
menus(first: $first, after: $after, before: $before, last: $last) {
|
$first: Int
|
||||||
|
$after: String
|
||||||
|
$last: Int
|
||||||
|
$before: String
|
||||||
|
$sort: MenuSortingInput
|
||||||
|
) {
|
||||||
|
menus(
|
||||||
|
first: $first
|
||||||
|
after: $after
|
||||||
|
before: $before
|
||||||
|
last: $last
|
||||||
|
sortBy: $sort
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...MenuFragment
|
...MenuFragment
|
||||||
|
@ -88,7 +101,9 @@ const menuList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const MenuListQuery = TypedQuery<MenuList, MenuListVariables>(menuList);
|
export const useMenuListQuery = makeQuery<MenuList, MenuListVariables>(
|
||||||
|
menuList
|
||||||
|
);
|
||||||
|
|
||||||
const menuDetails = gql`
|
const menuDetails = gql`
|
||||||
${menuDetailsFragment}
|
${menuDetailsFragment}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { MenuSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: MenuList
|
// GraphQL query operation: MenuList
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
@ -46,4 +48,5 @@ export interface MenuListVariables {
|
||||||
after?: string | null;
|
after?: string | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
|
sort?: MenuSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
import { stringify as stringifyQs } from "qs";
|
import { stringify as stringifyQs } from "qs";
|
||||||
import urlJoin from "url-join";
|
import urlJoin from "url-join";
|
||||||
|
|
||||||
import { BulkAction, Dialog, Pagination, SingleAction } from "../types";
|
import { BulkAction, Dialog, Pagination, SingleAction, Sort } from "../types";
|
||||||
|
|
||||||
export const navigationSection = "/navigation";
|
export const navigationSection = "/navigation";
|
||||||
|
|
||||||
export const menuListPath = navigationSection;
|
export const menuListPath = navigationSection;
|
||||||
export type MenuListUrlDialog = "add" | "remove" | "remove-many";
|
export type MenuListUrlDialog = "add" | "remove" | "remove-many";
|
||||||
|
export enum MenuListUrlSortField {
|
||||||
|
name = "name",
|
||||||
|
items = "items"
|
||||||
|
}
|
||||||
|
export type MenuListUrlSort = Sort<MenuListUrlSortField>;
|
||||||
export type MenuListUrlQueryParams = BulkAction &
|
export type MenuListUrlQueryParams = BulkAction &
|
||||||
Dialog<MenuListUrlDialog> &
|
Dialog<MenuListUrlDialog> &
|
||||||
|
MenuListUrlSort &
|
||||||
Pagination &
|
Pagination &
|
||||||
SingleAction;
|
SingleAction;
|
||||||
export const menuListUrl = (params?: MenuListUrlQueryParams) =>
|
export const menuListUrl = (params?: MenuListUrlQueryParams) =>
|
||||||
|
|
|
@ -1,259 +0,0 @@
|
||||||
import Button from "@material-ui/core/Button";
|
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
|
||||||
import React from "react";
|
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
|
||||||
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 { buttonMessages, commonMessages } from "@saleor/intl";
|
|
||||||
import { maybe } from "@saleor/misc";
|
|
||||||
import { ListViews } from "@saleor/types";
|
|
||||||
import MenuCreateDialog from "../components/MenuCreateDialog";
|
|
||||||
import MenuListPage from "../components/MenuListPage";
|
|
||||||
import {
|
|
||||||
MenuBulkDeleteMutation,
|
|
||||||
MenuCreateMutation,
|
|
||||||
MenuDeleteMutation
|
|
||||||
} from "../mutations";
|
|
||||||
import { MenuListQuery } from "../queries";
|
|
||||||
import { MenuBulkDelete } from "../types/MenuBulkDelete";
|
|
||||||
import { MenuCreate } from "../types/MenuCreate";
|
|
||||||
import { MenuDelete } from "../types/MenuDelete";
|
|
||||||
import { menuListUrl, MenuListUrlQueryParams, menuUrl } from "../urls";
|
|
||||||
|
|
||||||
interface MenuListProps {
|
|
||||||
params: MenuListUrlQueryParams;
|
|
||||||
}
|
|
||||||
const MenuList: React.FC<MenuListProps> = ({ params }) => {
|
|
||||||
const navigate = useNavigator();
|
|
||||||
const notify = useNotifier();
|
|
||||||
const paginate = usePaginator();
|
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
|
||||||
params.ids
|
|
||||||
);
|
|
||||||
const { updateListSettings, settings } = useListSettings(
|
|
||||||
ListViews.NAVIGATION_LIST
|
|
||||||
);
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
const closeModal = () =>
|
|
||||||
navigate(
|
|
||||||
menuListUrl({
|
|
||||||
...params,
|
|
||||||
action: undefined,
|
|
||||||
id: undefined,
|
|
||||||
ids: undefined
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuListQuery variables={paginationState}>
|
|
||||||
{({ data, loading, refetch }) => {
|
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
|
||||||
maybe(() => data.menus.pageInfo),
|
|
||||||
paginationState,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCreate = (data: MenuCreate) => {
|
|
||||||
if (data.menuCreate.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Created menu",
|
|
||||||
id: "menuListCreatedMenu"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
navigate(menuUrl(data.menuCreate.menu.id));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBulkDelete = (data: MenuBulkDelete) => {
|
|
||||||
if (data.menuBulkDelete.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage(commonMessages.savedChanges)
|
|
||||||
});
|
|
||||||
closeModal();
|
|
||||||
reset();
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = (data: MenuDelete) => {
|
|
||||||
if (data.menuDelete.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Deleted menu",
|
|
||||||
id: "menuListDeletedMenu"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
closeModal();
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuCreateMutation onCompleted={handleCreate}>
|
|
||||||
{(menuCreate, menuCreateOpts) => (
|
|
||||||
<MenuDeleteMutation onCompleted={handleDelete}>
|
|
||||||
{(menuDelete, menuDeleteOpts) => (
|
|
||||||
<MenuBulkDeleteMutation onCompleted={handleBulkDelete}>
|
|
||||||
{(menuBulkDelete, menuBulkDeleteOpts) => (
|
|
||||||
<>
|
|
||||||
<MenuListPage
|
|
||||||
disabled={loading}
|
|
||||||
menus={maybe(() =>
|
|
||||||
data.menus.edges.map(edge => edge.node)
|
|
||||||
)}
|
|
||||||
settings={settings}
|
|
||||||
onAdd={() =>
|
|
||||||
navigate(
|
|
||||||
menuListUrl({
|
|
||||||
action: "add"
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onBack={() => navigate(configurationMenuUrl)}
|
|
||||||
onDelete={id =>
|
|
||||||
navigate(
|
|
||||||
menuListUrl({
|
|
||||||
action: "remove",
|
|
||||||
id
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
onRowClick={id => () => navigate(menuUrl(id))}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
menuListUrl({
|
|
||||||
...params,
|
|
||||||
action: "remove-many",
|
|
||||||
ids: listElements
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FormattedMessage {...buttonMessages.remove} />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<MenuCreateDialog
|
|
||||||
open={params.action === "add"}
|
|
||||||
confirmButtonState={menuCreateOpts.status}
|
|
||||||
disabled={menuCreateOpts.loading}
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={formData =>
|
|
||||||
menuCreate({
|
|
||||||
variables: { input: formData }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
open={params.action === "remove"}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={menuDeleteOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
menuDelete({
|
|
||||||
variables: {
|
|
||||||
id: params.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="delete"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete Menu",
|
|
||||||
description: "dialog header",
|
|
||||||
id: "menuListDeleteMenuHeader"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {menuName}?"
|
|
||||||
id="menuListDeleteMenuContent"
|
|
||||||
values={{
|
|
||||||
menuName: maybe(
|
|
||||||
() =>
|
|
||||||
data.menus.edges.find(
|
|
||||||
edge => edge.node.id === params.id
|
|
||||||
).node.name,
|
|
||||||
"..."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
|
||||||
</ActionDialog>
|
|
||||||
<ActionDialog
|
|
||||||
open={
|
|
||||||
params.action === "remove-many" &&
|
|
||||||
maybe(() => params.ids.length > 0)
|
|
||||||
}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={menuBulkDeleteOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
menuBulkDelete({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="delete"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete Menus",
|
|
||||||
description: "dialog header",
|
|
||||||
id: "menuListDeleteMenusHeader"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this menu} other{{displayQuantity} menus}}?"
|
|
||||||
id="menuListDeleteMenusContent"
|
|
||||||
values={{
|
|
||||||
counter: maybe(
|
|
||||||
() => params.ids.length.toString(),
|
|
||||||
"..."
|
|
||||||
),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>
|
|
||||||
{maybe(
|
|
||||||
() => params.ids.length.toString(),
|
|
||||||
"..."
|
|
||||||
)}
|
|
||||||
</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
|
||||||
</ActionDialog>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</MenuBulkDeleteMutation>
|
|
||||||
)}
|
|
||||||
</MenuDeleteMutation>
|
|
||||||
)}
|
|
||||||
</MenuCreateMutation>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</MenuListQuery>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default MenuList;
|
|
266
src/navigation/views/MenuList/MenuList.tsx
Normal file
266
src/navigation/views/MenuList/MenuList.tsx
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
|
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 { buttonMessages, commonMessages } from "@saleor/intl";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import MenuCreateDialog from "../../components/MenuCreateDialog";
|
||||||
|
import MenuListPage from "../../components/MenuListPage";
|
||||||
|
import {
|
||||||
|
MenuBulkDeleteMutation,
|
||||||
|
MenuCreateMutation,
|
||||||
|
MenuDeleteMutation
|
||||||
|
} from "../../mutations";
|
||||||
|
import { useMenuListQuery } from "../../queries";
|
||||||
|
import { MenuBulkDelete } from "../../types/MenuBulkDelete";
|
||||||
|
import { MenuCreate } from "../../types/MenuCreate";
|
||||||
|
import { MenuDelete } from "../../types/MenuDelete";
|
||||||
|
import { menuListUrl, MenuListUrlQueryParams, menuUrl } from "../../urls";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
|
interface MenuListProps {
|
||||||
|
params: MenuListUrlQueryParams;
|
||||||
|
}
|
||||||
|
const MenuList: React.FC<MenuListProps> = ({ params }) => {
|
||||||
|
const navigate = useNavigator();
|
||||||
|
const notify = useNotifier();
|
||||||
|
const paginate = usePaginator();
|
||||||
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
|
params.ids
|
||||||
|
);
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
|
ListViews.NAVIGATION_LIST
|
||||||
|
);
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const closeModal = () =>
|
||||||
|
navigate(
|
||||||
|
menuListUrl({
|
||||||
|
...params,
|
||||||
|
action: undefined,
|
||||||
|
id: undefined,
|
||||||
|
ids: undefined
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = useMenuListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
|
maybe(() => data.menus.pageInfo),
|
||||||
|
paginationState,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCreate = (data: MenuCreate) => {
|
||||||
|
if (data.menuCreate.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Created menu",
|
||||||
|
id: "menuListCreatedMenu"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
navigate(menuUrl(data.menuCreate.menu.id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBulkDelete = (data: MenuBulkDelete) => {
|
||||||
|
if (data.menuBulkDelete.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
|
});
|
||||||
|
closeModal();
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (data: MenuDelete) => {
|
||||||
|
if (data.menuDelete.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Deleted menu",
|
||||||
|
id: "menuListDeletedMenu"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
closeModal();
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, menuListUrl, params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuCreateMutation onCompleted={handleCreate}>
|
||||||
|
{(menuCreate, menuCreateOpts) => (
|
||||||
|
<MenuDeleteMutation onCompleted={handleDelete}>
|
||||||
|
{(menuDelete, menuDeleteOpts) => (
|
||||||
|
<MenuBulkDeleteMutation onCompleted={handleBulkDelete}>
|
||||||
|
{(menuBulkDelete, menuBulkDeleteOpts) => (
|
||||||
|
<>
|
||||||
|
<MenuListPage
|
||||||
|
disabled={loading}
|
||||||
|
menus={maybe(() => data.menus.edges.map(edge => edge.node))}
|
||||||
|
settings={settings}
|
||||||
|
onAdd={() =>
|
||||||
|
navigate(
|
||||||
|
menuListUrl({
|
||||||
|
action: "add"
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onBack={() => navigate(configurationMenuUrl)}
|
||||||
|
onDelete={id =>
|
||||||
|
navigate(
|
||||||
|
menuListUrl({
|
||||||
|
action: "remove",
|
||||||
|
id
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
onRowClick={id => () => navigate(menuUrl(id))}
|
||||||
|
onSort={handleSort}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
toolbar={
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
menuListUrl({
|
||||||
|
...params,
|
||||||
|
action: "remove-many",
|
||||||
|
ids: listElements
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FormattedMessage {...buttonMessages.remove} />
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuCreateDialog
|
||||||
|
open={params.action === "add"}
|
||||||
|
confirmButtonState={menuCreateOpts.status}
|
||||||
|
disabled={menuCreateOpts.loading}
|
||||||
|
onClose={closeModal}
|
||||||
|
onConfirm={formData =>
|
||||||
|
menuCreate({
|
||||||
|
variables: { input: formData }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ActionDialog
|
||||||
|
open={params.action === "remove"}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={menuDeleteOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
menuDelete({
|
||||||
|
variables: {
|
||||||
|
id: params.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="delete"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Menu",
|
||||||
|
description: "dialog header",
|
||||||
|
id: "menuListDeleteMenuHeader"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to delete {menuName}?"
|
||||||
|
id="menuListDeleteMenuContent"
|
||||||
|
values={{
|
||||||
|
menuName: maybe(
|
||||||
|
() =>
|
||||||
|
data.menus.edges.find(
|
||||||
|
edge => edge.node.id === params.id
|
||||||
|
).node.name,
|
||||||
|
"..."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
</ActionDialog>
|
||||||
|
<ActionDialog
|
||||||
|
open={
|
||||||
|
params.action === "remove-many" &&
|
||||||
|
maybe(() => params.ids.length > 0)
|
||||||
|
}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={menuBulkDeleteOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
menuBulkDelete({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="delete"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Menus",
|
||||||
|
description: "dialog header",
|
||||||
|
id: "menuListDeleteMenusHeader"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this menu} other{{displayQuantity} menus}}?"
|
||||||
|
id="menuListDeleteMenusContent"
|
||||||
|
values={{
|
||||||
|
counter: maybe(
|
||||||
|
() => params.ids.length.toString(),
|
||||||
|
"..."
|
||||||
|
),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>
|
||||||
|
{maybe(() => params.ids.length.toString(), "...")}
|
||||||
|
</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
</ActionDialog>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</MenuBulkDeleteMutation>
|
||||||
|
)}
|
||||||
|
</MenuDeleteMutation>
|
||||||
|
)}
|
||||||
|
</MenuCreateMutation>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default MenuList;
|
2
src/navigation/views/MenuList/index.ts
Normal file
2
src/navigation/views/MenuList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./MenuList";
|
||||||
|
export * from "./MenuList";
|
18
src/navigation/views/MenuList/sort.ts
Normal file
18
src/navigation/views/MenuList/sort.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { MenuListUrlSortField } from "@saleor/navigation/urls";
|
||||||
|
import { MenuSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(sort: MenuListUrlSortField): MenuSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case MenuListUrlSortField.name:
|
||||||
|
return MenuSortField.NAME;
|
||||||
|
case MenuListUrlSortField.items:
|
||||||
|
return MenuSortField.ITEMS_COUNT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -19,7 +19,10 @@ import {
|
||||||
transformOrderStatus,
|
transformOrderStatus,
|
||||||
transformPaymentStatus
|
transformPaymentStatus
|
||||||
} from "@saleor/misc";
|
} from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { OrderDraftListUrlSortField } from "@saleor/orders/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -32,7 +35,7 @@ const useStyles = makeStyles(
|
||||||
width: 300
|
width: 300
|
||||||
},
|
},
|
||||||
colNumber: {
|
colNumber: {
|
||||||
width: 120
|
width: 160
|
||||||
},
|
},
|
||||||
colTotal: {}
|
colTotal: {}
|
||||||
},
|
},
|
||||||
|
@ -51,7 +54,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "OrderDraftList" }
|
{ name: "OrderDraftList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface OrderDraftListProps extends ListProps, ListActions {
|
interface OrderDraftListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<OrderDraftListUrlSortField> {
|
||||||
orders: OrderDraftList_draftOrders_edges_node[];
|
orders: OrderDraftList_draftOrders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +73,10 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -95,24 +103,58 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colNumber}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderDraftListUrlSortField.number
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(OrderDraftListUrlSortField.number)}
|
||||||
|
className={classes.colNumber}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="No. of Order" />
|
<FormattedMessage defaultMessage="No. of Order" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colDate}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderDraftListUrlSortField.date
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderDraftListUrlSortField.date)}
|
||||||
|
className={classes.colDate}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Date"
|
defaultMessage="Date"
|
||||||
description="order draft creation date"
|
description="order draft creation date"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colCustomer}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderDraftListUrlSortField.customer
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderDraftListUrlSortField.customer)}
|
||||||
|
className={classes.colCustomer}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Customer" />
|
<FormattedMessage defaultMessage="Customer" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colTotal}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderDraftListUrlSortField.total
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(OrderDraftListUrlSortField.total)}
|
||||||
|
className={classes.colTotal}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Total"
|
defaultMessage="Total"
|
||||||
description="order draft total price"
|
description="order draft total price"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -12,8 +12,10 @@ import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { OrderDraftListUrlSortField } from "@saleor/orders/urls";
|
||||||
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
||||||
import OrderDraftList from "../OrderDraftList";
|
import OrderDraftList from "../OrderDraftList";
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ export interface OrderDraftListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<OrderDraftListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
orders: OrderDraftList_draftOrders_edges_node[];
|
orders: OrderDraftList_draftOrders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@ import {
|
||||||
transformOrderStatus,
|
transformOrderStatus,
|
||||||
transformPaymentStatus
|
transformPaymentStatus
|
||||||
} from "@saleor/misc";
|
} from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -56,7 +59,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "OrderList" }
|
{ name: "OrderList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface OrderListProps extends ListProps, ListActions {
|
interface OrderListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<OrderListUrlSortField> {
|
||||||
orders: OrderList_orders_edges_node[];
|
orders: OrderList_orders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +78,10 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -99,36 +107,86 @@ export const OrderList: React.FC<OrderListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colNumber}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.number
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.number)}
|
||||||
|
className={classes.colNumber}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="No. of Order" />
|
<FormattedMessage defaultMessage="No. of Order" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colDate}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.date
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.date)}
|
||||||
|
className={classes.colDate}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Date"
|
defaultMessage="Date"
|
||||||
description="date when order was placed"
|
description="date when order was placed"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colCustomer}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.customer
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.customer)}
|
||||||
|
className={classes.colCustomer}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Customer"
|
defaultMessage="Customer"
|
||||||
description="e-mail or full name"
|
description="e-mail or full name"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colPayment}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.payment
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.payment)}
|
||||||
|
className={classes.colPayment}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Payment"
|
defaultMessage="Payment"
|
||||||
description="payment status"
|
description="payment status"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colFulfillment}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.fulfillment
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.fulfillment)}
|
||||||
|
className={classes.colFulfillment}
|
||||||
|
>
|
||||||
<FormattedMessage defaultMessage="Fulfillment status" />
|
<FormattedMessage defaultMessage="Fulfillment status" />
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colTotal}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === OrderListUrlSortField.total
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
textAlign="right"
|
||||||
|
onClick={() => onSort(OrderListUrlSortField.total)}
|
||||||
|
className={classes.colTotal}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Total"
|
defaultMessage="Total"
|
||||||
description="total order price"
|
description="total order price"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
|
@ -7,7 +7,13 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { FilterPageProps, ListActions, PageListProps } from "@saleor/types";
|
import {
|
||||||
|
FilterPageProps,
|
||||||
|
ListActions,
|
||||||
|
PageListProps,
|
||||||
|
SortPage
|
||||||
|
} from "@saleor/types";
|
||||||
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
||||||
import OrderList from "../OrderList";
|
import OrderList from "../OrderList";
|
||||||
import OrderListFilter, { OrderFilterKeys } from "../OrderListFilter";
|
import OrderListFilter, { OrderFilterKeys } from "../OrderListFilter";
|
||||||
|
@ -15,7 +21,8 @@ import OrderListFilter, { OrderFilterKeys } from "../OrderListFilter";
|
||||||
export interface OrderListPageProps
|
export interface OrderListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
FilterPageProps<OrderFilterKeys> {
|
FilterPageProps<OrderFilterKeys>,
|
||||||
|
SortPage<OrderListUrlSortField> {
|
||||||
orders: OrderList_orders_edges_node[];
|
orders: OrderList_orders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
orderDraftListPath,
|
orderDraftListPath,
|
||||||
|
@ -11,7 +12,9 @@ import {
|
||||||
orderListPath,
|
orderListPath,
|
||||||
OrderListUrlQueryParams,
|
OrderListUrlQueryParams,
|
||||||
orderPath,
|
orderPath,
|
||||||
OrderUrlQueryParams
|
OrderUrlQueryParams,
|
||||||
|
OrderDraftListUrlSortField,
|
||||||
|
OrderListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import OrderDetailsComponent from "./views/OrderDetails";
|
import OrderDetailsComponent from "./views/OrderDetails";
|
||||||
import OrderDraftListComponent from "./views/OrderDraftList";
|
import OrderDraftListComponent from "./views/OrderDraftList";
|
||||||
|
@ -19,12 +22,23 @@ import OrderListComponent from "./views/OrderList";
|
||||||
|
|
||||||
const OrderList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
const OrderList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: OrderListUrlQueryParams = qs;
|
const params: OrderListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
OrderListUrlSortField,
|
||||||
|
OrderListUrlSortField.number,
|
||||||
|
false
|
||||||
|
);
|
||||||
return <OrderListComponent params={params} />;
|
return <OrderListComponent params={params} />;
|
||||||
};
|
};
|
||||||
const OrderDraftList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
const OrderDraftList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: OrderDraftListUrlQueryParams = qs;
|
const params: OrderDraftListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
OrderDraftListUrlSortField,
|
||||||
|
OrderDraftListUrlSortField.number,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
return <OrderDraftListComponent params={params} />;
|
return <OrderDraftListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { TypedQuery } from "../queries";
|
import { TypedQuery } from "../queries";
|
||||||
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
|
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
|
||||||
import {
|
import {
|
||||||
|
@ -169,6 +170,7 @@ export const orderListQuery = gql`
|
||||||
$last: Int
|
$last: Int
|
||||||
$before: String
|
$before: String
|
||||||
$filter: OrderFilterInput
|
$filter: OrderFilterInput
|
||||||
|
$sort: OrderSortingInput
|
||||||
) {
|
) {
|
||||||
orders(
|
orders(
|
||||||
before: $before
|
before: $before
|
||||||
|
@ -176,6 +178,7 @@ export const orderListQuery = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -208,7 +211,7 @@ export const orderListQuery = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedOrderListQuery = TypedQuery<OrderList, OrderListVariables>(
|
export const useOrderListQuery = makeQuery<OrderList, OrderListVariables>(
|
||||||
orderListQuery
|
orderListQuery
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -220,6 +223,7 @@ export const orderDraftListQuery = gql`
|
||||||
$last: Int
|
$last: Int
|
||||||
$before: String
|
$before: String
|
||||||
$filter: OrderDraftFilterInput
|
$filter: OrderDraftFilterInput
|
||||||
|
$sort: OrderSortingInput
|
||||||
) {
|
) {
|
||||||
draftOrders(
|
draftOrders(
|
||||||
before: $before
|
before: $before
|
||||||
|
@ -227,6 +231,7 @@ export const orderDraftListQuery = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -259,7 +264,7 @@ export const orderDraftListQuery = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedOrderDraftListQuery = TypedQuery<
|
export const useOrderDraftListQuery = makeQuery<
|
||||||
OrderDraftList,
|
OrderDraftList,
|
||||||
OrderDraftListVariables
|
OrderDraftListVariables
|
||||||
>(orderDraftListQuery);
|
>(orderDraftListQuery);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { OrderDraftFilterInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
import { OrderDraftFilterInput, OrderSortingInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: OrderDraftList
|
// GraphQL query operation: OrderDraftList
|
||||||
|
@ -82,4 +82,5 @@ export interface OrderDraftListVariables {
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
filter?: OrderDraftFilterInput | null;
|
filter?: OrderDraftFilterInput | null;
|
||||||
|
sort?: OrderSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { OrderFilterInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
import { OrderFilterInput, OrderSortingInput, PaymentChargeStatusEnum, OrderStatus } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: OrderList
|
// GraphQL query operation: OrderList
|
||||||
|
@ -82,4 +82,5 @@ export interface OrderListVariables {
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
filter?: OrderFilterInput | null;
|
filter?: OrderFilterInput | null;
|
||||||
|
sort?: OrderSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ import {
|
||||||
FiltersWithMultipleValues,
|
FiltersWithMultipleValues,
|
||||||
Pagination,
|
Pagination,
|
||||||
SingleAction,
|
SingleAction,
|
||||||
TabActionDialog
|
TabActionDialog,
|
||||||
|
Sort
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
const orderSectionUrl = "/orders";
|
const orderSectionUrl = "/orders";
|
||||||
|
@ -28,9 +29,19 @@ export enum OrderListUrlFiltersWithMultipleValuesEnum {
|
||||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
||||||
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
||||||
export type OrderListUrlDialog = "cancel" | TabActionDialog;
|
export type OrderListUrlDialog = "cancel" | TabActionDialog;
|
||||||
|
export enum OrderListUrlSortField {
|
||||||
|
number = "number",
|
||||||
|
customer = "customer",
|
||||||
|
date = "date",
|
||||||
|
fulfillment = "status",
|
||||||
|
payment = "payment",
|
||||||
|
total = "total"
|
||||||
|
}
|
||||||
|
export type OrderListUrlSort = Sort<OrderListUrlSortField>;
|
||||||
export type OrderListUrlQueryParams = BulkAction &
|
export type OrderListUrlQueryParams = BulkAction &
|
||||||
Dialog<OrderListUrlDialog> &
|
Dialog<OrderListUrlDialog> &
|
||||||
OrderListUrlFilters &
|
OrderListUrlFilters &
|
||||||
|
OrderListUrlSort &
|
||||||
Pagination &
|
Pagination &
|
||||||
ActiveTab;
|
ActiveTab;
|
||||||
export const orderListUrl = (params?: OrderListUrlQueryParams): string => {
|
export const orderListUrl = (params?: OrderListUrlQueryParams): string => {
|
||||||
|
@ -48,10 +59,18 @@ export enum OrderDraftListUrlFiltersEnum {
|
||||||
}
|
}
|
||||||
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
|
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
|
||||||
export type OrderDraftListUrlDialog = "remove" | TabActionDialog;
|
export type OrderDraftListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
export enum OrderDraftListUrlSortField {
|
||||||
|
number = "number",
|
||||||
|
customer = "customer",
|
||||||
|
date = "date",
|
||||||
|
total = "total"
|
||||||
|
}
|
||||||
|
export type OrderDraftListUrlSort = Sort<OrderDraftListUrlSortField>;
|
||||||
export type OrderDraftListUrlQueryParams = ActiveTab &
|
export type OrderDraftListUrlQueryParams = ActiveTab &
|
||||||
BulkAction &
|
BulkAction &
|
||||||
Dialog<OrderDraftListUrlDialog> &
|
Dialog<OrderDraftListUrlDialog> &
|
||||||
OrderDraftListUrlFilters &
|
OrderDraftListUrlFilters &
|
||||||
|
OrderDraftListUrlSort &
|
||||||
Pagination;
|
Pagination;
|
||||||
export const orderDraftListUrl = (
|
export const orderDraftListUrl = (
|
||||||
params?: OrderDraftListUrlQueryParams
|
params?: OrderDraftListUrlQueryParams
|
||||||
|
|
|
@ -18,12 +18,14 @@ import usePaginator, {
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
import OrderDraftListPage from "../../components/OrderDraftListPage";
|
import OrderDraftListPage from "../../components/OrderDraftListPage";
|
||||||
import {
|
import {
|
||||||
TypedOrderDraftBulkCancelMutation,
|
TypedOrderDraftBulkCancelMutation,
|
||||||
useOrderDraftCreateMutation
|
useOrderDraftCreateMutation
|
||||||
} from "../../mutations";
|
} from "../../mutations";
|
||||||
import { TypedOrderDraftListQuery } from "../../queries";
|
import { useOrderDraftListQuery } from "../../queries";
|
||||||
import { OrderDraftBulkCancel } from "../../types/OrderDraftBulkCancel";
|
import { OrderDraftBulkCancel } from "../../types/OrderDraftBulkCancel";
|
||||||
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
||||||
import {
|
import {
|
||||||
|
@ -41,6 +43,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filter";
|
} from "./filter";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface OrderDraftListProps {
|
interface OrderDraftListProps {
|
||||||
params: OrderDraftListUrlQueryParams;
|
params: OrderDraftListUrlQueryParams;
|
||||||
|
@ -135,131 +138,132 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
filter: getFilterVariables(params)
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
}),
|
}),
|
||||||
[params]
|
[params]
|
||||||
);
|
);
|
||||||
|
const { data, loading, refetch } = useOrderDraftListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
|
maybe(() => data.draftOrders.pageInfo),
|
||||||
|
paginationState,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOrderDraftBulkCancel = (data: OrderDraftBulkCancel) => {
|
||||||
|
if (data.draftOrderBulkDelete.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Deleted draft orders"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
reset();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, orderDraftListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedOrderDraftListQuery displayLoader variables={queryVariables}>
|
<TypedOrderDraftBulkCancelMutation onCompleted={handleOrderDraftBulkCancel}>
|
||||||
{({ data, loading, refetch }) => {
|
{(orderDraftBulkDelete, orderDraftBulkDeleteOpts) => {
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
const onOrderDraftBulkDelete = () =>
|
||||||
maybe(() => data.draftOrders.pageInfo),
|
orderDraftBulkDelete({
|
||||||
paginationState,
|
variables: {
|
||||||
params
|
ids: params.ids
|
||||||
);
|
}
|
||||||
|
});
|
||||||
const handleOrderDraftBulkCancel = (data: OrderDraftBulkCancel) => {
|
|
||||||
if (data.draftOrderBulkDelete.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Deleted draft orders"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
reset();
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedOrderDraftBulkCancelMutation
|
<>
|
||||||
onCompleted={handleOrderDraftBulkCancel}
|
<OrderDraftListPage
|
||||||
>
|
currentTab={currentTab}
|
||||||
{(orderDraftBulkDelete, orderDraftBulkDeleteOpts) => {
|
initialSearch={params.query || ""}
|
||||||
const onOrderDraftBulkDelete = () =>
|
onSearchChange={query => changeFilterField({ query })}
|
||||||
orderDraftBulkDelete({
|
onAll={() => navigate(orderDraftListUrl())}
|
||||||
variables: {
|
onTabChange={handleTabChange}
|
||||||
ids: params.ids
|
onTabDelete={() => openModal("delete-search")}
|
||||||
|
onTabSave={() => openModal("save-search")}
|
||||||
|
tabs={tabs.map(tab => tab.name)}
|
||||||
|
disabled={loading}
|
||||||
|
settings={settings}
|
||||||
|
orders={maybe(() =>
|
||||||
|
data.draftOrders.edges.map(edge => edge.node)
|
||||||
|
)}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
onAdd={createOrder}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onRowClick={id => () => navigate(orderUrl(id))}
|
||||||
|
onSort={handleSort}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
toolbar={
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
orderDraftListUrl({
|
||||||
|
action: "remove",
|
||||||
|
ids: listElements
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
>
|
||||||
|
<DeleteIcon />
|
||||||
return (
|
</IconButton>
|
||||||
<>
|
}
|
||||||
<OrderDraftListPage
|
/>
|
||||||
currentTab={currentTab}
|
<ActionDialog
|
||||||
initialSearch={params.query || ""}
|
confirmButtonState={orderDraftBulkDeleteOpts.status}
|
||||||
onSearchChange={query => changeFilterField({ query })}
|
onClose={closeModal}
|
||||||
onAll={() => navigate(orderDraftListUrl())}
|
onConfirm={onOrderDraftBulkDelete}
|
||||||
onTabChange={handleTabChange}
|
open={params.action === "remove"}
|
||||||
onTabDelete={() => openModal("delete-search")}
|
title={intl.formatMessage({
|
||||||
onTabSave={() => openModal("save-search")}
|
defaultMessage: "Delete Order Drafts",
|
||||||
tabs={tabs.map(tab => tab.name)}
|
description: "dialog header"
|
||||||
disabled={loading}
|
})}
|
||||||
settings={settings}
|
variant="delete"
|
||||||
orders={maybe(() =>
|
>
|
||||||
data.draftOrders.edges.map(edge => edge.node)
|
<DialogContentText>
|
||||||
)}
|
<FormattedMessage
|
||||||
pageInfo={pageInfo}
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this order draft} other{{displayQuantity} orderDrafts}}?"
|
||||||
onAdd={createOrder}
|
description="dialog content"
|
||||||
onNextPage={loadNextPage}
|
values={{
|
||||||
onPreviousPage={loadPreviousPage}
|
counter: maybe(() => params.ids.length),
|
||||||
onUpdateListSettings={updateListSettings}
|
displayQuantity: (
|
||||||
onRowClick={id => () => navigate(orderUrl(id))}
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
isChecked={isSelected}
|
)
|
||||||
selected={listElements.length}
|
}}
|
||||||
toggle={toggle}
|
/>
|
||||||
toggleAll={toggleAll}
|
</DialogContentText>
|
||||||
toolbar={
|
</ActionDialog>
|
||||||
<IconButton
|
<SaveFilterTabDialog
|
||||||
color="primary"
|
open={params.action === "save-search"}
|
||||||
onClick={() =>
|
confirmButtonState="default"
|
||||||
navigate(
|
onClose={closeModal}
|
||||||
orderDraftListUrl({
|
onSubmit={handleTabSave}
|
||||||
action: "remove",
|
/>
|
||||||
ids: listElements
|
<DeleteFilterTabDialog
|
||||||
})
|
open={params.action === "delete-search"}
|
||||||
)
|
confirmButtonState="default"
|
||||||
}
|
onClose={closeModal}
|
||||||
>
|
onSubmit={handleTabDelete}
|
||||||
<DeleteIcon />
|
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||||
</IconButton>
|
/>
|
||||||
}
|
</>
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
confirmButtonState={orderDraftBulkDeleteOpts.status}
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={onOrderDraftBulkDelete}
|
|
||||||
open={params.action === "remove"}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete Order Drafts",
|
|
||||||
description: "dialog header"
|
|
||||||
})}
|
|
||||||
variant="delete"
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this order draft} other{{displayQuantity} orderDrafts}}?"
|
|
||||||
description="dialog content"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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, "...")}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</TypedOrderDraftBulkCancelMutation>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</TypedOrderDraftListQuery>
|
</TypedOrderDraftBulkCancelMutation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
24
src/orders/views/OrderDraftList/sort.ts
Normal file
24
src/orders/views/OrderDraftList/sort.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { OrderDraftListUrlSortField } from "@saleor/orders/urls";
|
||||||
|
import { OrderSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: OrderDraftListUrlSortField
|
||||||
|
): OrderSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case OrderDraftListUrlSortField.number:
|
||||||
|
return OrderSortField.NUMBER;
|
||||||
|
case OrderDraftListUrlSortField.date:
|
||||||
|
return OrderSortField.CREATION_DATE;
|
||||||
|
case OrderDraftListUrlSortField.customer:
|
||||||
|
return OrderSortField.CUSTOMER;
|
||||||
|
case OrderDraftListUrlSortField.total:
|
||||||
|
return OrderSortField.TOTAL;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -17,13 +17,15 @@ import usePaginator, {
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog";
|
import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog";
|
||||||
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
||||||
import {
|
import {
|
||||||
TypedOrderBulkCancelMutation,
|
TypedOrderBulkCancelMutation,
|
||||||
useOrderDraftCreateMutation
|
useOrderDraftCreateMutation
|
||||||
} from "../../mutations";
|
} from "../../mutations";
|
||||||
import { TypedOrderListQuery } from "../../queries";
|
import { useOrderListQuery } from "../../queries";
|
||||||
import { OrderBulkCancel } from "../../types/OrderBulkCancel";
|
import { OrderBulkCancel } from "../../types/OrderBulkCancel";
|
||||||
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
|
||||||
import {
|
import {
|
||||||
|
@ -43,6 +45,7 @@ import {
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filters";
|
} from "./filters";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface OrderListProps {
|
interface OrderListProps {
|
||||||
params: OrderListUrlQueryParams;
|
params: OrderListUrlQueryParams;
|
||||||
|
@ -146,128 +149,126 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
filter: getFilterVariables(params)
|
filter: getFilterVariables(params),
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
}),
|
}),
|
||||||
[params, settings.rowNumber]
|
[params, settings.rowNumber]
|
||||||
);
|
);
|
||||||
|
const { data, loading, refetch } = useOrderListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
|
maybe(() => data.orders.pageInfo),
|
||||||
|
paginationState,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOrderBulkCancel = (data: OrderBulkCancel) => {
|
||||||
|
if (data.orderBulkCancel.errors.length === 0) {
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Orders cancelled"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, orderListUrl, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedOrderListQuery displayLoader variables={queryVariables}>
|
<TypedOrderBulkCancelMutation onCompleted={handleOrderBulkCancel}>
|
||||||
{({ data, loading, refetch }) => {
|
{(orderBulkCancel, orderBulkCancelOpts) => {
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
const onOrderBulkCancel = (restock: boolean) =>
|
||||||
maybe(() => data.orders.pageInfo),
|
orderBulkCancel({
|
||||||
paginationState,
|
variables: {
|
||||||
params
|
ids: params.ids,
|
||||||
);
|
restock
|
||||||
|
}
|
||||||
const handleOrderBulkCancel = (data: OrderBulkCancel) => {
|
});
|
||||||
if (data.orderBulkCancel.errors.length === 0) {
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Orders cancelled"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
reset();
|
|
||||||
refetch();
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedOrderBulkCancelMutation onCompleted={handleOrderBulkCancel}>
|
<>
|
||||||
{(orderBulkCancel, orderBulkCancelOpts) => {
|
<OrderListPage
|
||||||
const onOrderBulkCancel = (restock: boolean) =>
|
currencySymbol={currencySymbol}
|
||||||
orderBulkCancel({
|
settings={settings}
|
||||||
variables: {
|
filtersList={createFilterChips(
|
||||||
ids: params.ids,
|
params,
|
||||||
restock
|
{
|
||||||
}
|
formatDate
|
||||||
});
|
},
|
||||||
|
changeFilterField,
|
||||||
return (
|
intl
|
||||||
<>
|
)}
|
||||||
<OrderListPage
|
currentTab={currentTab}
|
||||||
currencySymbol={currencySymbol}
|
disabled={loading}
|
||||||
settings={settings}
|
orders={maybe(() => data.orders.edges.map(edge => edge.node))}
|
||||||
filtersList={createFilterChips(
|
pageInfo={pageInfo}
|
||||||
params,
|
sort={getSortParams(params)}
|
||||||
{
|
onAdd={createOrder}
|
||||||
formatDate
|
onNextPage={loadNextPage}
|
||||||
},
|
onPreviousPage={loadPreviousPage}
|
||||||
changeFilterField,
|
onUpdateListSettings={updateListSettings}
|
||||||
intl
|
onRowClick={id => () => navigate(orderUrl(id))}
|
||||||
)}
|
onSort={handleSort}
|
||||||
currentTab={currentTab}
|
isChecked={isSelected}
|
||||||
disabled={loading}
|
selected={listElements.length}
|
||||||
orders={maybe(() =>
|
toggle={toggle}
|
||||||
data.orders.edges.map(edge => edge.node)
|
toggleAll={toggleAll}
|
||||||
)}
|
toolbar={
|
||||||
pageInfo={pageInfo}
|
<Button
|
||||||
onAdd={createOrder}
|
color="primary"
|
||||||
onNextPage={loadNextPage}
|
onClick={() => openModal("cancel", listElements)}
|
||||||
onPreviousPage={loadPreviousPage}
|
>
|
||||||
onUpdateListSettings={updateListSettings}
|
<FormattedMessage
|
||||||
onRowClick={id => () => navigate(orderUrl(id))}
|
defaultMessage="Cancel"
|
||||||
isChecked={isSelected}
|
description="cancel orders, button"
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("cancel", listElements)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Cancel"
|
|
||||||
description="cancel orders, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
onSearchChange={query => changeFilterField({ query })}
|
|
||||||
onFilterAdd={data =>
|
|
||||||
changeFilterField(createFilter(params, data))
|
|
||||||
}
|
|
||||||
onTabSave={() => openModal("save-search")}
|
|
||||||
onTabDelete={() => openModal("delete-search")}
|
|
||||||
onTabChange={handleTabChange}
|
|
||||||
initialSearch={params.query || ""}
|
|
||||||
tabs={getFilterTabs().map(tab => tab.name)}
|
|
||||||
onAll={() =>
|
|
||||||
changeFilters({
|
|
||||||
status: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<OrderBulkCancelDialog
|
</Button>
|
||||||
confirmButtonState={orderBulkCancelOpts.status}
|
}
|
||||||
numberOfOrders={maybe(
|
onSearchChange={query => changeFilterField({ query })}
|
||||||
() => params.ids.length.toString(),
|
onFilterAdd={data =>
|
||||||
"..."
|
changeFilterField(createFilter(params, data))
|
||||||
)}
|
}
|
||||||
onClose={closeModal}
|
onTabSave={() => openModal("save-search")}
|
||||||
onConfirm={onOrderBulkCancel}
|
onTabDelete={() => openModal("delete-search")}
|
||||||
open={params.action === "cancel"}
|
onTabChange={handleTabChange}
|
||||||
/>
|
initialSearch={params.query || ""}
|
||||||
<SaveFilterTabDialog
|
tabs={getFilterTabs().map(tab => tab.name)}
|
||||||
open={params.action === "save-search"}
|
onAll={() =>
|
||||||
confirmButtonState="default"
|
changeFilters({
|
||||||
onClose={closeModal}
|
status: undefined
|
||||||
onSubmit={handleFilterTabSave}
|
})
|
||||||
/>
|
}
|
||||||
<DeleteFilterTabDialog
|
/>
|
||||||
open={params.action === "delete-search"}
|
<OrderBulkCancelDialog
|
||||||
confirmButtonState="default"
|
confirmButtonState={orderBulkCancelOpts.status}
|
||||||
onClose={closeModal}
|
numberOfOrders={maybe(() => params.ids.length.toString(), "...")}
|
||||||
onSubmit={handleFilterTabDelete}
|
onClose={closeModal}
|
||||||
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
onConfirm={onOrderBulkCancel}
|
||||||
/>
|
open={params.action === "cancel"}
|
||||||
</>
|
/>
|
||||||
);
|
<SaveFilterTabDialog
|
||||||
}}
|
open={params.action === "save-search"}
|
||||||
</TypedOrderBulkCancelMutation>
|
confirmButtonState="default"
|
||||||
|
onClose={closeModal}
|
||||||
|
onSubmit={handleFilterTabSave}
|
||||||
|
/>
|
||||||
|
<DeleteFilterTabDialog
|
||||||
|
open={params.action === "delete-search"}
|
||||||
|
confirmButtonState="default"
|
||||||
|
onClose={closeModal}
|
||||||
|
onSubmit={handleFilterTabDelete}
|
||||||
|
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</TypedOrderListQuery>
|
</TypedOrderBulkCancelMutation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
26
src/orders/views/OrderList/sort.ts
Normal file
26
src/orders/views/OrderList/sort.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
|
import { OrderSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(sort: OrderListUrlSortField): OrderSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case OrderListUrlSortField.number:
|
||||||
|
return OrderSortField.NUMBER;
|
||||||
|
case OrderListUrlSortField.date:
|
||||||
|
return OrderSortField.CREATION_DATE;
|
||||||
|
case OrderListUrlSortField.customer:
|
||||||
|
return OrderSortField.CUSTOMER;
|
||||||
|
case OrderListUrlSortField.total:
|
||||||
|
return OrderSortField.TOTAL;
|
||||||
|
case OrderListUrlSortField.fulfillment:
|
||||||
|
return OrderSortField.FULFILLMENT_STATUS;
|
||||||
|
case OrderListUrlSortField.payment:
|
||||||
|
return OrderSortField.PAYMENT;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -14,10 +14,16 @@ import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { PageListUrlSortField } from "@saleor/pages/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { PageList_pages_edges_node } from "../../types/PageList";
|
import { PageList_pages_edges_node } from "../../types/PageList";
|
||||||
|
|
||||||
export interface PageListProps extends ListProps, ListActions {
|
export interface PageListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<PageListUrlSortField> {
|
||||||
pages: PageList_pages_edges_node[];
|
pages: PageList_pages_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +60,12 @@ const PageList: React.FC<PageListProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -77,24 +85,51 @@ const PageList: React.FC<PageListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colTitle}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === PageListUrlSortField.title
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(PageListUrlSortField.title)}
|
||||||
|
className={classes.colTitle}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Title"
|
defaultMessage="Title"
|
||||||
description="dialog header"
|
description="dialog header"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colSlug}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === PageListUrlSortField.slug
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(PageListUrlSortField.slug)}
|
||||||
|
className={classes.colSlug}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Slug"
|
defaultMessage="Slug"
|
||||||
description="page internal name"
|
description="page internal name"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colVisibility}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === PageListUrlSortField.visible
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(PageListUrlSortField.visible)}
|
||||||
|
className={classes.colVisibility}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Visibility"
|
defaultMessage="Visibility"
|
||||||
description="page status"
|
description="page status"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -133,13 +168,13 @@ const PageList: React.FC<PageListProps> = props => {
|
||||||
onChange={() => toggle(page.id)}
|
onChange={() => toggle(page.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colTitle}>
|
<TableCellHeader className={classes.colTitle}>
|
||||||
{maybe<React.ReactNode>(() => page.title, <Skeleton />)}
|
{maybe<React.ReactNode>(() => page.title, <Skeleton />)}
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colSlug}>
|
<TableCellHeader className={classes.colSlug}>
|
||||||
{maybe<React.ReactNode>(() => page.slug, <Skeleton />)}
|
{maybe<React.ReactNode>(() => page.slug, <Skeleton />)}
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colVisibility}>
|
<TableCellHeader className={classes.colVisibility}>
|
||||||
{maybe<React.ReactNode>(
|
{maybe<React.ReactNode>(
|
||||||
() => (
|
() => (
|
||||||
<StatusLabel
|
<StatusLabel
|
||||||
|
@ -159,7 +194,7 @@ const PageList: React.FC<PageListProps> = props => {
|
||||||
),
|
),
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,31 +7,23 @@ import AppHeader from "@saleor/components/AppHeader";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { ListActions, PageListProps } from "@saleor/types";
|
import { ListActions, PageListProps, SortPage } from "@saleor/types";
|
||||||
|
import { PageListUrlSortField } from "@saleor/pages/urls";
|
||||||
import { PageList_pages_edges_node } from "../../types/PageList";
|
import { PageList_pages_edges_node } from "../../types/PageList";
|
||||||
import PageList from "../PageList/PageList";
|
import PageList from "../PageList";
|
||||||
|
|
||||||
export interface PageListPageProps extends PageListProps, ListActions {
|
export interface PageListPageProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<PageListUrlSortField> {
|
||||||
pages: PageList_pages_edges_node[];
|
pages: PageList_pages_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageListPage: React.FC<PageListPageProps> = ({
|
const PageListPage: React.FC<PageListPageProps> = ({
|
||||||
disabled,
|
|
||||||
settings,
|
|
||||||
onAdd,
|
onAdd,
|
||||||
onBack,
|
onBack,
|
||||||
onNextPage,
|
...listProps
|
||||||
onPreviousPage,
|
|
||||||
onRowClick,
|
|
||||||
onUpdateListSettings,
|
|
||||||
pageInfo,
|
|
||||||
pages,
|
|
||||||
isChecked,
|
|
||||||
selected,
|
|
||||||
toggle,
|
|
||||||
toggleAll,
|
|
||||||
toolbar
|
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -41,30 +33,11 @@ const PageListPage: React.FC<PageListPageProps> = ({
|
||||||
{intl.formatMessage(sectionNames.configuration)}
|
{intl.formatMessage(sectionNames.configuration)}
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.pages)}>
|
<PageHeader title={intl.formatMessage(sectionNames.pages)}>
|
||||||
<Button
|
<Button onClick={onAdd} variant="contained" color="primary">
|
||||||
disabled={disabled}
|
|
||||||
onClick={onAdd}
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
<FormattedMessage defaultMessage="Create page" description="button" />
|
<FormattedMessage defaultMessage="Create page" description="button" />
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<PageList
|
<PageList {...listProps} />
|
||||||
disabled={disabled}
|
|
||||||
settings={settings}
|
|
||||||
pages={pages}
|
|
||||||
onNextPage={onNextPage}
|
|
||||||
onPreviousPage={onPreviousPage}
|
|
||||||
onUpdateListSettings={onUpdateListSettings}
|
|
||||||
onRowClick={onRowClick}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
isChecked={isChecked}
|
|
||||||
selected={selected}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
toolbar={toolbar}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,15 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
pageCreatePath,
|
pageCreatePath,
|
||||||
pageListPath,
|
pageListPath,
|
||||||
PageListUrlQueryParams,
|
PageListUrlQueryParams,
|
||||||
pagePath,
|
pagePath,
|
||||||
PageUrlQueryParams
|
PageUrlQueryParams,
|
||||||
|
PageListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import PageCreate from "./views/PageCreate";
|
import PageCreate from "./views/PageCreate";
|
||||||
import PageDetailsComponent from "./views/PageDetails";
|
import PageDetailsComponent from "./views/PageDetails";
|
||||||
|
@ -18,7 +20,11 @@ import PageListComponent from "./views/PageList";
|
||||||
|
|
||||||
const PageList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
const PageList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: PageListUrlQueryParams = qs;
|
const params: PageListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
PageListUrlSortField,
|
||||||
|
PageListUrlSortField.title
|
||||||
|
);
|
||||||
return <PageListComponent params={params} />;
|
return <PageListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { TypedQuery } from "../queries";
|
import { TypedQuery } from "../queries";
|
||||||
import { PageDetails, PageDetailsVariables } from "./types/PageDetails";
|
import { PageDetails, PageDetailsVariables } from "./types/PageDetails";
|
||||||
import { PageList, PageListVariables } from "./types/PageList";
|
import { PageList, PageListVariables } from "./types/PageList";
|
||||||
|
@ -26,8 +27,20 @@ export const pageDetailsFragment = gql`
|
||||||
|
|
||||||
const pageList = gql`
|
const pageList = gql`
|
||||||
${pageFragment}
|
${pageFragment}
|
||||||
query PageList($first: Int, $after: String, $last: Int, $before: String) {
|
query PageList(
|
||||||
pages(before: $before, after: $after, first: $first, last: $last) {
|
$first: Int
|
||||||
|
$after: String
|
||||||
|
$last: Int
|
||||||
|
$before: String
|
||||||
|
$sort: PageSortingInput
|
||||||
|
) {
|
||||||
|
pages(
|
||||||
|
before: $before
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
last: $last
|
||||||
|
sortBy: $sort
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...PageFragment
|
...PageFragment
|
||||||
|
@ -42,7 +55,7 @@ const pageList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedPageListQuery = TypedQuery<PageList, PageListVariables>(
|
export const usePageListQuery = makeQuery<PageList, PageListVariables>(
|
||||||
pageList
|
pageList
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { PageSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: PageList
|
// GraphQL query operation: PageList
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
@ -42,4 +44,5 @@ export interface PageListVariables {
|
||||||
after?: string | null;
|
after?: string | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
|
sort?: PageSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
import { stringify as stringifyQs } from "qs";
|
import { stringify as stringifyQs } from "qs";
|
||||||
import urlJoin from "url-join";
|
import urlJoin from "url-join";
|
||||||
|
|
||||||
import { BulkAction, Dialog, Pagination } from "../types";
|
import { BulkAction, Dialog, Pagination, Sort } from "../types";
|
||||||
|
|
||||||
export const pagesSection = "/pages/";
|
export const pagesSection = "/pages/";
|
||||||
|
|
||||||
export const pageListPath = pagesSection;
|
export const pageListPath = pagesSection;
|
||||||
export type PageListUrlDialog = "publish" | "unpublish" | "remove";
|
export type PageListUrlDialog = "publish" | "unpublish" | "remove";
|
||||||
|
export enum PageListUrlSortField {
|
||||||
|
title = "title",
|
||||||
|
slug = "slug",
|
||||||
|
visible = "visible"
|
||||||
|
}
|
||||||
|
export type PageListUrlSort = Sort<PageListUrlSortField>;
|
||||||
export type PageListUrlQueryParams = BulkAction &
|
export type PageListUrlQueryParams = BulkAction &
|
||||||
Dialog<PageListUrlDialog> &
|
Dialog<PageListUrlDialog> &
|
||||||
|
PageListUrlSort &
|
||||||
Pagination;
|
Pagination;
|
||||||
export const pageListUrl = (params?: PageListUrlQueryParams) =>
|
export const pageListUrl = (params?: PageListUrlQueryParams) =>
|
||||||
pageListPath + "?" + stringifyQs(params);
|
pageListPath + "?" + stringifyQs(params);
|
||||||
|
|
|
@ -1,255 +0,0 @@
|
||||||
import Button from "@material-ui/core/Button";
|
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
|
||||||
import React from "react";
|
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
|
||||||
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 { maybe } from "@saleor/misc";
|
|
||||||
import { ListViews } from "@saleor/types";
|
|
||||||
import PageListPage from "../components/PageListPage/PageListPage";
|
|
||||||
import { TypedPageBulkPublish, TypedPageBulkRemove } from "../mutations";
|
|
||||||
import { TypedPageListQuery } from "../queries";
|
|
||||||
import { PageBulkPublish } from "../types/PageBulkPublish";
|
|
||||||
import { PageBulkRemove } from "../types/PageBulkRemove";
|
|
||||||
import {
|
|
||||||
pageCreateUrl,
|
|
||||||
pageListUrl,
|
|
||||||
PageListUrlDialog,
|
|
||||||
PageListUrlQueryParams,
|
|
||||||
pageUrl
|
|
||||||
} from "../urls";
|
|
||||||
|
|
||||||
interface PageListProps {
|
|
||||||
params: PageListUrlQueryParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|
||||||
const navigate = useNavigator();
|
|
||||||
const notify = useNotifier();
|
|
||||||
const paginate = usePaginator();
|
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
|
||||||
params.ids
|
|
||||||
);
|
|
||||||
const { updateListSettings, settings } = useListSettings(
|
|
||||||
ListViews.PAGES_LIST
|
|
||||||
);
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TypedPageListQuery displayLoader variables={paginationState}>
|
|
||||||
{({ data, loading, refetch }) => {
|
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
|
||||||
maybe(() => data.pages.pageInfo),
|
|
||||||
paginationState,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const closeModal = () =>
|
|
||||||
navigate(
|
|
||||||
pageListUrl({
|
|
||||||
...params,
|
|
||||||
action: undefined,
|
|
||||||
ids: undefined
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const openModal = (action: PageListUrlDialog, ids: string[]) =>
|
|
||||||
navigate(
|
|
||||||
pageListUrl({
|
|
||||||
...params,
|
|
||||||
action,
|
|
||||||
ids
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePageBulkPublish = (data: PageBulkPublish) => {
|
|
||||||
if (data.pageBulkPublish.errors.length === 0) {
|
|
||||||
closeModal();
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Published pages",
|
|
||||||
description: "notification"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
reset();
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePageBulkRemove = (data: PageBulkRemove) => {
|
|
||||||
if (data.pageBulkDelete.errors.length === 0) {
|
|
||||||
closeModal();
|
|
||||||
notify({
|
|
||||||
text: intl.formatMessage({
|
|
||||||
defaultMessage: "Removed pages",
|
|
||||||
description: "notification"
|
|
||||||
})
|
|
||||||
});
|
|
||||||
reset();
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TypedPageBulkRemove onCompleted={handlePageBulkRemove}>
|
|
||||||
{(bulkPageRemove, bulkPageRemoveOpts) => (
|
|
||||||
<TypedPageBulkPublish onCompleted={handlePageBulkPublish}>
|
|
||||||
{(bulkPagePublish, bulkPagePublishOpts) => (
|
|
||||||
<>
|
|
||||||
<PageListPage
|
|
||||||
disabled={loading}
|
|
||||||
settings={settings}
|
|
||||||
pages={maybe(() =>
|
|
||||||
data.pages.edges.map(edge => edge.node)
|
|
||||||
)}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
onAdd={() => navigate(pageCreateUrl)}
|
|
||||||
onBack={() => navigate(configurationMenuUrl)}
|
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
onRowClick={id => () => navigate(pageUrl(id))}
|
|
||||||
toolbar={
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("unpublish", listElements)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Unpublish"
|
|
||||||
description="unpublish page, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("publish", listElements)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Publish"
|
|
||||||
description="publish page, button"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() => openModal("remove", listElements)}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
isChecked={isSelected}
|
|
||||||
selected={listElements.length}
|
|
||||||
toggle={toggle}
|
|
||||||
toggleAll={toggleAll}
|
|
||||||
/>
|
|
||||||
<ActionDialog
|
|
||||||
open={params.action === "publish"}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={bulkPagePublishOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
bulkPagePublish({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids,
|
|
||||||
isPublished: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Publish Pages",
|
|
||||||
description: "dialog header"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to publish {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
|
||||||
description="dialog content"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContentText>
|
|
||||||
</ActionDialog>
|
|
||||||
<ActionDialog
|
|
||||||
open={params.action === "unpublish"}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={bulkPagePublishOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
bulkPagePublish({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids,
|
|
||||||
isPublished: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Unpublish Pages",
|
|
||||||
description: "dialog header"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to unpublish {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
|
||||||
description="dialog content"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ActionDialog>
|
|
||||||
<ActionDialog
|
|
||||||
open={params.action === "remove"}
|
|
||||||
onClose={closeModal}
|
|
||||||
confirmButtonState={bulkPageRemoveOpts.status}
|
|
||||||
onConfirm={() =>
|
|
||||||
bulkPageRemove({
|
|
||||||
variables: {
|
|
||||||
ids: params.ids
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
variant="delete"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Delete Pages",
|
|
||||||
description: "dialog header"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
|
||||||
description="dialog content"
|
|
||||||
values={{
|
|
||||||
counter: maybe(() => params.ids.length),
|
|
||||||
displayQuantity: (
|
|
||||||
<strong>{maybe(() => params.ids.length)}</strong>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ActionDialog>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</TypedPageBulkPublish>
|
|
||||||
)}
|
|
||||||
</TypedPageBulkRemove>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</TypedPageListQuery>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PageList;
|
|
265
src/pages/views/PageList/PageList.tsx
Normal file
265
src/pages/views/PageList/PageList.tsx
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
|
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 { maybe } from "@saleor/misc";
|
||||||
|
import { ListViews } from "@saleor/types";
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import PageListPage from "../../components/PageListPage/PageListPage";
|
||||||
|
import { TypedPageBulkPublish, TypedPageBulkRemove } from "../../mutations";
|
||||||
|
import { usePageListQuery } from "../../queries";
|
||||||
|
import { PageBulkPublish } from "../../types/PageBulkPublish";
|
||||||
|
import { PageBulkRemove } from "../../types/PageBulkRemove";
|
||||||
|
import {
|
||||||
|
pageCreateUrl,
|
||||||
|
pageListUrl,
|
||||||
|
PageListUrlDialog,
|
||||||
|
PageListUrlQueryParams,
|
||||||
|
pageUrl
|
||||||
|
} from "../../urls";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
|
interface PageListProps {
|
||||||
|
params: PageListUrlQueryParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PageList: React.FC<PageListProps> = ({ params }) => {
|
||||||
|
const navigate = useNavigator();
|
||||||
|
const notify = useNotifier();
|
||||||
|
const paginate = usePaginator();
|
||||||
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
|
params.ids
|
||||||
|
);
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
|
ListViews.PAGES_LIST
|
||||||
|
);
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading, refetch } = usePageListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
|
maybe(() => data.pages.pageInfo),
|
||||||
|
paginationState,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const closeModal = () =>
|
||||||
|
navigate(
|
||||||
|
pageListUrl({
|
||||||
|
...params,
|
||||||
|
action: undefined,
|
||||||
|
ids: undefined
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const openModal = (action: PageListUrlDialog, ids: string[]) =>
|
||||||
|
navigate(
|
||||||
|
pageListUrl({
|
||||||
|
...params,
|
||||||
|
action,
|
||||||
|
ids
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePageBulkPublish = (data: PageBulkPublish) => {
|
||||||
|
if (data.pageBulkPublish.errors.length === 0) {
|
||||||
|
closeModal();
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Published pages",
|
||||||
|
description: "notification"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageBulkRemove = (data: PageBulkRemove) => {
|
||||||
|
if (data.pageBulkDelete.errors.length === 0) {
|
||||||
|
closeModal();
|
||||||
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Removed pages",
|
||||||
|
description: "notification"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, pageListUrl, params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TypedPageBulkRemove onCompleted={handlePageBulkRemove}>
|
||||||
|
{(bulkPageRemove, bulkPageRemoveOpts) => (
|
||||||
|
<TypedPageBulkPublish onCompleted={handlePageBulkPublish}>
|
||||||
|
{(bulkPagePublish, bulkPagePublishOpts) => (
|
||||||
|
<>
|
||||||
|
<PageListPage
|
||||||
|
disabled={loading}
|
||||||
|
settings={settings}
|
||||||
|
pages={maybe(() => data.pages.edges.map(edge => edge.node))}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
onAdd={() => navigate(pageCreateUrl)}
|
||||||
|
onBack={() => navigate(configurationMenuUrl)}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
onRowClick={id => () => navigate(pageUrl(id))}
|
||||||
|
onSort={handleSort}
|
||||||
|
toolbar={
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("unpublish", listElements)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Unpublish"
|
||||||
|
description="unpublish page, button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("publish", listElements)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Publish"
|
||||||
|
description="publish page, button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openModal("remove", listElements)}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
isChecked={isSelected}
|
||||||
|
selected={listElements.length}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
toggle={toggle}
|
||||||
|
toggleAll={toggleAll}
|
||||||
|
/>
|
||||||
|
<ActionDialog
|
||||||
|
open={params.action === "publish"}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={bulkPagePublishOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
bulkPagePublish({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids,
|
||||||
|
isPublished: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Publish Pages",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to publish {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
||||||
|
description="dialog content"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContentText>
|
||||||
|
</ActionDialog>
|
||||||
|
<ActionDialog
|
||||||
|
open={params.action === "unpublish"}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={bulkPagePublishOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
bulkPagePublish({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids,
|
||||||
|
isPublished: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Unpublish Pages",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to unpublish {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
||||||
|
description="dialog content"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ActionDialog>
|
||||||
|
<ActionDialog
|
||||||
|
open={params.action === "remove"}
|
||||||
|
onClose={closeModal}
|
||||||
|
confirmButtonState={bulkPageRemoveOpts.status}
|
||||||
|
onConfirm={() =>
|
||||||
|
bulkPageRemove({
|
||||||
|
variables: {
|
||||||
|
ids: params.ids
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="delete"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Pages",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Are you sure you want to delete {counter,plural,one{this page} other{{displayQuantity} pages}}?"
|
||||||
|
description="dialog content"
|
||||||
|
values={{
|
||||||
|
counter: maybe(() => params.ids.length),
|
||||||
|
displayQuantity: (
|
||||||
|
<strong>{maybe(() => params.ids.length)}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ActionDialog>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TypedPageBulkPublish>
|
||||||
|
)}
|
||||||
|
</TypedPageBulkRemove>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageList;
|
2
src/pages/views/PageList/index.ts
Normal file
2
src/pages/views/PageList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./PageList";
|
||||||
|
export * from "./PageList";
|
20
src/pages/views/PageList/sort.ts
Normal file
20
src/pages/views/PageList/sort.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { PageListUrlSortField } from "@saleor/pages/urls";
|
||||||
|
import { PageSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(sort: PageListUrlSortField): PageSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case PageListUrlSortField.title:
|
||||||
|
return PageSortField.TITLE;
|
||||||
|
case PageListUrlSortField.visible:
|
||||||
|
return PageSortField.VISIBILITY;
|
||||||
|
case PageListUrlSortField.slug:
|
||||||
|
return PageSortField.SLUG;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -15,10 +15,15 @@ import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { translateBoolean } from "@saleor/intl";
|
import { translateBoolean } from "@saleor/intl";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListProps } from "@saleor/types";
|
import { ListProps, SortPage } from "@saleor/types";
|
||||||
|
import { PluginListUrlSortField } from "@saleor/plugins/urls";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
||||||
|
|
||||||
export interface PluginListProps extends ListProps {
|
export interface PluginListProps
|
||||||
|
extends ListProps,
|
||||||
|
SortPage<PluginListUrlSortField> {
|
||||||
plugins: Plugins_plugins_edges_node[];
|
plugins: Plugins_plugins_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +58,9 @@ const PluginList: React.FC<PluginListProps> = props => {
|
||||||
disabled,
|
disabled,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
|
sort,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onPreviousPage
|
onPreviousPage
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -64,18 +71,35 @@ const PluginList: React.FC<PluginListProps> = props => {
|
||||||
<Card>
|
<Card>
|
||||||
<ResponsiveTable>
|
<ResponsiveTable>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === PluginListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(PluginListUrlSortField.name)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
defaultMessage: "Name",
|
defaultMessage: "Name",
|
||||||
description: "plugin name"
|
description: "plugin name"
|
||||||
})}
|
})}
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colActive}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === PluginListUrlSortField.active
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(PluginListUrlSortField.active)}
|
||||||
|
className={classes.colActive}
|
||||||
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
defaultMessage: "Active",
|
defaultMessage: "Active",
|
||||||
description: "plugin status"
|
description: "plugin status"
|
||||||
})}
|
})}
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colAction}>
|
<TableCell className={classes.colAction}>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
defaultMessage: "Action",
|
defaultMessage: "Action",
|
||||||
|
|
|
@ -5,43 +5,31 @@ import AppHeader from "@saleor/components/AppHeader";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { PageListProps } from "@saleor/types";
|
import { PageListProps, SortPage } from "@saleor/types";
|
||||||
|
import { PluginListUrlSortField } from "@saleor/plugins/urls";
|
||||||
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
||||||
import PluginsList from "../PluginsList/PluginsList";
|
import PluginsList from "../PluginsList/PluginsList";
|
||||||
|
|
||||||
export interface PluginsListPageProps extends PageListProps {
|
export interface PluginsListPageProps
|
||||||
|
extends PageListProps,
|
||||||
|
SortPage<PluginListUrlSortField> {
|
||||||
plugins: Plugins_plugins_edges_node[];
|
plugins: Plugins_plugins_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PluginsListPage: React.FC<PluginsListPageProps> = ({
|
const PluginsListPage: React.FC<PluginsListPageProps> = ({
|
||||||
disabled,
|
|
||||||
settings,
|
|
||||||
onBack,
|
onBack,
|
||||||
onNextPage,
|
...listProps
|
||||||
onPreviousPage,
|
|
||||||
onRowClick,
|
|
||||||
onUpdateListSettings,
|
|
||||||
pageInfo,
|
|
||||||
plugins
|
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>
|
<AppHeader onBack={onBack}>
|
||||||
{intl.formatMessage(sectionNames.configuration)}
|
{intl.formatMessage(sectionNames.configuration)}
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.plugins)} />
|
<PageHeader title={intl.formatMessage(sectionNames.plugins)} />
|
||||||
<PluginsList
|
<PluginsList {...listProps} />
|
||||||
disabled={disabled}
|
|
||||||
settings={settings}
|
|
||||||
plugins={plugins}
|
|
||||||
onNextPage={onNextPage}
|
|
||||||
onPreviousPage={onPreviousPage}
|
|
||||||
onUpdateListSettings={onUpdateListSettings}
|
|
||||||
onRowClick={onRowClick}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,25 +4,30 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
pluginsListPath,
|
pluginListPath,
|
||||||
PluginsListUrlQueryParams,
|
PluginListUrlQueryParams,
|
||||||
pluginsPath,
|
pluginPath,
|
||||||
PluginsUrlQueryParams
|
PluginUrlQueryParams,
|
||||||
|
PluginListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import PluginsDetailsComponent from "./views/PluginsDetails";
|
import PluginsDetailsComponent from "./views/PluginsDetails";
|
||||||
import PluginsListComponent from "./views/PluginsList";
|
import PluginsListComponent from "./views/PluginList";
|
||||||
|
|
||||||
const PluginList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
const PluginList: React.FC<RouteComponentProps<any>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: PluginsListUrlQueryParams = qs;
|
const params: PluginListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
PluginListUrlSortField
|
||||||
|
);
|
||||||
return <PluginsListComponent params={params} />;
|
return <PluginsListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PageDetails: React.FC<RouteComponentProps<any>> = ({ match }) => {
|
const PageDetails: React.FC<RouteComponentProps<any>> = ({ match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: PluginsUrlQueryParams = qs;
|
const params: PluginUrlQueryParams = qs;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PluginsDetailsComponent
|
<PluginsDetailsComponent
|
||||||
|
@ -38,8 +43,8 @@ const Component = () => {
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={intl.formatMessage(sectionNames.plugins)} />
|
<WindowTitle title={intl.formatMessage(sectionNames.plugins)} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={pluginsListPath} component={PluginList} />
|
<Route exact path={pluginListPath} component={PluginList} />
|
||||||
<Route path={pluginsPath(":id")} component={PageDetails} />
|
<Route path={pluginPath(":id")} component={PageDetails} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { TypedQuery } from "../queries";
|
import { TypedQuery } from "../queries";
|
||||||
import { Plugin, PluginVariables } from "./types/Plugin";
|
import { Plugin, PluginVariables } from "./types/Plugin";
|
||||||
import { Plugins, PluginsVariables } from "./types/Plugins";
|
import { Plugins, PluginsVariables } from "./types/Plugins";
|
||||||
|
@ -29,8 +30,20 @@ export const pluginsDetailsFragment = gql`
|
||||||
|
|
||||||
const pluginsList = gql`
|
const pluginsList = gql`
|
||||||
${pluginsFragment}
|
${pluginsFragment}
|
||||||
query Plugins($first: Int, $after: String, $last: Int, $before: String) {
|
query Plugins(
|
||||||
plugins(before: $before, after: $after, first: $first, last: $last) {
|
$first: Int
|
||||||
|
$after: String
|
||||||
|
$last: Int
|
||||||
|
$before: String
|
||||||
|
$sort: PluginSortingInput
|
||||||
|
) {
|
||||||
|
plugins(
|
||||||
|
before: $before
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
last: $last
|
||||||
|
sortBy: $sort
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...PluginFragment
|
...PluginFragment
|
||||||
|
@ -45,7 +58,7 @@ const pluginsList = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedPluginsListQuery = TypedQuery<Plugins, PluginsVariables>(
|
export const usePluginsListQuery = makeQuery<Plugins, PluginsVariables>(
|
||||||
pluginsList
|
pluginsList
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { PluginSortingInput } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: Plugins
|
// GraphQL query operation: Plugins
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
@ -42,4 +44,5 @@ export interface PluginsVariables {
|
||||||
after?: string | null;
|
after?: string | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
|
sort?: PluginSortingInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import { stringify as stringifyQs } from "qs";
|
import { stringify as stringifyQs } from "qs";
|
||||||
import urlJoin from "url-join";
|
import urlJoin from "url-join";
|
||||||
|
|
||||||
import { Dialog, Pagination, SingleAction } from "../types";
|
import { Dialog, Pagination, SingleAction, Sort } from "../types";
|
||||||
|
|
||||||
export const pluginsSection = "/plugins/";
|
export const pluginSection = "/plugins/";
|
||||||
|
|
||||||
export const pluginsListPath = pluginsSection;
|
export const pluginListPath = pluginSection;
|
||||||
export type PluginsListUrlQueryParams = Pagination & SingleAction;
|
export enum PluginListUrlSortField {
|
||||||
export const pluginsListUrl = (params?: PluginsListUrlQueryParams) =>
|
name = "name",
|
||||||
pluginsListPath + "?" + stringifyQs(params);
|
active = "active"
|
||||||
|
}
|
||||||
|
export type PluginListUrlSort = Sort<PluginListUrlSortField>;
|
||||||
|
export type PluginListUrlQueryParams = Pagination &
|
||||||
|
PluginListUrlSort &
|
||||||
|
SingleAction;
|
||||||
|
export const pluginListUrl = (params?: PluginListUrlQueryParams) =>
|
||||||
|
pluginListPath + "?" + stringifyQs(params);
|
||||||
|
|
||||||
export const pluginsPath = (id: string) => urlJoin(pluginsSection, id);
|
export const pluginPath = (id: string) => urlJoin(pluginSection, id);
|
||||||
export type PluginUrlDialog = "clear" | "edit";
|
export type PluginUrlDialog = "clear" | "edit";
|
||||||
export type PluginsUrlQueryParams = Dialog<PluginUrlDialog> & {
|
export type PluginUrlQueryParams = Dialog<PluginUrlDialog> & {
|
||||||
field?: string;
|
field?: string;
|
||||||
};
|
};
|
||||||
export const pluginsUrl = (id: string, params?: PluginsUrlQueryParams) =>
|
export const pluginsUrl = (id: string, params?: PluginUrlQueryParams) =>
|
||||||
pluginsPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
pluginPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
74
src/plugins/views/PluginList/PluginList.tsx
Normal file
74
src/plugins/views/PluginList/PluginList.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
|
import usePaginator, {
|
||||||
|
createPaginationState
|
||||||
|
} from "@saleor/hooks/usePaginator";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
|
import { ListViews } from "@saleor/types";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import PluginsListPage from "../../components/PluginsListPage/PluginsListPage";
|
||||||
|
import { usePluginsListQuery } from "../../queries";
|
||||||
|
import {
|
||||||
|
PluginListUrlQueryParams,
|
||||||
|
pluginListUrl,
|
||||||
|
pluginsUrl
|
||||||
|
} from "../../urls";
|
||||||
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
|
interface PluginsListProps {
|
||||||
|
params: PluginListUrlQueryParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PluginsList: React.FC<PluginsListProps> = ({ params }) => {
|
||||||
|
const navigate = useNavigator();
|
||||||
|
const paginate = usePaginator();
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
|
ListViews.PLUGINS_LIST
|
||||||
|
);
|
||||||
|
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
const queryVariables = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...paginationState,
|
||||||
|
sort: getSortQueryVariables(params)
|
||||||
|
}),
|
||||||
|
[params]
|
||||||
|
);
|
||||||
|
const { data, loading } = usePluginsListQuery({
|
||||||
|
displayLoader: true,
|
||||||
|
variables: queryVariables
|
||||||
|
});
|
||||||
|
|
||||||
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
|
maybe(() => data.plugins.pageInfo),
|
||||||
|
paginationState,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSort = createSortHandler(navigate, pluginListUrl, params);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PluginsListPage
|
||||||
|
disabled={loading}
|
||||||
|
settings={settings}
|
||||||
|
plugins={maybe(() => data.plugins.edges.map(edge => edge.node))}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
sort={getSortParams(params)}
|
||||||
|
onAdd={() => navigate(configurationMenuUrl)}
|
||||||
|
onBack={() => navigate(configurationMenuUrl)}
|
||||||
|
onNextPage={loadNextPage}
|
||||||
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onSort={handleSort}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
|
onRowClick={id => () => navigate(pluginsUrl(id))}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PluginsList;
|
2
src/plugins/views/PluginList/index.ts
Normal file
2
src/plugins/views/PluginList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./PluginList";
|
||||||
|
export * from "./PluginList";
|
20
src/plugins/views/PluginList/sort.ts
Normal file
20
src/plugins/views/PluginList/sort.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { PluginListUrlSortField } from "@saleor/plugins/urls";
|
||||||
|
import { PluginSortField } from "@saleor/types/globalTypes";
|
||||||
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export function getSortQueryField(
|
||||||
|
sort: PluginListUrlSortField
|
||||||
|
): PluginSortField {
|
||||||
|
switch (sort) {
|
||||||
|
case PluginListUrlSortField.name:
|
||||||
|
return PluginSortField.NAME;
|
||||||
|
case PluginListUrlSortField.active:
|
||||||
|
return PluginSortField.IS_ACTIVE;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||||
|
getSortQueryField
|
||||||
|
);
|
|
@ -16,16 +16,16 @@ import { TypedPluginsDetailsQuery } from "../queries";
|
||||||
import { Plugin_plugin_configuration } from "../types/Plugin";
|
import { Plugin_plugin_configuration } from "../types/Plugin";
|
||||||
import { PluginUpdate } from "../types/PluginUpdate";
|
import { PluginUpdate } from "../types/PluginUpdate";
|
||||||
import {
|
import {
|
||||||
pluginsListUrl,
|
pluginListUrl,
|
||||||
pluginsUrl,
|
pluginsUrl,
|
||||||
PluginsUrlQueryParams,
|
PluginUrlQueryParams,
|
||||||
PluginUrlDialog
|
PluginUrlDialog
|
||||||
} from "../urls";
|
} from "../urls";
|
||||||
import { isSecretField } from "../utils";
|
import { isSecretField } from "../utils";
|
||||||
|
|
||||||
export interface PluginsDetailsProps {
|
export interface PluginsDetailsProps {
|
||||||
id: string;
|
id: string;
|
||||||
params: PluginsUrlQueryParams;
|
params: PluginUrlQueryParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConfigurationInput(
|
export function getConfigurationInput(
|
||||||
|
@ -117,7 +117,7 @@ export const PluginsDetails: React.FC<PluginsDetailsProps> = ({
|
||||||
!params.action ? pluginUpdateOpts.status : "default"
|
!params.action ? pluginUpdateOpts.status : "default"
|
||||||
}
|
}
|
||||||
plugin={maybe(() => pluginDetails.data.plugin)}
|
plugin={maybe(() => pluginDetails.data.plugin)}
|
||||||
onBack={() => navigate(pluginsListUrl())}
|
onBack={() => navigate(pluginListUrl())}
|
||||||
onClear={field => openModal("clear", field)}
|
onClear={field => openModal("clear", field)}
|
||||||
onEdit={field => openModal("edit", field)}
|
onEdit={field => openModal("edit", field)}
|
||||||
onSubmit={formData =>
|
onSubmit={formData =>
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
|
||||||
import usePaginator, {
|
|
||||||
createPaginationState
|
|
||||||
} from "@saleor/hooks/usePaginator";
|
|
||||||
import { maybe } from "@saleor/misc";
|
|
||||||
import { ListViews } from "@saleor/types";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import PluginsListPage from "../components/PluginsListPage/PluginsListPage";
|
|
||||||
import { TypedPluginsListQuery } from "../queries";
|
|
||||||
import { PluginsListUrlQueryParams, pluginsUrl } from "../urls";
|
|
||||||
|
|
||||||
interface PluginsListProps {
|
|
||||||
params: PluginsListUrlQueryParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PluginsList: React.FC<PluginsListProps> = ({ params }) => {
|
|
||||||
const navigate = useNavigator();
|
|
||||||
const paginate = usePaginator();
|
|
||||||
const { updateListSettings, settings } = useListSettings(
|
|
||||||
ListViews.PLUGINS_LIST
|
|
||||||
);
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TypedPluginsListQuery displayLoader variables={paginationState}>
|
|
||||||
{({ data, loading }) => {
|
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
|
||||||
maybe(() => data.plugins.pageInfo),
|
|
||||||
paginationState,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PluginsListPage
|
|
||||||
disabled={loading}
|
|
||||||
settings={settings}
|
|
||||||
plugins={maybe(() => data.plugins.edges.map(edge => edge.node))}
|
|
||||||
pageInfo={pageInfo}
|
|
||||||
onAdd={() => navigate(configurationMenuUrl)}
|
|
||||||
onBack={() => navigate(configurationMenuUrl)}
|
|
||||||
onNextPage={loadNextPage}
|
|
||||||
onPreviousPage={loadPreviousPage}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
|
||||||
onRowClick={id => () => navigate(pluginsUrl(id))}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</TypedPluginsListQuery>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PluginsList;
|
|
|
@ -12,8 +12,11 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
|
import { ProductTypeListUrlSortField } from "@saleor/productTypes/urls";
|
||||||
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
|
import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps, SortPage } from "../../../types";
|
||||||
import { ProductTypeList_productTypes_edges_node } from "../../types/ProductTypeList";
|
import { ProductTypeList_productTypes_edges_node } from "../../types/ProductTypeList";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -39,7 +42,10 @@ const useStyles = makeStyles(
|
||||||
{ name: "ProductTypeList" }
|
{ name: "ProductTypeList" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface ProductTypeListProps extends ListProps, ListActions {
|
interface ProductTypeListProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
SortPage<ProductTypeListUrlSortField> {
|
||||||
productTypes: ProductTypeList_productTypes_edges_node[];
|
productTypes: ProductTypeList_productTypes_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +59,10 @@ const ProductTypeList: React.FC<ProductTypeListProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
onSort,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -73,18 +81,35 @@ const ProductTypeList: React.FC<ProductTypeListProps> = props => {
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === ProductTypeListUrlSortField.name
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
arrowPosition="right"
|
||||||
|
onClick={() => onSort(ProductTypeListUrlSortField.name)}
|
||||||
|
className={classes.colName}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Type Name"
|
defaultMessage="Type Name"
|
||||||
description="product type name"
|
description="product type name"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colType}>
|
<TableCellHeader
|
||||||
|
direction={
|
||||||
|
sort.sort === ProductTypeListUrlSortField.digital
|
||||||
|
? getArrowDirection(sort.asc)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={() => onSort(ProductTypeListUrlSortField.digital)}
|
||||||
|
className={classes.colType}
|
||||||
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Type"
|
defaultMessage="Type"
|
||||||
description="product type is either simple or configurable"
|
description="product type is either simple or configurable"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCellHeader>
|
||||||
<TableCell className={classes.colTax}>
|
<TableCell className={classes.colTax}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Tax"
|
defaultMessage="Tax"
|
||||||
|
|
|
@ -8,11 +8,13 @@ import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SearchBar from "@saleor/components/SearchBar";
|
import SearchBar from "@saleor/components/SearchBar";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { ProductTypeListUrlSortField } from "@saleor/productTypes/urls";
|
||||||
import {
|
import {
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
TabPageProps
|
TabPageProps,
|
||||||
|
SortPage
|
||||||
} from "../../../types";
|
} from "../../../types";
|
||||||
import { ProductTypeList_productTypes_edges_node } from "../../types/ProductTypeList";
|
import { ProductTypeList_productTypes_edges_node } from "../../types/ProductTypeList";
|
||||||
import ProductTypeList from "../ProductTypeList";
|
import ProductTypeList from "../ProductTypeList";
|
||||||
|
@ -21,6 +23,7 @@ export interface ProductTypeListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
SortPage<ProductTypeListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
productTypes: ProductTypeList_productTypes_edges_node[];
|
productTypes: ProductTypeList_productTypes_edges_node[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
|
|
@ -4,13 +4,15 @@ import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
import { asSortParams } from "@saleor/utils/sort";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
productTypeAddPath,
|
productTypeAddPath,
|
||||||
productTypeListPath,
|
productTypeListPath,
|
||||||
ProductTypeListUrlQueryParams,
|
ProductTypeListUrlQueryParams,
|
||||||
productTypePath,
|
productTypePath,
|
||||||
ProductTypeUrlQueryParams
|
ProductTypeUrlQueryParams,
|
||||||
|
ProductTypeListUrlSortField
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import ProductTypeCreate from "./views/ProductTypeCreate";
|
import ProductTypeCreate from "./views/ProductTypeCreate";
|
||||||
import ProductTypeListComponent from "./views/ProductTypeList";
|
import ProductTypeListComponent from "./views/ProductTypeList";
|
||||||
|
@ -18,16 +20,19 @@ import ProductTypeUpdateComponent from "./views/ProductTypeUpdate";
|
||||||
|
|
||||||
const ProductTypeList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
const ProductTypeList: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: ProductTypeListUrlQueryParams = qs;
|
const params: ProductTypeListUrlQueryParams = asSortParams(
|
||||||
|
qs,
|
||||||
|
ProductTypeListUrlSortField
|
||||||
|
);
|
||||||
return <ProductTypeListComponent params={params} />;
|
return <ProductTypeListComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ProductTypeUpdateRouteParams {
|
interface ProductTypeUpdateRouteParams {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
const ProductTypeUpdate: React.FC<
|
const ProductTypeUpdate: React.FC<RouteComponentProps<
|
||||||
RouteComponentProps<ProductTypeUpdateRouteParams>
|
ProductTypeUpdateRouteParams
|
||||||
> = ({ match }) => {
|
>> = ({ match }) => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
const params: ProductTypeUrlQueryParams = qs;
|
const params: ProductTypeUrlQueryParams = qs;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
import { attributeFragment } from "@saleor/attributes/queries";
|
import { attributeFragment } from "@saleor/attributes/queries";
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
import { pageInfoFragment, TypedQuery } from "../queries";
|
import { pageInfoFragment, TypedQuery } from "../queries";
|
||||||
import { ProductTypeCreateData } from "./types/ProductTypeCreateData";
|
import { ProductTypeCreateData } from "./types/ProductTypeCreateData";
|
||||||
import {
|
import {
|
||||||
|
@ -52,6 +53,7 @@ export const productTypeListQuery = gql`
|
||||||
$first: Int
|
$first: Int
|
||||||
$last: Int
|
$last: Int
|
||||||
$filter: ProductTypeFilterInput
|
$filter: ProductTypeFilterInput
|
||||||
|
$sort: ProductTypeSortingInput
|
||||||
) {
|
) {
|
||||||
productTypes(
|
productTypes(
|
||||||
after: $after
|
after: $after
|
||||||
|
@ -59,6 +61,7 @@ export const productTypeListQuery = gql`
|
||||||
first: $first
|
first: $first
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
sortBy: $sort
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -71,7 +74,7 @@ export const productTypeListQuery = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const TypedProductTypeListQuery = TypedQuery<
|
export const useProductTypeListQuery = makeQuery<
|
||||||
ProductTypeList,
|
ProductTypeList,
|
||||||
ProductTypeListVariables
|
ProductTypeListVariables
|
||||||
>(productTypeListQuery);
|
>(productTypeListQuery);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue