diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index f4d3aba4c..17ac8c07b 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3550,6 +3550,10 @@ "context": "tab name", "string": "All Orders" }, + "src_dot_orders_dot_components_dot_OrderListPage_dot_channel": { + "context": "order", + "string": "Channel" + }, "src_dot_orders_dot_components_dot_OrderListPage_dot_customer": { "context": "order", "string": "Customer" diff --git a/schema.graphql b/schema.graphql index 0bdaec696..2d320ab14 100644 --- a/schema.graphql +++ b/schema.graphql @@ -3042,6 +3042,7 @@ input OrderFilterInput { customer: String created: DateRangeInput search: String + channels: [ID] } type OrderFulfill { diff --git a/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx index fa14df20e..0c0785f4e 100644 --- a/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx +++ b/src/channels/components/ChannelDeleteDialog/ChannelDeleteDialog.stories.tsx @@ -1,4 +1,5 @@ import Decorator from "@saleor/storybook/Decorator"; +import { mapNodeToChoice } from "@saleor/utils/maps"; import { storiesOf } from "@storybook/react"; import React from "react"; @@ -8,10 +9,7 @@ import ChannelDeleteDialog, { } from "./ChannelDeleteDialog"; const props: ChannelDeleteDialogProps = { - channelsChoices: channelsList.map(channel => ({ - label: channel.name, - value: channel.id - })), + channelsChoices: mapNodeToChoice(channelsList), hasOrders: true, confirmButtonState: "default", onBack: () => undefined, diff --git a/src/channels/components/ChannelPickerDialog/ChannelPickerDialog.stories.tsx b/src/channels/components/ChannelPickerDialog/ChannelPickerDialog.stories.tsx index ccdf2f594..ef7be15b5 100644 --- a/src/channels/components/ChannelPickerDialog/ChannelPickerDialog.stories.tsx +++ b/src/channels/components/ChannelPickerDialog/ChannelPickerDialog.stories.tsx @@ -1,4 +1,5 @@ import Decorator from "@saleor/storybook/Decorator"; +import { mapNodeToChoice } from "@saleor/utils/maps"; import { storiesOf } from "@storybook/react"; import React from "react"; @@ -7,10 +8,7 @@ import ChannelPickerDialog, { ChannelPickerDialogProps } from "./ChannelPickerDialog"; -const channelsChoices = channelsList.map(channel => ({ - label: channel.name, - value: channel.id -})); +const channelsChoices = mapNodeToChoice(channelsList); const props: ChannelPickerDialogProps = { channelsChoices, diff --git a/src/components/Filter/FilterContent.tsx b/src/components/Filter/FilterContent.tsx index 18924d80d..7d8faf12c 100644 --- a/src/components/Filter/FilterContent.tsx +++ b/src/components/Filter/FilterContent.tsx @@ -157,6 +157,7 @@ const FilterContent: React.FC = ({
{filters + .filter(filter => !!filter) .sort((a, b) => (a.name > b.name ? 1 : -1)) .map(filterField => ( diff --git a/src/orders/components/OrderListPage/filters.ts b/src/orders/components/OrderListPage/filters.ts index 98228311e..a47826db7 100644 --- a/src/orders/components/OrderListPage/filters.ts +++ b/src/orders/components/OrderListPage/filters.ts @@ -1,4 +1,5 @@ import { IFilter } from "@saleor/components/Filter"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { commonMessages } from "@saleor/intl"; import { orderStatusMessages } from "@saleor/misc"; import { FilterOpts, MinMax } from "@saleor/types"; @@ -13,16 +14,22 @@ import { defineMessages, IntlShape } from "react-intl"; export enum OrderFilterKeys { created = "created", customer = "customer", - status = "status" + status = "status", + channel = "channel" } export interface OrderListFilterOpts { created: FilterOpts; customer: FilterOpts; status: FilterOpts; + channel?: FilterOpts; } const messages = defineMessages({ + channel: { + defaultMessage: "Channel", + description: "order" + }, customer: { defaultMessage: "Customer", description: "order" @@ -88,6 +95,20 @@ export function createFilterStructure( ] ), active: opts.status.active - } + }, + ...(opts?.channel?.value.length + ? [ + { + ...createOptionsField( + OrderFilterKeys.channel, + intl.formatMessage(messages.channel), + [], + true, + opts.channel.value + ), + active: opts.channel.active + } + ] + : []) ]; } diff --git a/src/orders/urls.ts b/src/orders/urls.ts index 1280dcba5..615f57043 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -6,6 +6,7 @@ import { BulkAction, Dialog, Filters, + FiltersAsDictWithMultipleValues, FiltersWithMultipleValues, Pagination, SingleAction, @@ -25,11 +26,17 @@ export enum OrderListUrlFiltersEnum { payment = "payment", query = "query" } -export enum OrderListUrlFiltersWithMultipleValuesEnum { +export enum OrderListUrlFiltersWithMultipleValues { status = "status" } + +export enum OrderListUrlFiltersDictWithMultipleValues { + channel = "channel" +} + export type OrderListUrlFilters = Filters & - FiltersWithMultipleValues; + FiltersWithMultipleValues & + FiltersAsDictWithMultipleValues; export type OrderListUrlDialog = "cancel" | CreateOrderDialog | TabActionDialog; export enum OrderListUrlSortField { number = "number", diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index f9ac6b9fb..1c57ceec3 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -15,6 +15,7 @@ import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; +import { mapNodeToChoice } from "@saleor/utils/maps"; import { getSortParams } from "@saleor/utils/sort"; import React from "react"; import { useIntl } from "react-intl"; @@ -72,6 +73,9 @@ export const OrderList: React.FC = ({ params }) => { const { channel, availableChannels } = useAppChannel(); const noChannel = !channel && typeof channel !== "undefined"; + const channelOpts = availableChannels + ? mapNodeToChoice(availableChannels) + : null; const tabs = getFilterTabs(); @@ -132,7 +136,7 @@ export const OrderList: React.FC = ({ params }) => { }); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( - maybe(() => data.orders.pageInfo), + data?.orders?.pageInfo, paginationState, params ); @@ -145,7 +149,7 @@ export const OrderList: React.FC = ({ params }) => { settings={settings} currentTab={currentTab} disabled={loading} - filterOpts={getFilterOpts(params)} + filterOpts={getFilterOpts(params, channelOpts)} orders={maybe(() => data.orders.edges.map(edge => edge.node))} pageInfo={pageInfo} sort={getSortParams(params)} @@ -180,10 +184,7 @@ export const OrderList: React.FC = ({ params }) => { /> {!noChannel && ( ({ - label: channel.name, - value: channel.id - }))} + channelsChoices={mapNodeToChoice(availableChannels)} confirmButtonState="success" defaultChoice={channel.id} open={params.action === "create-order"} diff --git a/src/orders/views/OrderList/__snapshots__/filters.test.ts.snap b/src/orders/views/OrderList/__snapshots__/filters.test.ts.snap index e7163da40..acff4ee9b 100644 --- a/src/orders/views/OrderList/__snapshots__/filters.test.ts.snap +++ b/src/orders/views/OrderList/__snapshots__/filters.test.ts.snap @@ -2,6 +2,7 @@ exports[`Filtering URL params should not be empty if active filters are present 1`] = ` Object { + "channel": Array [], "createdFrom": "2019-12-09", "createdTo": "2019-12-38", "customer": "email@example.com", diff --git a/src/orders/views/OrderList/filters.test.ts b/src/orders/views/OrderList/filters.test.ts index ac0d5f386..77ce32e16 100644 --- a/src/orders/views/OrderList/filters.test.ts +++ b/src/orders/views/OrderList/filters.test.ts @@ -38,6 +38,15 @@ describe("Filtering URL params", () => { const intl = createIntl(config); const filters = createFilterStructure(intl, { + channel: { + active: false, + value: [ + { + label: "Channel PLN", + value: "channelId" + } + ] + }, created: { active: false, value: { diff --git a/src/orders/views/OrderList/filters.ts b/src/orders/views/OrderList/filters.ts index f24b39494..8f5c459ee 100644 --- a/src/orders/views/OrderList/filters.ts +++ b/src/orders/views/OrderList/filters.ts @@ -1,4 +1,5 @@ -import { findInEnum, findValueInEnum, maybe } from "@saleor/misc"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { findInEnum, findValueInEnum } from "@saleor/misc"; import { OrderFilterKeys, OrderListFilterOpts @@ -16,48 +17,49 @@ import { getGteLteVariables, getMinMaxQueryParam, getMultipleEnumValueQueryParam, + getMultipleValueQueryParam, getSingleValueQueryParam } from "../../../utils/filters"; import { OrderListUrlFilters, + OrderListUrlFiltersDictWithMultipleValues, OrderListUrlFiltersEnum, - OrderListUrlFiltersWithMultipleValuesEnum, + OrderListUrlFiltersWithMultipleValues, OrderListUrlQueryParams } from "../../urls"; export const ORDER_FILTERS_KEY = "orderFilters"; export function getFilterOpts( - params: OrderListUrlFilters + params: OrderListUrlFilters, + channels: MultiAutocompleteChoiceType[] ): OrderListFilterOpts { return { + channel: channels + ? { + active: params?.channel !== undefined, + value: channels + } + : null, created: { - active: maybe( - () => - [params.createdFrom, params.createdTo].some( - field => field !== undefined - ), - false + active: [params?.createdFrom, params?.createdTo].some( + field => field !== undefined ), value: { - max: maybe(() => params.createdTo, ""), - min: maybe(() => params.createdFrom, "") + max: params?.createdTo || "", + min: params?.createdFrom || "" } }, customer: { - active: !!maybe(() => params.customer), - value: params.customer + active: !!params?.customer, + value: params?.customer }, status: { - active: maybe(() => params.status !== undefined, false), - value: maybe( - () => - dedupeFilter( - params.status.map(status => - findValueInEnum(status, OrderStatusFilter) - ) - ), - [] + active: params?.status !== undefined, + value: dedupeFilter( + params?.status?.map(status => + findValueInEnum(status, OrderStatusFilter) + ) ) } }; @@ -67,15 +69,14 @@ export function getFilterVariables( params: OrderListUrlFilters ): OrderFilterInput { return { + channels: (params.channel as unknown) as string[], created: getGteLteVariables({ gte: params.createdFrom, lte: params.createdTo }), customer: params.customer, search: params.query, - status: maybe(() => - params.status.map(status => findInEnum(status, OrderStatusFilter)) - ) + status: params?.status?.map(status => findInEnum(status, OrderStatusFilter)) }; } @@ -95,10 +96,16 @@ export function getFilterQueryParam( case OrderFilterKeys.status: return getMultipleEnumValueQueryParam( filter, - OrderListUrlFiltersWithMultipleValuesEnum.status, + OrderListUrlFiltersWithMultipleValues.status, OrderStatusFilter ); + case OrderFilterKeys.channel: + return getMultipleValueQueryParam( + filter, + OrderListUrlFiltersDictWithMultipleValues.channel + ); + case OrderFilterKeys.customer: return getSingleValueQueryParam(filter, OrderListUrlFiltersEnum.customer); } @@ -115,5 +122,5 @@ export const { areFiltersApplied, getActiveFilters } = createFilterUtils< OrderListUrlFilters >({ ...OrderListUrlFiltersEnum, - ...OrderListUrlFiltersWithMultipleValuesEnum + ...OrderListUrlFiltersWithMultipleValues }); diff --git a/src/storybook/stories/orders/OrderListPage.tsx b/src/storybook/stories/orders/OrderListPage.tsx index 266b7aaba..b96142e84 100644 --- a/src/storybook/stories/orders/OrderListPage.tsx +++ b/src/storybook/stories/orders/OrderListPage.tsx @@ -21,6 +21,15 @@ const props: OrderListPageProps = { ...filterPageProps, ...sortPageProps, filterOpts: { + channel: { + active: false, + value: [ + { + label: "Channel PLN", + value: "channelId" + } + ] + }, created: { active: false, value: { diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index a805f262e..a856bfa98 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -1326,6 +1326,7 @@ export interface OrderFilterInput { customer?: string | null; created?: DateRangeInput | null; search?: string | null; + channels?: (string | null)[] | null; } export interface OrderFulfillInput {