Code cleanup

This commit is contained in:
dominik-zeglen 2019-12-20 16:53:03 +01:00
parent c155cf36e4
commit 1f3cbfda82
11 changed files with 156 additions and 73 deletions

View file

@ -351,14 +351,14 @@ export function findInEnum<TEnum extends object>(
export function findValueInEnum<TEnum extends object>( export function findValueInEnum<TEnum extends object>(
needle: string, needle: string,
haystack: TEnum haystack: TEnum
) { ): TEnum[keyof TEnum] {
const match = Object.entries(haystack).find(([_, value]) => value === needle); const match = Object.entries(haystack).find(([_, value]) => value === needle);
if (!!match) { if (!match) {
return match[1] as TEnum; throw new Error(`Value ${needle} not found in enum`);
} }
throw new Error(`Value ${needle} not found in enum`); return (needle as unknown) as TEnum[keyof TEnum];
} }
export function parseBoolean(a: string, defaultValue: boolean): boolean { export function parseBoolean(a: string, defaultValue: boolean): boolean {

View file

@ -21,11 +21,12 @@ import {
import FilterBar from "@saleor/components/FilterBar"; import FilterBar from "@saleor/components/FilterBar";
import { OrderList_orders_edges_node } from "../../types/OrderList"; import { OrderList_orders_edges_node } from "../../types/OrderList";
import OrderList from "../OrderList"; import OrderList from "../OrderList";
import { OrderListFilterOpts } from "../../types";
export interface OrderListPageProps export interface OrderListPageProps
extends PageListProps, extends PageListProps,
ListActions, ListActions,
FilterPageProps<OrderFilterKeys>, FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
SortPage<OrderListUrlSortField> { SortPage<OrderListUrlSortField> {
orders: OrderList_orders_edges_node[]; orders: OrderList_orders_edges_node[];
} }
@ -34,6 +35,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
currencySymbol, currencySymbol,
currentTab, currentTab,
initialSearch, initialSearch,
filterOpts,
tabs, tabs,
onAdd, onAdd,
onAll, onAll,
@ -46,7 +48,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const filterStructure = createFilterStructure(intl); const filterStructure = createFilterStructure(intl, filterOpts);
return ( return (
<Container> <Container>

7
src/orders/types.ts Normal file
View file

@ -0,0 +1,7 @@
import { FilterOpts, MinMax } from "@saleor/types";
import { OrderStatusFilter } from "@saleor/types/globalTypes";
export interface OrderListFilterOpts {
created: FilterOpts<MinMax>;
status: FilterOpts<OrderStatusFilter[]>;
}

View file

@ -41,6 +41,7 @@ import {
deleteFilterTab, deleteFilterTab,
getActiveFilters, getActiveFilters,
getFilterTabs, getFilterTabs,
getFilterOpts,
getFilterVariables, getFilterVariables,
saveFilterTab, saveFilterTab,
OrderFilterKeys, OrderFilterKeys,
@ -200,6 +201,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
settings={settings} settings={settings}
currentTab={currentTab} currentTab={currentTab}
disabled={loading} disabled={loading}
filterOpts={getFilterOpts(params)}
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)}

View file

@ -1,6 +1,11 @@
import { IntlShape } from "react-intl"; import { IntlShape } from "react-intl";
import { findInEnum, maybe, orderStatusMessages } from "@saleor/misc"; import {
findInEnum,
maybe,
orderStatusMessages,
findValueInEnum
} from "@saleor/misc";
import { import {
createDateField, createDateField,
createOptionsField createOptionsField
@ -22,6 +27,7 @@ import {
OrderListUrlFiltersWithMultipleValuesEnum, OrderListUrlFiltersWithMultipleValuesEnum,
OrderListUrlQueryParams OrderListUrlQueryParams
} from "../../urls"; } from "../../urls";
import { OrderListFilterOpts } from "../../types";
import messages from "./messages"; import messages from "./messages";
export const ORDER_FILTERS_KEY = "orderFilters"; export const ORDER_FILTERS_KEY = "orderFilters";
@ -31,39 +37,56 @@ export enum OrderFilterKeys {
status = "status" status = "status"
} }
export function createFilterStructure( export function getFilterOpts(
intl: IntlShape,
params: OrderListUrlFilters params: OrderListUrlFilters
): IFilter<OrderFilterKeys> { ): OrderListFilterOpts {
return [ return {
{ created: {
...createDateField(
OrderFilterKeys.created,
intl.formatMessage(messages.placed),
{
max: maybe(() => params.createdTo, ""),
min: maybe(() => params.createdFrom, "")
}
),
active: maybe( active: maybe(
() => () =>
[params.createdFrom, params.createdTo].some( [params.createdFrom, params.createdTo].some(
field => field !== undefined field => field !== undefined
), ),
false false
),
value: {
max: maybe(() => params.createdTo, ""),
min: maybe(() => params.createdFrom, "")
}
},
status: {
active: maybe(() => params.status !== undefined, false),
value: maybe(
() =>
dedupeFilter(
params.status.map(status =>
findValueInEnum(status, OrderStatusFilter)
) )
),
[]
)
}
};
}
export function createFilterStructure(
intl: IntlShape,
opts: OrderListFilterOpts
): IFilter<OrderFilterKeys> {
return [
{
...createDateField(
OrderFilterKeys.created,
intl.formatMessage(messages.placed),
opts.created.value
),
active: opts.created.active
}, },
{ {
...createOptionsField( ...createOptionsField(
OrderFilterKeys.status, OrderFilterKeys.status,
intl.formatMessage(messages.status), intl.formatMessage(messages.status),
maybe( opts.status.value,
() =>
dedupeFilter(
params.status.map(status => findInEnum(status, OrderStatusFilter))
),
[]
),
true, true,
[ [
{ {
@ -84,7 +107,7 @@ export function createFilterStructure(
} }
] ]
), ),
active: maybe(() => params.status !== undefined, false) active: opts.status.active
} }
]; ];
} }
@ -108,15 +131,22 @@ export function getFilterVariables(
export function getFilterQueryParam( export function getFilterQueryParam(
filter: IFilterElement<OrderFilterKeys> filter: IFilterElement<OrderFilterKeys>
): OrderListUrlFilters { ): OrderListUrlFilters {
const { active, name, value } = filter; const { active, multiple, name, value } = filter;
if (active) { if (active) {
switch (name) { switch (name) {
case OrderFilterKeys.created: case OrderFilterKeys.created:
if (multiple) {
return { return {
createdFrom: value[0], createdFrom: value[0],
createdTo: value[1] createdTo: value[1]
}; };
}
return {
createdFrom: value[0],
createdTo: value[0]
};
case OrderFilterKeys.status: case OrderFilterKeys.status:
return { return {

View file

@ -24,17 +24,18 @@ import {
SortPage SortPage
} from "@saleor/types"; } from "@saleor/types";
import FilterBar from "@saleor/components/FilterBar"; import FilterBar from "@saleor/components/FilterBar";
import { ProductListFilterOpts } from "@saleor/products/types";
import { ProductListUrlSortField } from "../../urls";
import { import {
createFilterStructure, createFilterStructure,
ProductFilterKeys ProductFilterKeys
} from "@saleor/products/views/ProductList/filters"; } from "../../views/ProductList/filters";
import { ProductListUrlSortField } from "../../urls";
import ProductList from "../ProductList"; import ProductList from "../ProductList";
export interface ProductListPageProps export interface ProductListPageProps
extends PageListProps<ProductListColumns>, extends PageListProps<ProductListColumns>,
ListActions, ListActions,
FilterPageProps<ProductFilterKeys>, FilterPageProps<ProductFilterKeys, ProductListFilterOpts>,
FetchMoreProps, FetchMoreProps,
SortPage<ProductListUrlSortField> { SortPage<ProductListUrlSortField> {
activeAttributeSortId: string; activeAttributeSortId: string;
@ -61,6 +62,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
defaultSettings, defaultSettings,
gridAttributes, gridAttributes,
availableInGridAttributes, availableInGridAttributes,
filterOpts,
hasMore, hasMore,
initialSearch, initialSearch,
loading, loading,
@ -84,7 +86,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
const handleSave = (columns: ProductListColumns[]) => const handleSave = (columns: ProductListColumns[]) =>
onUpdateListSettings("columns", columns); onUpdateListSettings("columns", columns);
const filterStructure = createFilterStructure(intl); const filterStructure = createFilterStructure(intl, filterOpts);
const columns: ColumnPickerChoice[] = [ const columns: ColumnPickerChoice[] = [
{ {

13
src/products/types.ts Normal file
View file

@ -0,0 +1,13 @@
import { FilterOpts, MinMax } from "@saleor/types";
import { StockAvailability } from "@saleor/types/globalTypes";
export enum ProductStatus {
PUBLISHED = "published",
HIDDEN = "hidden"
}
export interface ProductListFilterOpts {
price: FilterOpts<MinMax>;
status: FilterOpts<ProductStatus>;
stockStatus: FilterOpts<StockAvailability>;
}

View file

@ -54,7 +54,8 @@ import {
getFilterVariables, getFilterVariables,
saveFilterTab, saveFilterTab,
ProductFilterKeys, ProductFilterKeys,
createFilterQueryParams createFilterQueryParams,
getFilterOpts
} from "./filters"; } from "./filters";
import { getSortQueryVariables } from "./sort"; import { getSortQueryVariables } from "./sort";
@ -234,6 +235,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
defaultSettings={ defaultSettings={
defaultListSettings[ListViews.PRODUCT_LIST] defaultListSettings[ListViews.PRODUCT_LIST]
} }
filterOpts={getFilterOpts(params)}
gridAttributes={maybe( gridAttributes={maybe(
() => () =>
attributes.data.grid.edges.map(edge => edge.node), attributes.data.grid.edges.map(edge => edge.node),

View file

@ -1,10 +1,11 @@
import { IntlShape } from "react-intl"; import { IntlShape } from "react-intl";
import { findInEnum, maybe } from "@saleor/misc"; import { maybe, findValueInEnum } from "@saleor/misc";
import { import {
createOptionsField, createOptionsField,
createPriceField createPriceField
} from "@saleor/utils/filters/fields"; } from "@saleor/utils/filters/fields";
import { ProductStatus, ProductListFilterOpts } from "@saleor/products/types";
import { IFilterElement, IFilter } from "../../../components/Filter"; import { IFilterElement, IFilter } from "../../../components/Filter";
import { import {
ProductFilterInput, ProductFilterInput,
@ -29,21 +30,42 @@ export enum ProductFilterKeys {
stock = "stock" stock = "stock"
} }
export enum ProductStatus { export function getFilterOpts(
PUBLISHED = "published", params: ProductListUrlFilters
HIDDEN = "hidden" ): ProductListFilterOpts {
return {
price: {
active: maybe(
() =>
[params.priceFrom, params.priceTo].some(field => field !== undefined),
false
),
value: {
max: maybe(() => params.priceTo, "0"),
min: maybe(() => params.priceFrom, "0")
}
},
status: {
active: maybe(() => params.status !== undefined, false),
value: maybe(() => findValueInEnum(params.status, ProductStatus))
},
stockStatus: {
active: maybe(() => params.stockStatus !== undefined, false),
value: maybe(() => findValueInEnum(params.stockStatus, StockAvailability))
}
};
} }
export function createFilterStructure( export function createFilterStructure(
intl: IntlShape, intl: IntlShape,
params: ProductListUrlFilters opts: ProductListFilterOpts
): IFilter<ProductFilterKeys> { ): IFilter<ProductFilterKeys> {
return [ return [
{ {
...createOptionsField( ...createOptionsField(
ProductFilterKeys.status, ProductFilterKeys.status,
intl.formatMessage(messages.visibility), intl.formatMessage(messages.visibility),
[ProductStatus.PUBLISHED], [opts.status.value],
false, false,
[ [
{ {
@ -56,13 +78,13 @@ export function createFilterStructure(
} }
] ]
), ),
active: maybe(() => params.status !== undefined, false) active: opts.status.active
}, },
{ {
...createOptionsField( ...createOptionsField(
ProductFilterKeys.stock, ProductFilterKeys.stock,
intl.formatMessage(messages.quantity), intl.formatMessage(messages.quantity),
[StockAvailability.IN_STOCK], [opts.stockStatus.value],
false, false,
[ [
{ {
@ -75,22 +97,15 @@ export function createFilterStructure(
} }
] ]
), ),
active: maybe(() => params.stockStatus !== undefined, false) active: opts.stockStatus.active
}, },
{ {
...createPriceField( ...createPriceField(
ProductFilterKeys.price, ProductFilterKeys.price,
intl.formatMessage(messages.price), intl.formatMessage(messages.price),
{ opts.price.value
max: maybe(() => params.priceTo, "0"),
min: maybe(() => params.priceFrom, "0")
}
), ),
active: maybe( active: opts.price.active
() =>
[params.priceFrom, params.priceTo].some(field => field !== undefined),
false
)
} }
]; ];
} }
@ -108,33 +123,41 @@ export function getFilterVariables(
lte: parseFloat(params.priceTo) lte: parseFloat(params.priceTo)
}, },
search: params.query, search: params.query,
stockAvailability: StockAvailability[params.stockStatus] stockAvailability:
params.stockStatus !== undefined
? findValueInEnum(params.stockStatus, StockAvailability)
: null
}; };
} }
export function getFilterQueryParam( export function getFilterQueryParam(
filter: IFilterElement<ProductFilterKeys> filter: IFilterElement<ProductFilterKeys>
): ProductListUrlFilters { ): ProductListUrlFilters {
const { active, name, value } = filter; const { active, multiple, name, value } = filter;
if (active) { if (active) {
switch (name) { switch (name) {
case ProductFilterKeys.price: case ProductFilterKeys.price:
if (multiple) {
return { return {
priceFrom: value[0], priceFrom: value[0],
priceTo: value[1] priceTo: value[1]
}; };
}
return {
priceFrom: value[0],
priceTo: value[0]
};
case ProductFilterKeys.status: case ProductFilterKeys.status:
return { return {
status: ( status: findValueInEnum(value[0], ProductStatus)
findInEnum(value[0], ProductStatus) === ProductStatus.PUBLISHED
).toString()
}; };
case ProductFilterKeys.stock: case ProductFilterKeys.stock:
return { return {
status: findInEnum(value[0], StockAvailability) stockStatus: findValueInEnum(value[0], StockAvailability)
}; };
} }
} }

View file

@ -82,20 +82,16 @@ export interface SearchPageProps {
initialSearch: string; initialSearch: string;
onSearchChange: (value: string) => void; onSearchChange: (value: string) => void;
} }
export interface FilterPageProps<TKeys extends string> export interface FilterPageProps<TKeys extends string, TOpts extends object>
extends SearchPageProps, extends FilterProps<TKeys>,
SearchPageProps,
TabPageProps { TabPageProps {
currencySymbol: string; filterOpts: TOpts;
onFilterChange: (filter: IFilter<TKeys>) => void;
} }
export interface SearchProps { export interface FilterProps<TKeys extends string> {
searchPlaceholder: string;
}
export interface FilterProps<TKeys extends string>
extends FilterPageProps<TKeys>,
SearchProps {
currencySymbol: string; currencySymbol: string;
onFilterChange: (filter: IFilter<TKeys>) => void;
} }
export interface TabPageProps { export interface TabPageProps {
@ -170,3 +166,10 @@ export interface UserPermissionProps {
export interface MutationResultAdditionalProps { export interface MutationResultAdditionalProps {
status: ConfirmButtonTransitionState; status: ConfirmButtonTransitionState;
} }
export type MinMax = Record<"min" | "max", string>;
export interface FilterOpts<T> {
active: boolean;
value: T;
}

View file

@ -1,7 +1,6 @@
import { IFilterElement, FieldType } from "@saleor/components/Filter"; import { IFilterElement, FieldType } from "@saleor/components/Filter";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { MinMax } from "@saleor/types";
type MinMax = Record<"min" | "max", string>;
export function createPriceField<T extends string>( export function createPriceField<T extends string>(
name: T, name: T,
@ -26,7 +25,7 @@ export function createDateField<T extends string>(
return { return {
active: false, active: false,
label, label,
multiple: true, multiple: defaultValue.min !== defaultValue.max,
name, name,
type: FieldType.date, type: FieldType.date,
value: [defaultValue.min, defaultValue.max] value: [defaultValue.min, defaultValue.max]