Saleor 1636 add possibility to filter through channels in order view (#929)
* Add possibility to filter through channels in order view * Update storybook and locale * Refactor * Refactor to mapNodetoChoice * Fix conditional value in array implicit syntax Co-authored-by: Jakub Majorek <majorek.jakub@gmail.com>
This commit is contained in:
parent
d0be941ade
commit
4a8ebd5b1f
13 changed files with 103 additions and 45 deletions
|
@ -3550,6 +3550,10 @@
|
||||||
"context": "tab name",
|
"context": "tab name",
|
||||||
"string": "All Orders"
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderListPage_dot_customer": {
|
||||||
"context": "order",
|
"context": "order",
|
||||||
"string": "Customer"
|
"string": "Customer"
|
||||||
|
|
|
@ -3042,6 +3042,7 @@ input OrderFilterInput {
|
||||||
customer: String
|
customer: String
|
||||||
created: DateRangeInput
|
created: DateRangeInput
|
||||||
search: String
|
search: String
|
||||||
|
channels: [ID]
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderFulfill {
|
type OrderFulfill {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Decorator from "@saleor/storybook/Decorator";
|
import Decorator from "@saleor/storybook/Decorator";
|
||||||
|
import { mapNodeToChoice } from "@saleor/utils/maps";
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -8,10 +9,7 @@ import ChannelDeleteDialog, {
|
||||||
} from "./ChannelDeleteDialog";
|
} from "./ChannelDeleteDialog";
|
||||||
|
|
||||||
const props: ChannelDeleteDialogProps = {
|
const props: ChannelDeleteDialogProps = {
|
||||||
channelsChoices: channelsList.map(channel => ({
|
channelsChoices: mapNodeToChoice(channelsList),
|
||||||
label: channel.name,
|
|
||||||
value: channel.id
|
|
||||||
})),
|
|
||||||
hasOrders: true,
|
hasOrders: true,
|
||||||
confirmButtonState: "default",
|
confirmButtonState: "default",
|
||||||
onBack: () => undefined,
|
onBack: () => undefined,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Decorator from "@saleor/storybook/Decorator";
|
import Decorator from "@saleor/storybook/Decorator";
|
||||||
|
import { mapNodeToChoice } from "@saleor/utils/maps";
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -7,10 +8,7 @@ import ChannelPickerDialog, {
|
||||||
ChannelPickerDialogProps
|
ChannelPickerDialogProps
|
||||||
} from "./ChannelPickerDialog";
|
} from "./ChannelPickerDialog";
|
||||||
|
|
||||||
const channelsChoices = channelsList.map(channel => ({
|
const channelsChoices = mapNodeToChoice(channelsList);
|
||||||
label: channel.name,
|
|
||||||
value: channel.id
|
|
||||||
}));
|
|
||||||
|
|
||||||
const props: ChannelPickerDialogProps = {
|
const props: ChannelPickerDialogProps = {
|
||||||
channelsChoices,
|
channelsChoices,
|
||||||
|
|
|
@ -157,6 +157,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<Hr />
|
<Hr />
|
||||||
{filters
|
{filters
|
||||||
|
.filter(filter => !!filter)
|
||||||
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
||||||
.map(filterField => (
|
.map(filterField => (
|
||||||
<React.Fragment key={filterField.name}>
|
<React.Fragment key={filterField.name}>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { IFilter } from "@saleor/components/Filter";
|
import { IFilter } from "@saleor/components/Filter";
|
||||||
|
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { orderStatusMessages } from "@saleor/misc";
|
import { orderStatusMessages } from "@saleor/misc";
|
||||||
import { FilterOpts, MinMax } from "@saleor/types";
|
import { FilterOpts, MinMax } from "@saleor/types";
|
||||||
|
@ -13,16 +14,22 @@ import { defineMessages, IntlShape } from "react-intl";
|
||||||
export enum OrderFilterKeys {
|
export enum OrderFilterKeys {
|
||||||
created = "created",
|
created = "created",
|
||||||
customer = "customer",
|
customer = "customer",
|
||||||
status = "status"
|
status = "status",
|
||||||
|
channel = "channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderListFilterOpts {
|
export interface OrderListFilterOpts {
|
||||||
created: FilterOpts<MinMax>;
|
created: FilterOpts<MinMax>;
|
||||||
customer: FilterOpts<string>;
|
customer: FilterOpts<string>;
|
||||||
status: FilterOpts<OrderStatusFilter[]>;
|
status: FilterOpts<OrderStatusFilter[]>;
|
||||||
|
channel?: FilterOpts<MultiAutocompleteChoiceType[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
channel: {
|
||||||
|
defaultMessage: "Channel",
|
||||||
|
description: "order"
|
||||||
|
},
|
||||||
customer: {
|
customer: {
|
||||||
defaultMessage: "Customer",
|
defaultMessage: "Customer",
|
||||||
description: "order"
|
description: "order"
|
||||||
|
@ -88,6 +95,20 @@ export function createFilterStructure(
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
active: opts.status.active
|
active: opts.status.active
|
||||||
}
|
},
|
||||||
|
...(opts?.channel?.value.length
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
...createOptionsField(
|
||||||
|
OrderFilterKeys.channel,
|
||||||
|
intl.formatMessage(messages.channel),
|
||||||
|
[],
|
||||||
|
true,
|
||||||
|
opts.channel.value
|
||||||
|
),
|
||||||
|
active: opts.channel.active
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
BulkAction,
|
BulkAction,
|
||||||
Dialog,
|
Dialog,
|
||||||
Filters,
|
Filters,
|
||||||
|
FiltersAsDictWithMultipleValues,
|
||||||
FiltersWithMultipleValues,
|
FiltersWithMultipleValues,
|
||||||
Pagination,
|
Pagination,
|
||||||
SingleAction,
|
SingleAction,
|
||||||
|
@ -25,11 +26,17 @@ export enum OrderListUrlFiltersEnum {
|
||||||
payment = "payment",
|
payment = "payment",
|
||||||
query = "query"
|
query = "query"
|
||||||
}
|
}
|
||||||
export enum OrderListUrlFiltersWithMultipleValuesEnum {
|
export enum OrderListUrlFiltersWithMultipleValues {
|
||||||
status = "status"
|
status = "status"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrderListUrlFiltersDictWithMultipleValues {
|
||||||
|
channel = "channel"
|
||||||
|
}
|
||||||
|
|
||||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
||||||
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValues> &
|
||||||
|
FiltersAsDictWithMultipleValues<OrderListUrlFiltersDictWithMultipleValues>;
|
||||||
export type OrderListUrlDialog = "cancel" | CreateOrderDialog | TabActionDialog;
|
export type OrderListUrlDialog = "cancel" | CreateOrderDialog | TabActionDialog;
|
||||||
export enum OrderListUrlSortField {
|
export enum OrderListUrlSortField {
|
||||||
number = "number",
|
number = "number",
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { ListViews } from "@saleor/types";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
||||||
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
|
import { mapNodeToChoice } from "@saleor/utils/maps";
|
||||||
import { getSortParams } from "@saleor/utils/sort";
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
@ -72,6 +73,9 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
const { channel, availableChannels } = useAppChannel();
|
const { channel, availableChannels } = useAppChannel();
|
||||||
|
|
||||||
const noChannel = !channel && typeof channel !== "undefined";
|
const noChannel = !channel && typeof channel !== "undefined";
|
||||||
|
const channelOpts = availableChannels
|
||||||
|
? mapNodeToChoice(availableChannels)
|
||||||
|
: null;
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
|
@ -132,7 +136,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||||
maybe(() => data.orders.pageInfo),
|
data?.orders?.pageInfo,
|
||||||
paginationState,
|
paginationState,
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
|
@ -145,7 +149,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
settings={settings}
|
settings={settings}
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
filterOpts={getFilterOpts(params)}
|
filterOpts={getFilterOpts(params, channelOpts)}
|
||||||
orders={maybe(() => data.orders.edges.map(edge => edge.node))}
|
orders={maybe(() => data.orders.edges.map(edge => edge.node))}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
sort={getSortParams(params)}
|
sort={getSortParams(params)}
|
||||||
|
@ -180,10 +184,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
/>
|
/>
|
||||||
{!noChannel && (
|
{!noChannel && (
|
||||||
<ChannelPickerDialog
|
<ChannelPickerDialog
|
||||||
channelsChoices={availableChannels.map(channel => ({
|
channelsChoices={mapNodeToChoice(availableChannels)}
|
||||||
label: channel.name,
|
|
||||||
value: channel.id
|
|
||||||
}))}
|
|
||||||
confirmButtonState="success"
|
confirmButtonState="success"
|
||||||
defaultChoice={channel.id}
|
defaultChoice={channel.id}
|
||||||
open={params.action === "create-order"}
|
open={params.action === "create-order"}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
exports[`Filtering URL params should not be empty if active filters are present 1`] = `
|
exports[`Filtering URL params should not be empty if active filters are present 1`] = `
|
||||||
Object {
|
Object {
|
||||||
|
"channel": Array [],
|
||||||
"createdFrom": "2019-12-09",
|
"createdFrom": "2019-12-09",
|
||||||
"createdTo": "2019-12-38",
|
"createdTo": "2019-12-38",
|
||||||
"customer": "email@example.com",
|
"customer": "email@example.com",
|
||||||
|
|
|
@ -38,6 +38,15 @@ describe("Filtering URL params", () => {
|
||||||
const intl = createIntl(config);
|
const intl = createIntl(config);
|
||||||
|
|
||||||
const filters = createFilterStructure(intl, {
|
const filters = createFilterStructure(intl, {
|
||||||
|
channel: {
|
||||||
|
active: false,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
label: "Channel PLN",
|
||||||
|
value: "channelId"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
created: {
|
created: {
|
||||||
active: false,
|
active: false,
|
||||||
value: {
|
value: {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { findInEnum, findValueInEnum, maybe } from "@saleor/misc";
|
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
|
||||||
|
import { findInEnum, findValueInEnum } from "@saleor/misc";
|
||||||
import {
|
import {
|
||||||
OrderFilterKeys,
|
OrderFilterKeys,
|
||||||
OrderListFilterOpts
|
OrderListFilterOpts
|
||||||
|
@ -16,48 +17,49 @@ import {
|
||||||
getGteLteVariables,
|
getGteLteVariables,
|
||||||
getMinMaxQueryParam,
|
getMinMaxQueryParam,
|
||||||
getMultipleEnumValueQueryParam,
|
getMultipleEnumValueQueryParam,
|
||||||
|
getMultipleValueQueryParam,
|
||||||
getSingleValueQueryParam
|
getSingleValueQueryParam
|
||||||
} from "../../../utils/filters";
|
} from "../../../utils/filters";
|
||||||
import {
|
import {
|
||||||
OrderListUrlFilters,
|
OrderListUrlFilters,
|
||||||
|
OrderListUrlFiltersDictWithMultipleValues,
|
||||||
OrderListUrlFiltersEnum,
|
OrderListUrlFiltersEnum,
|
||||||
OrderListUrlFiltersWithMultipleValuesEnum,
|
OrderListUrlFiltersWithMultipleValues,
|
||||||
OrderListUrlQueryParams
|
OrderListUrlQueryParams
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
|
|
||||||
export const ORDER_FILTERS_KEY = "orderFilters";
|
export const ORDER_FILTERS_KEY = "orderFilters";
|
||||||
|
|
||||||
export function getFilterOpts(
|
export function getFilterOpts(
|
||||||
params: OrderListUrlFilters
|
params: OrderListUrlFilters,
|
||||||
|
channels: MultiAutocompleteChoiceType[]
|
||||||
): OrderListFilterOpts {
|
): OrderListFilterOpts {
|
||||||
return {
|
return {
|
||||||
|
channel: channels
|
||||||
|
? {
|
||||||
|
active: params?.channel !== undefined,
|
||||||
|
value: channels
|
||||||
|
}
|
||||||
|
: null,
|
||||||
created: {
|
created: {
|
||||||
active: maybe(
|
active: [params?.createdFrom, params?.createdTo].some(
|
||||||
() =>
|
field => field !== undefined
|
||||||
[params.createdFrom, params.createdTo].some(
|
|
||||||
field => field !== undefined
|
|
||||||
),
|
|
||||||
false
|
|
||||||
),
|
),
|
||||||
value: {
|
value: {
|
||||||
max: maybe(() => params.createdTo, ""),
|
max: params?.createdTo || "",
|
||||||
min: maybe(() => params.createdFrom, "")
|
min: params?.createdFrom || ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
active: !!maybe(() => params.customer),
|
active: !!params?.customer,
|
||||||
value: params.customer
|
value: params?.customer
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
active: maybe(() => params.status !== undefined, false),
|
active: params?.status !== undefined,
|
||||||
value: maybe(
|
value: dedupeFilter(
|
||||||
() =>
|
params?.status?.map(status =>
|
||||||
dedupeFilter(
|
findValueInEnum(status, OrderStatusFilter)
|
||||||
params.status.map(status =>
|
)
|
||||||
findValueInEnum(status, OrderStatusFilter)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -67,15 +69,14 @@ export function getFilterVariables(
|
||||||
params: OrderListUrlFilters
|
params: OrderListUrlFilters
|
||||||
): OrderFilterInput {
|
): OrderFilterInput {
|
||||||
return {
|
return {
|
||||||
|
channels: (params.channel as unknown) as string[],
|
||||||
created: getGteLteVariables({
|
created: getGteLteVariables({
|
||||||
gte: params.createdFrom,
|
gte: params.createdFrom,
|
||||||
lte: params.createdTo
|
lte: params.createdTo
|
||||||
}),
|
}),
|
||||||
customer: params.customer,
|
customer: params.customer,
|
||||||
search: params.query,
|
search: params.query,
|
||||||
status: maybe(() =>
|
status: params?.status?.map(status => findInEnum(status, OrderStatusFilter))
|
||||||
params.status.map(status => findInEnum(status, OrderStatusFilter))
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,10 +96,16 @@ export function getFilterQueryParam(
|
||||||
case OrderFilterKeys.status:
|
case OrderFilterKeys.status:
|
||||||
return getMultipleEnumValueQueryParam(
|
return getMultipleEnumValueQueryParam(
|
||||||
filter,
|
filter,
|
||||||
OrderListUrlFiltersWithMultipleValuesEnum.status,
|
OrderListUrlFiltersWithMultipleValues.status,
|
||||||
OrderStatusFilter
|
OrderStatusFilter
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case OrderFilterKeys.channel:
|
||||||
|
return getMultipleValueQueryParam(
|
||||||
|
filter,
|
||||||
|
OrderListUrlFiltersDictWithMultipleValues.channel
|
||||||
|
);
|
||||||
|
|
||||||
case OrderFilterKeys.customer:
|
case OrderFilterKeys.customer:
|
||||||
return getSingleValueQueryParam(filter, OrderListUrlFiltersEnum.customer);
|
return getSingleValueQueryParam(filter, OrderListUrlFiltersEnum.customer);
|
||||||
}
|
}
|
||||||
|
@ -115,5 +122,5 @@ export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
|
||||||
OrderListUrlFilters
|
OrderListUrlFilters
|
||||||
>({
|
>({
|
||||||
...OrderListUrlFiltersEnum,
|
...OrderListUrlFiltersEnum,
|
||||||
...OrderListUrlFiltersWithMultipleValuesEnum
|
...OrderListUrlFiltersWithMultipleValues
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,6 +21,15 @@ const props: OrderListPageProps = {
|
||||||
...filterPageProps,
|
...filterPageProps,
|
||||||
...sortPageProps,
|
...sortPageProps,
|
||||||
filterOpts: {
|
filterOpts: {
|
||||||
|
channel: {
|
||||||
|
active: false,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
label: "Channel PLN",
|
||||||
|
value: "channelId"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
created: {
|
created: {
|
||||||
active: false,
|
active: false,
|
||||||
value: {
|
value: {
|
||||||
|
|
|
@ -1326,6 +1326,7 @@ export interface OrderFilterInput {
|
||||||
customer?: string | null;
|
customer?: string | null;
|
||||||
created?: DateRangeInput | null;
|
created?: DateRangeInput | null;
|
||||||
search?: string | null;
|
search?: string | null;
|
||||||
|
channels?: (string | null)[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderFulfillInput {
|
export interface OrderFulfillInput {
|
||||||
|
|
Loading…
Reference in a new issue