Add search bar to category list

This commit is contained in:
dominik-zeglen 2019-09-11 14:59:41 +02:00
parent b842e85759
commit 53ac48062f
13 changed files with 442 additions and 224 deletions

View file

@ -1,5 +1,3 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import { import {
createStyles, createStyles,
Theme, Theme,
@ -12,9 +10,9 @@ import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter"; import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import { CategoryFragment } from "@saleor/categories/types/CategoryFragment";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import TableHead from "@saleor/components/TableHead"; import TableHead from "@saleor/components/TableHead";
@ -49,20 +47,8 @@ const styles = (theme: Theme) =>
} }
}); });
interface CategoryListProps interface CategoryListProps extends ListProps, ListActions {
extends ListProps, categories?: CategoryFragment[];
ListActions,
WithStyles<typeof styles> {
categories?: Array<{
id: string;
name: string;
children: {
totalCount: number;
};
products: {
totalCount: number;
};
}>;
isRoot: boolean; isRoot: boolean;
onAdd?(); onAdd?();
} }
@ -75,144 +61,119 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
classes, classes,
disabled, disabled,
settings, settings,
isRoot,
pageInfo, pageInfo,
isChecked, isChecked,
isRoot,
selected, selected,
toggle, toggle,
toggleAll, toggleAll,
toolbar, toolbar,
onAdd,
onNextPage, onNextPage,
onPreviousPage, onPreviousPage,
onUpdateListSettings, onUpdateListSettings,
onRowClick onRowClick
}: CategoryListProps) => { }: CategoryListProps & WithStyles<typeof styles>) => (
const intl = useIntl(); <Table>
<TableHead
return ( colSpan={numberOfColumns}
<Card> selected={selected}
{!isRoot && ( disabled={disabled}
<CardTitle items={categories}
title={intl.formatMessage({ toggleAll={toggleAll}
defaultMessage: "All Subcategories", toolbar={toolbar}
description: "section header" >
})} <TableCell className={classes.colName}>
toolbar={ <FormattedMessage defaultMessage="Category Name" />
<Button color="primary" variant="text" onClick={onAdd}> </TableCell>
<FormattedMessage <TableCell className={classes.colSubcategories}>
defaultMessage="Create subcategory" <FormattedMessage
description="button" defaultMessage="Subcategories"
/> description="number of subcategories"
</Button>
}
/> />
)} </TableCell>
<Table> <TableCell className={classes.colProducts}>
<TableHead <FormattedMessage
defaultMessage="No. of Products"
description="number of products"
/>
</TableCell>
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns} colSpan={numberOfColumns}
selected={selected} settings={settings}
disabled={disabled} hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
items={categories} onNextPage={onNextPage}
toggleAll={toggleAll} onUpdateListSettings={onUpdateListSettings}
toolbar={toolbar} hasPreviousPage={
> pageInfo && !disabled ? pageInfo.hasPreviousPage : false
<TableCell className={classes.colName}> }
<FormattedMessage defaultMessage="Category Name" /> onPreviousPage={onPreviousPage}
</TableCell> />
<TableCell className={classes.colSubcategories}> </TableRow>
<FormattedMessage </TableFooter>
defaultMessage="Subcategories" <TableBody>
description="number of subcategories" {renderCollection(
/> categories,
</TableCell> category => {
<TableCell className={classes.colProducts}> const isSelected = category ? isChecked(category.id) : false;
<FormattedMessage
defaultMessage="No. of Products"
description="number of products"
/>
</TableCell>
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
settings={settings}
hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage}
onUpdateListSettings={onUpdateListSettings}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
categories,
category => {
const isSelected = category ? isChecked(category.id) : false;
return ( return (
<TableRow <TableRow
className={classes.tableRow} className={classes.tableRow}
hover={!!category} hover={!!category}
onClick={category ? onRowClick(category.id) : undefined} onClick={category ? onRowClick(category.id) : undefined}
key={category ? category.id : "skeleton"} key={category ? category.id : "skeleton"}
selected={isSelected} selected={isSelected}
> >
<TableCell padding="checkbox"> <TableCell padding="checkbox">
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
disabled={disabled} disabled={disabled}
disableClickPropagation disableClickPropagation
onChange={() => toggle(category.id)} onChange={() => toggle(category.id)}
/> />
</TableCell> </TableCell>
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
{category && category.name ? category.name : <Skeleton />} {category && category.name ? category.name : <Skeleton />}
</TableCell> </TableCell>
<TableCell className={classes.colSubcategories}> <TableCell className={classes.colSubcategories}>
{category && {category &&
category.children && category.children &&
category.children.totalCount !== undefined ? ( category.children.totalCount !== undefined ? (
category.children.totalCount category.children.totalCount
) : ( ) : (
<Skeleton /> <Skeleton />
)} )}
</TableCell> </TableCell>
<TableCell className={classes.colProducts}> <TableCell className={classes.colProducts}>
{category && {category &&
category.products && category.products &&
category.products.totalCount !== undefined ? ( category.products.totalCount !== undefined ? (
category.products.totalCount category.products.totalCount
) : ( ) : (
<Skeleton /> <Skeleton />
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
}, },
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{isRoot ? ( {isRoot ? (
<FormattedMessage defaultMessage="No categories found" /> <FormattedMessage defaultMessage="No categories found" />
) : ( ) : (
<FormattedMessage defaultMessage="No subcategories found" /> <FormattedMessage defaultMessage="No subcategories found" />
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
)} )}
</TableBody> </TableBody>
</Table> </Table>
</Card> )
);
}
); );
CategoryList.displayName = "CategoryList"; CategoryList.displayName = "CategoryList";
export default CategoryList; export default CategoryList;

View file

@ -1,44 +1,55 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { CategoryFragment } from "@saleor/categories/types/CategoryFragment";
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 { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { ListActions, PageListProps } from "@saleor/types"; import {
ListActions,
PageListProps,
SearchPageProps,
TabPageProps
} from "@saleor/types";
import CategoryList from "../CategoryList"; import CategoryList from "../CategoryList";
export interface CategoryTableProps extends PageListProps, ListActions { export interface CategoryTableProps
categories: Array<{ extends PageListProps,
id: string; ListActions,
name: string; SearchPageProps,
children: { TabPageProps {
totalCount: number; categories: CategoryFragment[];
};
products: {
totalCount: number;
};
}>;
} }
export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
categories, categories,
currentTab,
disabled, disabled,
settings, initialSearch,
onAdd,
onNextPage,
onPreviousPage,
onUpdateListSettings,
onRowClick,
pageInfo,
isChecked, isChecked,
pageInfo,
selected, selected,
settings,
tabs,
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar,
onAdd,
onAll,
onNextPage,
onPreviousPage,
onRowClick,
onSearchChange,
onTabChange,
onTabDelete,
onTabSave,
onUpdateListSettings
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
return ( return (
<Container> <Container>
<PageHeader title={intl.formatMessage(sectionNames.categories)}> <PageHeader title={intl.formatMessage(sectionNames.categories)}>
@ -49,23 +60,38 @@ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
/> />
</Button> </Button>
</PageHeader> </PageHeader>
<CategoryList <Card>
categories={categories} <SearchBar
onAdd={onAdd} currentTab={currentTab}
onRowClick={onRowClick} initialSearch={initialSearch}
disabled={disabled} searchPlaceholder={intl.formatMessage({
settings={settings} defaultMessage: "Search Attribute"
isRoot={true} })}
onNextPage={onNextPage} tabs={tabs}
onPreviousPage={onPreviousPage} onAll={onAll}
onUpdateListSettings={onUpdateListSettings} onSearchChange={onSearchChange}
pageInfo={pageInfo} onTabChange={onTabChange}
isChecked={isChecked} onTabDelete={onTabDelete}
selected={selected} onTabSave={onTabSave}
toggle={toggle} />
toggleAll={toggleAll} <CategoryList
toolbar={toolbar} categories={categories}
/> disabled={disabled}
isChecked={isChecked}
isRoot={true}
pageInfo={pageInfo}
selected={selected}
settings={settings}
toggle={toggle}
toggleAll={toggleAll}
toolbar={toolbar}
onAdd={onAdd}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onRowClick={onRowClick}
onUpdateListSettings={onUpdateListSettings}
/>
</Card>
</Container> </Container>
); );
}; };

View file

@ -1,9 +1,12 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import { RawDraftContentState } from "draft-js"; import { RawDraftContentState } from "draft-js";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader"; import AppHeader from "@saleor/components/AppHeader";
import { CardSpacer } from "@saleor/components/CardSpacer"; import { CardSpacer } from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
@ -178,21 +181,40 @@ export const CategoryUpdatePage: React.StatelessComponent<
</TabContainer> </TabContainer>
<CardSpacer /> <CardSpacer />
{currentTab === CategoryPageTab.categories && ( {currentTab === CategoryPageTab.categories && (
<CategoryList <Card>
disabled={disabled} <CardTitle
isRoot={false} title={intl.formatMessage({
categories={subcategories} defaultMessage: "All Subcategories",
onAdd={onAddCategory} description: "section header"
onRowClick={onCategoryClick} })}
onNextPage={onNextPage} toolbar={
onPreviousPage={onPreviousPage} <Button
pageInfo={pageInfo} color="primary"
toggle={toggle} variant="text"
toggleAll={toggleAll} onClick={onAddCategory}
selected={selected} >
isChecked={isChecked} <FormattedMessage
toolbar={subcategoryListToolbar} defaultMessage="Create subcategory"
/> description="button"
/>
</Button>
}
/>
<CategoryList
categories={subcategories}
disabled={disabled}
isChecked={isChecked}
isRoot={false}
pageInfo={pageInfo}
selected={selected}
toggle={toggle}
toggleAll={toggleAll}
toolbar={subcategoryListToolbar}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onRowClick={onCategoryClick}
/>
</Card>
)} )}
{currentTab === CategoryPageTab.products && ( {currentTab === CategoryPageTab.products && (
<CategoryProducts <CategoryProducts

View file

@ -1,64 +1,83 @@
import { content } from "../storybook/stories/components/RichTextEditor"; import { content } from "../storybook/stories/components/RichTextEditor";
import { CategoryDetails_category } from "./types/CategoryDetails"; import { CategoryDetails_category } from "./types/CategoryDetails";
import { CategoryFragment } from "./types/CategoryFragment";
export const categories = [ export const categories: CategoryFragment[] = [
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 2 totalCount: 2
}, },
id: "123123", id: "123123",
name: "Lorem ipsum dolor", name: "Lorem ipsum dolor",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 4 totalCount: 4
} }
}, },
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 54 totalCount: 54
}, },
id: "876752", id: "876752",
name: "Mauris vehicula tortor vulputate", name: "Mauris vehicula tortor vulputate",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 3 totalCount: 3
} }
}, },
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 2 totalCount: 2
}, },
id: "876542", id: "876542",
name: "Excepteur sint occaecat cupidatat non proident", name: "Excepteur sint occaecat cupidatat non proident",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 6 totalCount: 6
} }
}, },
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 6 totalCount: 6
}, },
id: "875352", id: "875352",
name: "Ut enim ad minim veniam", name: "Ut enim ad minim veniam",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 12 totalCount: 12
} }
}, },
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 76 totalCount: 76
}, },
id: "865752", id: "865752",
name: "Duis aute irure dolor in reprehenderit", name: "Duis aute irure dolor in reprehenderit",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 43 totalCount: 43
} }
}, },
{ {
__typename: "Category",
children: { children: {
__typename: "CategoryCountableConnection",
totalCount: 11 totalCount: 11
}, },
id: "878752", id: "878752",
name: "Neque porro quisquam est", name: "Neque porro quisquam est",
products: { products: {
__typename: "ProductCountableConnection",
totalCount: 21 totalCount: 21
} }
} }

