Add filtering to collections

This commit is contained in:
dominik-zeglen 2020-01-07 14:34:45 +01:00
parent 223237d0fd
commit b4cb44834c
7 changed files with 158 additions and 14 deletions

View file

@ -5,34 +5,42 @@ 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 SearchBar from "@saleor/components/SearchBar"; import FilterBar from "@saleor/components/FilterBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { import {
ListActions, ListActions,
PageListProps, PageListProps,
SearchPageProps, FilterPageProps,
TabPageProps, TabPageProps,
SortPage SortPage
} from "@saleor/types"; } from "@saleor/types";
import { CollectionListUrlSortField } from "@saleor/collections/urls"; import { CollectionListUrlSortField } from "@saleor/collections/urls";
import {
CollectionFilterKeys,
createFilterStructure
} from "@saleor/collections/views/CollectionList/filter";
import { CollectionListFilterOpts } from "@saleor/collections/types";
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";
export interface CollectionListPageProps export interface CollectionListPageProps
extends PageListProps, extends PageListProps,
ListActions, ListActions,
SearchPageProps, FilterPageProps<CollectionFilterKeys, CollectionListFilterOpts>,
SortPage<CollectionListUrlSortField>, SortPage<CollectionListUrlSortField>,
TabPageProps { TabPageProps {
collections: CollectionList_collections_edges_node[]; collections: CollectionList_collections_edges_node[];
} }
const CollectionListPage: React.FC<CollectionListPageProps> = ({ const CollectionListPage: React.FC<CollectionListPageProps> = ({
currencySymbol,
currentTab, currentTab,
disabled, disabled,
filterOpts,
initialSearch, initialSearch,
onAdd, onAdd,
onAll, onAll,
onFilterChange,
onSearchChange, onSearchChange,
onTabChange, onTabChange,
onTabDelete, onTabDelete,
@ -42,6 +50,8 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const structure = createFilterStructure(intl, filterOpts);
return ( return (
<Container> <Container>
<PageHeader title={intl.formatMessage(sectionNames.collections)}> <PageHeader title={intl.formatMessage(sectionNames.collections)}>
@ -58,18 +68,21 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
</Button> </Button>
</PageHeader> </PageHeader>
<Card> <Card>
<SearchBar <FilterBar
allTabLabel={intl.formatMessage({ allTabLabel={intl.formatMessage({
defaultMessage: "All Collections", defaultMessage: "All Collections",
description: "tab name" description: "tab name"
})} })}
currencySymbol={currencySymbol}
currentTab={currentTab} currentTab={currentTab}
filterStructure={structure}
initialSearch={initialSearch} initialSearch={initialSearch}
searchPlaceholder={intl.formatMessage({ searchPlaceholder={intl.formatMessage({
defaultMessage: "Search Collection" defaultMessage: "Search Collection"
})} })}
tabs={tabs} tabs={tabs}
onAll={onAll} onAll={onAll}
onFilterChange={onFilterChange}
onSearchChange={onSearchChange} onSearchChange={onSearchChange}
onTabChange={onTabChange} onTabChange={onTabChange}
onTabDelete={onTabDelete} onTabDelete={onTabDelete}

6
src/collections/types.ts Normal file
View file

@ -0,0 +1,6 @@
import { FilterOpts } from "@saleor/types";
import { CollectionPublished } from "@saleor/types/globalTypes";
export interface CollectionListFilterOpts {
status: FilterOpts<CollectionPublished>;
}

View file

@ -15,6 +15,7 @@ const collectionSectionUrl = "/collections/";
export const collectionListPath = collectionSectionUrl; export const collectionListPath = collectionSectionUrl;
export enum CollectionListUrlFiltersEnum { export enum CollectionListUrlFiltersEnum {
status = "status",
query = "query" query = "query"
} }
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>; export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;

View file

@ -23,6 +23,9 @@ import { ListViews } from "@saleor/types";
import { getSortParams } from "@saleor/utils/sort"; import { getSortParams } from "@saleor/utils/sort";
import createSortHandler from "@saleor/utils/handlers/sortHandler"; import createSortHandler from "@saleor/utils/handlers/sortHandler";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { IFilter } from "@saleor/components/Filter";
import { getFilterQueryParams } from "@saleor/utils/filters";
import useShop from "@saleor/hooks/useShop";
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage"; import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
import { import {
TypedCollectionBulkDelete, TypedCollectionBulkDelete,
@ -45,7 +48,10 @@ import {
getActiveFilters, getActiveFilters,
getFilterTabs, getFilterTabs,
getFilterVariables, getFilterVariables,
saveFilterTab saveFilterTab,
CollectionFilterKeys,
getFilterQueryParam,
getFilterOpts
} from "./filter"; } from "./filter";
import { getSortQueryVariables } from "./sort"; import { getSortQueryVariables } from "./sort";
@ -57,6 +63,7 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); const notify = useNotifier();
const paginate = usePaginator(); const paginate = usePaginator();
const shop = useShop();
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
params.ids params.ids
); );
@ -88,17 +95,38 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
: 0 : 0
: parseInt(params.activeTab, 0); : parseInt(params.activeTab, 0);
const changeFilterField = (filter: CollectionListUrlFilters) => { const changeFilters = (filter: IFilter<CollectionFilterKeys>) => {
reset(); reset();
navigate( navigate(
collectionListUrl({ collectionListUrl({
...getActiveFilters(params), ...params,
...filter, ...getFilterQueryParams(filter, getFilterQueryParam),
activeTab: undefined activeTab: undefined
}) })
); );
}; };
const resetFilters = () => {
reset();
navigate(
collectionListUrl({
asc: params.asc,
sort: params.sort
})
);
};
const handleSearchChange = (query: string) => {
reset();
navigate(
collectionListUrl({
...params,
activeTab: undefined,
query
})
);
};
const [openModal, closeModal] = createDialogActionHandlers< const [openModal, closeModal] = createDialogActionHandlers<
CollectionListUrlDialog, CollectionListUrlDialog,
CollectionListUrlQueryParams CollectionListUrlQueryParams
@ -154,6 +182,7 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
}; };
const handleSort = createSortHandler(navigate, collectionListUrl, params); const handleSort = createSortHandler(navigate, collectionListUrl, params);
const currencySymbol = maybe(() => shop.defaultCurrency, "USD");
return ( return (
<TypedCollectionBulkDelete onCompleted={handleCollectionBulkDelete}> <TypedCollectionBulkDelete onCompleted={handleCollectionBulkDelete}>
@ -162,11 +191,14 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
{(collectionBulkPublish, collectionBulkPublishOpts) => ( {(collectionBulkPublish, collectionBulkPublishOpts) => (
<> <>
<CollectionListPage <CollectionListPage
currencySymbol={currencySymbol}
currentTab={currentTab} currentTab={currentTab}
filterOpts={getFilterOpts(params)}
initialSearch={params.query || ""} initialSearch={params.query || ""}
onSearchChange={query => changeFilterField({ query })} onSearchChange={handleSearchChange}
onFilterChange={changeFilters}
onAdd={() => navigate(collectionAddUrl)} onAdd={() => navigate(collectionAddUrl)}
onAll={() => navigate(collectionListUrl())} onAll={resetFilters}
onTabChange={handleTabChange} onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")} onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")} onTabSave={() => openModal("save-search")}

View file

@ -1,24 +1,99 @@
import { CollectionFilterInput } from "@saleor/types/globalTypes"; import { IntlShape } from "react-intl";
import { import {
createFilterTabUtils, CollectionFilterInput,
createFilterUtils CollectionPublished
} from "../../../utils/filters"; } from "@saleor/types/globalTypes";
import { IFilterElement, IFilter } from "@saleor/components/Filter";
import { maybe, findValueInEnum } from "@saleor/misc";
import { createOptionsField } from "@saleor/utils/filters/fields";
import { commonMessages } from "@saleor/intl";
import { CollectionListFilterOpts } from "../../types";
import { import {
CollectionListUrlFilters, CollectionListUrlFilters,
CollectionListUrlFiltersEnum, CollectionListUrlFiltersEnum,
CollectionListUrlQueryParams CollectionListUrlQueryParams
} from "../../urls"; } from "../../urls";
import {
createFilterTabUtils,
createFilterUtils
} from "../../../utils/filters";
import messages from "./messages";
export const COLLECTION_FILTERS_KEY = "collectionFilters"; export const COLLECTION_FILTERS_KEY = "collectionFilters";
export enum CollectionFilterKeys {
status = "status"
}
export function getFilterOpts(
params: CollectionListUrlFilters
): CollectionListFilterOpts {
return {
status: {
active: maybe(() => params.status !== undefined, false),
value: maybe(() => findValueInEnum(status, CollectionPublished))
}
};
}
export function createFilterStructure(
intl: IntlShape,
opts: CollectionListFilterOpts
): IFilter<CollectionFilterKeys> {
return [
{
...createOptionsField(
CollectionFilterKeys.status,
intl.formatMessage(commonMessages.status),
[opts.status.value],
false,
[
{
label: intl.formatMessage(messages.published),
value: CollectionPublished.PUBLISHED
},
{
label: intl.formatMessage(messages.hidden),
value: CollectionPublished.HIDDEN
}
]
),
active: opts.status.active
}
];
}
export function getFilterVariables( export function getFilterVariables(
params: CollectionListUrlFilters params: CollectionListUrlFilters
): CollectionFilterInput { ): CollectionFilterInput {
return { return {
published: params.status
? findValueInEnum(params.status, CollectionPublished)
: undefined,
search: params.query search: params.query
}; };
} }
export function getFilterQueryParam(
filter: IFilterElement<CollectionFilterKeys>
): CollectionListUrlFilters {
const { active, name, value } = filter;
switch (name) {
case CollectionFilterKeys.status:
if (!active) {
return {
status: undefined
};
}
return {
status: value[0]
};
}
}
export const { export const {
deleteFilterTab, deleteFilterTab,
getFilterTabs, getFilterTabs,

View file

@ -0,0 +1,14 @@
import { defineMessages } from "react-intl";
const messages = defineMessages({
hidden: {
defaultMessage: "Hidden",
description: "collection"
},
published: {
defaultMessage: "Published",
description: "collection"
}
});
export default messages;

View file

@ -65,6 +65,9 @@ export const commonMessages = defineMessages({
startHour: { startHour: {
defaultMessage: "Start Hour" defaultMessage: "Start Hour"
}, },
status: {
defaultMessage: "Status"
},
summary: { summary: {
defaultMessage: "Summary" defaultMessage: "Summary"
}, },