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

View file

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

View file

@ -2,6 +2,8 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeFilterInput } from "./../../types/globalTypes";
// ====================================================
// GraphQL query operation: AttributeList
// ====================================================
@ -40,7 +42,7 @@ export interface AttributeList {
}
export interface AttributeListVariables {
query?: string | null;
filter?: AttributeFilterInput | null;
inCategory?: string | null;
inCollection?: string | null;
before?: string | null;

View file

@ -1,12 +1,26 @@
import { stringify as stringifyQs } from "qs";
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 type AttributeListUrlDialog = "remove";
export type AttributeListUrlQueryParams = BulkAction &
export enum AttributeListUrlFiltersEnum {
query = "query"
}
export type AttributeListUrlFilters = Filters<AttributeListUrlFiltersEnum>;
export type AttributeListUrlDialog = "remove" | TabActionDialog;
export type AttributeListUrlQueryParams = ActiveTab &
AttributeListUrlFilters &
BulkAction &
Dialog<AttributeListUrlDialog> &
Pagination;
export const attributeListPath = attributeSection;

View file

@ -3,6 +3,18 @@ import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
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 useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, {
@ -20,6 +32,7 @@ import {
attributeAddUrl,
attributeListUrl,
AttributeListUrlDialog,
AttributeListUrlFilters,
AttributeListUrlQueryParams,
attributeUrl
} from "../../urls";
@ -37,6 +50,15 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
);
const intl = useIntl();
const tabs = getFilterTabs();
const currentTab =
params.activeTab === undefined
? areFiltersApplied(params)
? tabs.length + 1
: 0
: parseInt(params.activeTab, 0);
const closeModal = () =>
navigate(
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 queryVariables = React.useMemo(() => paginationState, [params]);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
return (
<AttributeListQuery variables={queryVariables}>
@ -99,14 +159,22 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
attributes={maybe(() =>
data.attributes.edges.map(edge => edge.node)
)}
currentTab={currentTab}
disabled={loading || attributeBulkDeleteOpts.loading}
initialSearch={params.query || ""}
isChecked={isSelected}
onAdd={() => navigate(attributeAddUrl())}
onAll={() => navigate(attributeListUrl())}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onRowClick={id => () => navigate(attributeUrl(id))}
onSearchChange={query => changeFilterField({ query })}
onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")}
pageInfo={pageInfo}
selected={listElements.length}
tabs={tabs.map(tab => tab.name)}
toggle={toggle}
toggleAll={toggleAll}
toolbar={
@ -130,6 +198,19 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
onClose={closeModal}
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
} from "@saleor/attributes/components/AttributeListPage";
import { attributes } from "@saleor/attributes/fixtures";
import { listActionsProps, pageListProps } from "@saleor/fixtures";
import {
listActionsProps,
pageListProps,
searchPageProps,
tabPageProps
} from "@saleor/fixtures";
import Decorator from "../../Decorator";
const props: AttributeListPageProps = {
...pageListProps.default,
...listActionsProps,
...tabPageProps,
...searchPageProps,
attributes
};

View file

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