Add search to attribute list

This commit is contained in:
dominik-zeglen 2019-09-10 17:32:47 +02:00
parent 249647cafa
commit dde0ec06d2
8 changed files with 191 additions and 10 deletions

View file

@ -4,19 +4,37 @@ 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 SearchBar from "@saleor/components/SearchBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import Container from "../../../components/Container"; import Container from "../../../components/Container";
import PageHeader from "../../../components/PageHeader"; import PageHeader from "../../../components/PageHeader";
import { ListActions, PageListProps } from "../../../types"; import {
ListActions,
PageListProps,
SearchPageProps,
TabPageProps
} from "../../../types";
import { AttributeList_attributes_edges_node } from "../../types/AttributeList"; import { AttributeList_attributes_edges_node } from "../../types/AttributeList";
import AttributeList from "../AttributeList/AttributeList"; import AttributeList from "../AttributeList/AttributeList";
export interface AttributeListPageProps extends PageListProps, ListActions { export interface AttributeListPageProps
extends PageListProps,
ListActions,
SearchPageProps,
TabPageProps {
attributes: AttributeList_attributes_edges_node[]; attributes: AttributeList_attributes_edges_node[];
} }
const AttributeListPage: React.FC<AttributeListPageProps> = ({ const AttributeListPage: React.FC<AttributeListPageProps> = ({
onAdd, onAdd,
initialSearch,
onSearchChange,
currentTab,
onAll,
onTabChange,
onTabDelete,
onTabSave,
tabs,
...listProps ...listProps
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@ -32,6 +50,19 @@ const AttributeListPage: React.FC<AttributeListPageProps> = ({
</Button> </Button>
</PageHeader> </PageHeader>
<Card> <Card>
<SearchBar
currentTab={currentTab}
initialSearch={initialSearch}
searchPlaceholder={intl.formatMessage({
defaultMessage: "Search Attribute"
})}
tabs={tabs}
onAll={onAll}
onSearchChange={onSearchChange}
onTabChange={onTabChange}
onTabDelete={onTabDelete}
onTabSave={onTabSave}
/>
<AttributeList {...listProps} /> <AttributeList {...listProps} />
</Card> </Card>
</Container> </Container>

View file

@ -52,7 +52,7 @@ const attributeList = gql`
${attributeFragment} ${attributeFragment}
${pageInfoFragment} ${pageInfoFragment}
query AttributeList( query AttributeList(
$query: String $filter: AttributeFilterInput
$inCategory: ID $inCategory: ID
$inCollection: ID $inCollection: ID
$before: String $before: String
@ -61,7 +61,7 @@ const attributeList = gql`
$last: Int $last: Int
) { ) {
attributes( attributes(
query: $query filter: $filter
inCategory: $inCategory inCategory: $inCategory
inCollection: $inCollection inCollection: $inCollection
before: $before before: $before

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 { AttributeFilterInput } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: AttributeList // GraphQL query operation: AttributeList
// ==================================================== // ====================================================
@ -40,7 +42,7 @@ export interface AttributeList {
} }
export interface AttributeListVariables { export interface AttributeListVariables {
query?: string | null; filter?: AttributeFilterInput | null;
inCategory?: string | null; inCategory?: string | null;
inCollection?: string | null; inCollection?: string | null;
before?: string | null; before?: string | null;

View file

@ -1,12 +1,26 @@
import { stringify as stringifyQs } from "qs"; import { stringify as stringifyQs } from "qs";
import urlJoin from "url-join"; import urlJoin from "url-join";
import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; import {
ActiveTab,
BulkAction,
Dialog,
Filters,
Pagination,
SingleAction,
TabActionDialog
} from "../types";
export const attributeSection = "/attributes/"; export const attributeSection = "/attributes/";
export type AttributeListUrlDialog = "remove"; export enum AttributeListUrlFiltersEnum {
export type AttributeListUrlQueryParams = BulkAction & query = "query"
}
export type AttributeListUrlFilters = Filters<AttributeListUrlFiltersEnum>;
export type AttributeListUrlDialog = "remove" | TabActionDialog;
export type AttributeListUrlQueryParams = ActiveTab &
AttributeListUrlFilters &
BulkAction &
Dialog<AttributeListUrlDialog> & Dialog<AttributeListUrlDialog> &
Pagination; Pagination;
export const attributeListPath = attributeSection; export const attributeListPath = attributeSection;

View file

@ -3,6 +3,18 @@ import DeleteIcon from "@material-ui/icons/Delete";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import {
areFiltersApplied,
deleteFilterTab,
getActiveFilters,
getFilterTabs,
saveFilterTab,
getFilterVariables
} from "@saleor/attributes/views/AttributeList/filters";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, { import usePaginator, {
@ -20,6 +32,7 @@ import {
attributeAddUrl, attributeAddUrl,
attributeListUrl, attributeListUrl,
AttributeListUrlDialog, AttributeListUrlDialog,
AttributeListUrlFilters,
AttributeListUrlQueryParams, AttributeListUrlQueryParams,
attributeUrl attributeUrl
} from "../../urls"; } from "../../urls";
@ -37,6 +50,15 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
); );
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 closeModal = () => const closeModal = () =>
navigate( navigate(
attributeListUrl({ attributeListUrl({
@ -56,8 +78,46 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
}) })
); );
const changeFilterField = (filter: AttributeListUrlFilters) => {
reset();
navigate(
attributeListUrl({
...getActiveFilters(params),
...filter,
activeTab: undefined
})
);
};
const handleTabChange = (tab: number) => {
reset();
navigate(
attributeListUrl({
activeTab: tab.toString(),
...getFilterTabs()[tab - 1].data
})
);
};
const handleTabDelete = () => {
deleteFilterTab(currentTab);
reset();
navigate(attributeListUrl());
};
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
saveFilterTab(data.name, getActiveFilters(params));
handleTabChange(tabs.length + 1);
};
const paginationState = createPaginationState(PAGINATE_BY, params); const paginationState = createPaginationState(PAGINATE_BY, params);
const queryVariables = React.useMemo(() => paginationState, [params]); const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
return ( return (
<AttributeListQuery variables={queryVariables}> <AttributeListQuery variables={queryVariables}>
@ -99,14 +159,22 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
attributes={maybe(() => attributes={maybe(() =>
data.attributes.edges.map(edge => edge.node) data.attributes.edges.map(edge => edge.node)
)} )}
currentTab={currentTab}
disabled={loading || attributeBulkDeleteOpts.loading} disabled={loading || attributeBulkDeleteOpts.loading}
initialSearch={params.query || ""}
isChecked={isSelected} isChecked={isSelected}
onAdd={() => navigate(attributeAddUrl())} onAdd={() => navigate(attributeAddUrl())}
onAll={() => navigate(attributeListUrl())}
onNextPage={loadNextPage} onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage} onPreviousPage={loadPreviousPage}
onRowClick={id => () => navigate(attributeUrl(id))} onRowClick={id => () => navigate(attributeUrl(id))}
onSearchChange={query => changeFilterField({ query })}
onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")}
pageInfo={pageInfo} pageInfo={pageInfo}
selected={listElements.length} selected={listElements.length}
tabs={tabs.map(tab => tab.name)}
toggle={toggle} toggle={toggle}
toggleAll={toggleAll} toggleAll={toggleAll}
toolbar={ toolbar={
@ -130,6 +198,19 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
onClose={closeModal} onClose={closeModal}
quantity={maybe(() => params.ids.length)} quantity={maybe(() => params.ids.length)}
/> />
<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 { AttributeFilterInput } from "@saleor/types/globalTypes";
import {
createFilterTabUtils,
createFilterUtils
} from "../../../utils/filters";
import {
AttributeListUrlFilters,
AttributeListUrlFiltersEnum,
AttributeListUrlQueryParams
} from "../../urls";
export const PRODUCT_FILTERS_KEY = "productFilters";
export function getFilterVariables(
params: AttributeListUrlFilters
): AttributeFilterInput {
return {
search: params.query
};
}
export const {
deleteFilterTab,
getFilterTabs,
saveFilterTab
} = createFilterTabUtils<AttributeListUrlFilters>(PRODUCT_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
AttributeListUrlQueryParams,
AttributeListUrlFilters
>(AttributeListUrlFiltersEnum);

View file

@ -5,12 +5,19 @@ import AttributeListPage, {
AttributeListPageProps AttributeListPageProps
} from "@saleor/attributes/components/AttributeListPage"; } from "@saleor/attributes/components/AttributeListPage";
import { attributes } from "@saleor/attributes/fixtures"; import { attributes } from "@saleor/attributes/fixtures";
import { listActionsProps, pageListProps } from "@saleor/fixtures"; import {
listActionsProps,
pageListProps,
searchPageProps,
tabPageProps
} from "@saleor/fixtures";
import Decorator from "../../Decorator"; import Decorator from "../../Decorator";
const props: AttributeListPageProps = { const props: AttributeListPageProps = {
...pageListProps.default, ...pageListProps.default,
...listActionsProps, ...listActionsProps,
...tabPageProps,
...searchPageProps,
attributes attributes
}; };

View file

@ -168,6 +168,7 @@ export enum PermissionEnum {
MANAGE_PAGES = "MANAGE_PAGES", MANAGE_PAGES = "MANAGE_PAGES",
MANAGE_PLUGINS = "MANAGE_PLUGINS", MANAGE_PLUGINS = "MANAGE_PLUGINS",
MANAGE_PRODUCTS = "MANAGE_PRODUCTS", MANAGE_PRODUCTS = "MANAGE_PRODUCTS",
MANAGE_SERVICE_ACCOUNTS = "MANAGE_SERVICE_ACCOUNTS",
MANAGE_SETTINGS = "MANAGE_SETTINGS", MANAGE_SETTINGS = "MANAGE_SETTINGS",
MANAGE_SHIPPING = "MANAGE_SHIPPING", MANAGE_SHIPPING = "MANAGE_SHIPPING",
MANAGE_STAFF = "MANAGE_STAFF", MANAGE_STAFF = "MANAGE_STAFF",
@ -264,6 +265,17 @@ export interface AttributeCreateInput {
availableInGrid?: boolean | null; availableInGrid?: boolean | null;
} }
export interface AttributeFilterInput {
valueRequired?: boolean | null;
isVariantOnly?: boolean | null;
visibleInStorefront?: boolean | null;
filterableInStorefront?: boolean | null;
filterableInDashboard?: boolean | null;
availableInGrid?: boolean | null;
search?: string | null;
ids?: (string | null)[] | null;
}
export interface AttributeInput { export interface AttributeInput {
slug: string; slug: string;
value: string; value: string;
@ -431,6 +443,7 @@ export interface OrderFilterInput {
status?: (OrderStatusFilter | null)[] | null; status?: (OrderStatusFilter | null)[] | null;
customer?: string | null; customer?: string | null;
created?: DateRangeInput | null; created?: DateRangeInput | null;
search?: string | null;
} }
export interface OrderLineCreateInput { export interface OrderLineCreateInput {
@ -577,6 +590,7 @@ export interface StaffCreateInput {
note?: string | null; note?: string | null;
permissions?: (PermissionEnum | null)[] | null; permissions?: (PermissionEnum | null)[] | null;
sendPasswordEmail?: boolean | null; sendPasswordEmail?: boolean | null;
redirectUrl?: string | null;
} }
export interface StaffInput { export interface StaffInput {
@ -605,6 +619,7 @@ export interface UserCreateInput {
isActive?: boolean | null; isActive?: boolean | null;
note?: string | null; note?: string | null;
sendPasswordEmail?: boolean | null; sendPasswordEmail?: boolean | null;
redirectUrl?: string | null;
} }
export interface VoucherInput { export interface VoucherInput {