View file

@ -7,6 +7,18 @@ import {
} from "./types/CategoryDetails"; } from "./types/CategoryDetails";
import { RootCategories } from "./types/RootCategories"; import { RootCategories } from "./types/RootCategories";
export const categoryFragment = gql`
fragment CategoryFragment on Category {
id
name
children {
totalCount
}
products {
totalCount
}
}
`;
export const categoryDetailsFragment = gql` export const categoryDetailsFragment = gql`
fragment CategoryDetailsFragment on Category { fragment CategoryDetailsFragment on Category {
id id
@ -25,11 +37,13 @@ export const categoryDetailsFragment = gql`
`; `;
export const rootCategories = gql` export const rootCategories = gql`
${categoryFragment}
query RootCategories( query RootCategories(
$first: Int $first: Int
$after: String $after: String
$last: Int $last: Int
$before: String $before: String
$filter: CategoryFilterInput
) { ) {
categories( categories(
level: 0 level: 0
@ -37,17 +51,11 @@ export const rootCategories = gql`
after: $after after: $after
last: $last last: $last
before: $before before: $before
filter: $filter
) { ) {
edges { edges {
node { node {
id ...CategoryFragment
name
children {
totalCount
}
products {
totalCount
}
} }
} }
pageInfo { pageInfo {
@ -64,6 +72,7 @@ export const TypedRootCategoriesQuery = TypedQuery<RootCategories, {}>(
); );
export const categoryDetails = gql` export const categoryDetails = gql`
${categoryFragment}
${categoryDetailsFragment} ${categoryDetailsFragment}
query CategoryDetails( query CategoryDetails(
$id: ID! $id: ID!
@ -77,14 +86,7 @@ export const categoryDetails = gql`
children(first: 20) { children(first: 20) {
edges { edges {
node { node {
id ...CategoryFragment
name
children {
totalCount
}
products {
totalCount
}
} }
} }
} }

View file

@ -0,0 +1,25 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL fragment: CategoryFragment
// ====================================================
export interface CategoryFragment_children {
__typename: "CategoryCountableConnection";
totalCount: number | null;
}
export interface CategoryFragment_products {
__typename: "ProductCountableConnection";
totalCount: number | null;
}
export interface CategoryFragment {
__typename: "Category";
id: string;
name: string;
children: CategoryFragment_children | null;
products: CategoryFragment_products | null;
}

View file

@ -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 { CategoryFilterInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: RootCategories // GraphQL query operation: RootCategories
// ==================================================== // ====================================================
@ -52,4 +54,5 @@ export interface RootCategoriesVariables {
after?: string | null; after?: string | null;
last?: number | null; last?: number | null;
before?: string | null; before?: string | null;
filter?: CategoryFilterInput | null;
} }

View file

@ -1,14 +1,27 @@
import { stringify as stringifyQs } from "qs"; import { stringify as stringifyQs } from "qs";
import urlJoin from "url-join"; import urlJoin from "url-join";
import { ActiveTab, BulkAction, Dialog, Pagination } from "../types"; import {
ActiveTab,
BulkAction,
Dialog,
Filters,
Pagination,
TabActionDialog
} from "../types";
import { CategoryPageTab } from "./components/CategoryUpdatePage"; import { CategoryPageTab } from "./components/CategoryUpdatePage";
const categorySectionUrl = "/categories/"; const categorySectionUrl = "/categories/";
export const categoryListPath = categorySectionUrl; export const categoryListPath = categorySectionUrl;
export type CategoryListUrlDialog = "delete"; export enum CategoryListUrlFiltersEnum {
export type CategoryListUrlQueryParams = BulkAction & query = "query"
}
export type CategoryListUrlFilters = Filters<CategoryListUrlFiltersEnum>;
export type CategoryListUrlDialog = "delete" | TabActionDialog;
export type CategoryListUrlQueryParams = ActiveTab &
BulkAction &
CategoryListUrlFilters &
Dialog<CategoryListUrlDialog> & Dialog<CategoryListUrlDialog> &
Pagination; Pagination;
export const categoryListUrl = (params?: CategoryListUrlQueryParams) => export const categoryListUrl = (params?: CategoryListUrlQueryParams) =>

View file

@ -5,6 +5,10 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useBulkActions from "@saleor/hooks/useBulkActions"; import useBulkActions from "@saleor/hooks/useBulkActions";
import useListSettings from "@saleor/hooks/useListSettings"; import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
@ -13,16 +17,26 @@ import usePaginator, {
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage"; import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
import { TypedCategoryBulkDeleteMutation } from "../mutations"; import { TypedCategoryBulkDeleteMutation } from "../../mutations";
import { TypedRootCategoriesQuery } from "../queries"; import { TypedRootCategoriesQuery } from "../../queries";
import { CategoryBulkDelete } from "../types/CategoryBulkDelete"; import { CategoryBulkDelete } from "../../types/CategoryBulkDelete";
import { import {
categoryAddUrl, categoryAddUrl,
categoryListUrl, categoryListUrl,
CategoryListUrlDialog,
CategoryListUrlFilters,
CategoryListUrlQueryParams, CategoryListUrlQueryParams,
categoryUrl categoryUrl
} from "../urls"; } from "../../urls";
import {
areFiltersApplied,
deleteFilterTab,
getActiveFilters,
getFilterTabs,
getFilterVariables,
saveFilterTab
} from "./filter";
interface CategoryListProps { interface CategoryListProps {
params: CategoryListUrlQueryParams; params: CategoryListUrlQueryParams;
@ -41,9 +55,77 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
); );
const intl = useIntl(); const intl = useIntl();
const tabs = getFilterTabs();
const currentTab =
params.activeTab === undefined
? areFiltersApplied(params)
? tabs.length + 1
: 0
: parseInt(params.activeTab, 0);
const changeFilterField = (filter: CategoryListUrlFilters) => {
reset();
navigate(
categoryListUrl({
...getActiveFilters(params),
...filter,
activeTab: undefined
})
);
};
const closeModal = () =>
navigate(
categoryListUrl({
...params,
action: undefined,
ids: undefined
}),
true
);
const openModal = (action: CategoryListUrlDialog, ids?: string[]) =>
navigate(
categoryListUrl({
...params,
action,
ids
})
);
const handleTabChange = (tab: number) => {
reset();
navigate(
categoryListUrl({
activeTab: tab.toString(),
...getFilterTabs()[tab - 1].data
})
);
};
const handleTabDelete = () => {
deleteFilterTab(currentTab);
reset();
navigate(categoryListUrl());
};
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
saveFilterTab(data.name, getActiveFilters(params));
handleTabChange(tabs.length + 1);
};
const paginationState = createPaginationState(settings.rowNumber, params); const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
return ( return (
<TypedRootCategoriesQuery displayLoader variables={paginationState}> <TypedRootCategoriesQuery displayLoader variables={queryVariables}>
{({ data, loading, refetch }) => { {({ data, loading, refetch }) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate( const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
maybe(() => data.categories.pageInfo), maybe(() => data.categories.pageInfo),
@ -78,6 +160,14 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
() => data.categories.edges.map(edge => edge.node), () => data.categories.edges.map(edge => edge.node),
[] []
)} )}
currentTab={currentTab}
initialSearch={params.query || ""}
onSearchChange={query => changeFilterField({ query })}
onAll={() => navigate(categoryListUrl())}
onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")}
tabs={tabs.map(tab => tab.name)}
settings={settings} settings={settings}
onAdd={() => navigate(categoryAddUrl())} onAdd={() => navigate(categoryAddUrl())}
onRowClick={id => () => navigate(categoryUrl(id))} onRowClick={id => () => navigate(categoryUrl(id))}
@ -134,7 +224,7 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
> >
<FormattedMessage <FormattedMessage
defaultMessage="Are you sure you want to delete {counter, plural, defaultMessage="Are you sure you want to delete {counter, plural,
one {this attribute} one {this category}
other {{displayQuantity} categories} other {{displayQuantity} categories}
}?" }?"
values={{ values={{
@ -148,6 +238,19 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." /> <FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
</DialogContentText> </DialogContentText>
</ActionDialog> </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, "...")}
/>
</> </>
); );
}} }}

View file

@ -0,0 +1,31 @@
import { CategoryFilterInput } from "@saleor/types/globalTypes";
import {
createFilterTabUtils,
createFilterUtils
} from "../../../utils/filters";
import {
CategoryListUrlFilters,
CategoryListUrlFiltersEnum,
CategoryListUrlQueryParams
} from "../../urls";
export const PRODUCT_FILTERS_KEY = "productFilters";
export function getFilterVariables(
params: CategoryListUrlFilters
): CategoryFilterInput {
return {
search: params.query
};
}
export const {
deleteFilterTab,
getFilterTabs,
saveFilterTab
} = createFilterTabUtils<CategoryListUrlFilters>(PRODUCT_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
CategoryListUrlQueryParams,
CategoryListUrlFilters
>(CategoryListUrlFiltersEnum);

View file

@ -0,0 +1,2 @@
export { default } from "./CategoryList";
export * from "./CategoryList";

View file

@ -3,7 +3,12 @@ import React from "react";
import CategoryListPage from "../../../categories/components/CategoryListPage"; import CategoryListPage from "../../../categories/components/CategoryListPage";
import { categories } from "../../../categories/fixtures"; import { categories } from "../../../categories/fixtures";
import { listActionsProps, pageListProps } from "../../../fixtures"; import {
listActionsProps,
pageListProps,
searchPageProps,
tabPageProps
} from "../../../fixtures";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const categoryTableProps = { const categoryTableProps = {
@ -11,6 +16,8 @@ const categoryTableProps = {
onAddCategory: undefined, onAddCategory: undefined,
onCategoryClick: () => undefined, onCategoryClick: () => undefined,
...listActionsProps, ...listActionsProps,
...tabPageProps,
...searchPageProps,
...pageListProps.default ...pageListProps.default
}; };

View file

@ -316,6 +316,10 @@ export interface CatalogueInput {
collections?: (string | null)[] | null; collections?: (string | null)[] | null;
} }
export interface CategoryFilterInput {
search?: string | null;
}
export interface CategoryInput { export interface CategoryInput {
description?: string | null; description?: string | null;
descriptionJson?: any | null; descriptionJson?: any | null;