diff --git a/src/discounts/components/VoucherList/VoucherList.tsx b/src/discounts/components/VoucherList/VoucherList.tsx index 50b4eaff7..4107c2794 100644 --- a/src/discounts/components/VoucherList/VoucherList.tsx +++ b/src/discounts/components/VoucherList/VoucherList.tsx @@ -1,4 +1,3 @@ -import Card from "@material-ui/core/Card"; import { createStyles, Theme, @@ -98,163 +97,155 @@ const VoucherList = withStyles(styles, { toggleAll, toolbar }: VoucherListProps & WithStyles) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {renderCollection( - vouchers, - voucher => { - const isSelected = voucher ? isChecked(voucher.id) : false; +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + {renderCollection( + vouchers, + voucher => { + const isSelected = voucher ? isChecked(voucher.id) : false; - return ( - + + toggle(voucher.id)} + /> + + + {maybe(() => voucher.code, )} + + + {voucher && voucher.minAmountSpent ? ( + + ) : voucher && voucher.minAmountSpent === null ? ( + "-" + ) : ( + + )} + + + {voucher && voucher.startDate ? ( + + ) : ( + + )} + + + {voucher && voucher.endDate ? ( + + ) : voucher && voucher.endDate === null ? ( + "-" + ) : ( + + )} + + - - toggle(voucher.id)} - /> - - - {maybe(() => voucher.code, )} - - - {voucher && voucher.minAmountSpent ? ( - - ) : voucher && voucher.minAmountSpent === null ? ( - "-" + {voucher && + voucher.discountValueType && + voucher.discountValue ? ( + voucher.discountValueType === + DiscountValueTypeEnum.FIXED ? ( + ) : ( - - )} - - - {voucher && voucher.startDate ? ( - - ) : ( - - )} - - - {voucher && voucher.endDate ? ( - - ) : voucher && voucher.endDate === null ? ( - "-" - ) : ( - - )} - - - {voucher && - voucher.discountValueType && - voucher.discountValue ? ( - voucher.discountValueType === - DiscountValueTypeEnum.FIXED ? ( - - ) : ( - - ) - ) : ( - - )} - - - {voucher && voucher.usageLimit ? ( - voucher.usageLimit - ) : voucher && voucher.usageLimit === null ? ( - "-" - ) : ( - - )} - - - ); - }, - () => ( - - - + + ) + ) : ( + + )} + + + {voucher && voucher.usageLimit ? ( + voucher.usageLimit + ) : voucher && voucher.usageLimit === null ? ( + "-" + ) : ( + + )} - ) - )} - -
-
+ ); + }, + () => ( + + + + + + ) + )} + + ) ); VoucherList.displayName = "VoucherList"; diff --git a/src/discounts/components/VoucherListPage/VoucherListPage.tsx b/src/discounts/components/VoucherListPage/VoucherListPage.tsx index 3b3fea014..4d32b59f2 100644 --- a/src/discounts/components/VoucherListPage/VoucherListPage.tsx +++ b/src/discounts/components/VoucherListPage/VoucherListPage.tsx @@ -1,36 +1,41 @@ import Button from "@material-ui/core/Button"; - +import Card from "@material-ui/core/Card"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; +import SearchBar from "@saleor/components/SearchBar"; import { sectionNames } from "@saleor/intl"; -import { ListActions, PageListProps } from "@saleor/types"; +import { + ListActions, + PageListProps, + SearchPageProps, + TabPageProps +} from "@saleor/types"; import { VoucherList_vouchers_edges_node } from "../../types/VoucherList"; import VoucherList from "../VoucherList"; -export interface VoucherListPageProps extends PageListProps, ListActions { +export interface VoucherListPageProps + extends PageListProps, + ListActions, + SearchPageProps, + TabPageProps { defaultCurrency: string; vouchers: VoucherList_vouchers_edges_node[]; } const VoucherListPage: React.StatelessComponent = ({ - defaultCurrency, - disabled, - settings, + currentTab, + initialSearch, onAdd, - onNextPage, - onPreviousPage, - onUpdateListSettings, - onRowClick, - pageInfo, - vouchers, - isChecked, - selected, - toggle, - toggleAll, - toolbar + onAll, + onSearchChange, + onTabChange, + onTabDelete, + onTabSave, + tabs, + ...listProps }) => { const intl = useIntl(); @@ -44,22 +49,26 @@ const VoucherListPage: React.StatelessComponent = ({ /> - + + + + ); }; diff --git a/src/discounts/queries.ts b/src/discounts/queries.ts index 9057b5b89..54e98a5bc 100644 --- a/src/discounts/queries.ts +++ b/src/discounts/queries.ts @@ -196,8 +196,20 @@ export const TypedSaleList = TypedQuery(saleList); export const voucherList = gql` ${pageInfoFragment} ${voucherFragment} - query VoucherList($after: String, $before: String, $first: Int, $last: Int) { - vouchers(after: $after, before: $before, first: $first, last: $last) { + query VoucherList( + $after: String + $before: String + $first: Int + $last: Int + $filter: VoucherFilterInput + ) { + vouchers( + after: $after + before: $before + first: $first + last: $last + filter: $filter + ) { edges { node { ...VoucherFragment diff --git a/src/discounts/types/VoucherList.ts b/src/discounts/types/VoucherList.ts index 9b421ef41..476dff395 100644 --- a/src/discounts/types/VoucherList.ts +++ b/src/discounts/types/VoucherList.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { DiscountValueTypeEnum } from "./../../types/globalTypes"; +import { VoucherFilterInput, DiscountValueTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: VoucherList @@ -62,4 +62,5 @@ export interface VoucherListVariables { before?: string | null; first?: number | null; last?: number | null; + filter?: VoucherFilterInput | null; } diff --git a/src/discounts/urls.ts b/src/discounts/urls.ts index 659449b7f..44877e748 100644 --- a/src/discounts/urls.ts +++ b/src/discounts/urls.ts @@ -48,10 +48,16 @@ export const saleAddUrl = saleAddPath; export const voucherSection = urlJoin(discountSection, "vouchers"); export const voucherListPath = voucherSection; -export type VoucherListUrlDialog = "remove"; -export type VoucherListUrlQueryParams = BulkAction & +export enum VoucherListUrlFiltersEnum { + query = "query" +} +export type VoucherListUrlFilters = Filters; +export type VoucherListUrlDialog = "remove" | TabActionDialog; +export type VoucherListUrlQueryParams = ActiveTab & + BulkAction & Dialog & - Pagination; + Pagination & + VoucherListUrlFilters; export const voucherListUrl = (params?: VoucherListUrlQueryParams) => voucherListPath + "?" + stringifyQs(params); export const voucherPath = (id: string) => urlJoin(voucherSection, id); diff --git a/src/discounts/views/VoucherList.tsx b/src/discounts/views/VoucherList/VoucherList.tsx similarity index 64% rename from src/discounts/views/VoucherList.tsx rename to src/discounts/views/VoucherList/VoucherList.tsx index e70830261..27b2bef39 100644 --- a/src/discounts/views/VoucherList.tsx +++ b/src/discounts/views/VoucherList/VoucherList.tsx @@ -5,6 +5,10 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import ActionDialog from "@saleor/components/ActionDialog"; +import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; +import SaveFilterTabDialog, { + SaveFilterTabDialogFormData +} from "@saleor/components/SaveFilterTabDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useListSettings from "@saleor/hooks/useListSettings"; @@ -17,16 +21,26 @@ import useShop from "@saleor/hooks/useShop"; import { commonMessages, sectionNames } from "@saleor/intl"; import { getMutationState, maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; -import VoucherListPage from "../components/VoucherListPage"; -import { TypedVoucherBulkDelete } from "../mutations"; -import { TypedVoucherList } from "../queries"; -import { VoucherBulkDelete } from "../types/VoucherBulkDelete"; +import VoucherListPage from "../../components/VoucherListPage"; +import { TypedVoucherBulkDelete } from "../../mutations"; +import { TypedVoucherList } from "../../queries"; +import { VoucherBulkDelete } from "../../types/VoucherBulkDelete"; import { voucherAddUrl, voucherListUrl, + VoucherListUrlDialog, + VoucherListUrlFilters, VoucherListUrlQueryParams, voucherUrl -} from "../urls"; +} from "../../urls"; +import { + areFiltersApplied, + deleteFilterTab, + getActiveFilters, + getFilterTabs, + getFilterVariables, + saveFilterTab +} from "./filter"; interface VoucherListProps { params: VoucherListUrlQueryParams; @@ -47,13 +61,78 @@ export const VoucherList: React.StatelessComponent = ({ ); const intl = useIntl(); - const closeModal = () => navigate(voucherListUrl(), true); + const tabs = getFilterTabs(); + + const currentTab = + params.activeTab === undefined + ? areFiltersApplied(params) + ? tabs.length + 1 + : 0 + : parseInt(params.activeTab, 0); + + const changeFilterField = (filter: VoucherListUrlFilters) => { + reset(); + navigate( + voucherListUrl({ + ...getActiveFilters(params), + ...filter, + activeTab: undefined + }) + ); + }; + + const closeModal = () => + navigate( + voucherListUrl({ + ...params, + action: undefined, + ids: undefined + }) + ); + + const openModal = (action: VoucherListUrlDialog, ids?: string[]) => + navigate( + voucherListUrl({ + ...params, + action, + ids + }) + ); + + const handleTabChange = (tab: number) => { + reset(); + navigate( + voucherListUrl({ + activeTab: tab.toString(), + ...getFilterTabs()[tab - 1].data + }) + ); + }; + + const handleTabDelete = () => { + deleteFilterTab(currentTab); + reset(); + navigate(voucherListUrl()); + }; + + const handleTabSave = (data: SaveFilterTabDialogFormData) => { + saveFilterTab(data.name, getActiveFilters(params)); + handleTabChange(tabs.length + 1); + }; const paginationState = createPaginationState(settings.rowNumber, params); + const queryVariables = React.useMemo( + () => ({ + ...paginationState, + filter: getFilterVariables(params) + }), + [params] + ); + const canOpenBulkActionDialog = maybe(() => params.ids.length > 0); return ( - + {({ data, loading, refetch }) => { const { loadNextPage, loadPreviousPage, pageInfo } = paginate( maybe(() => data.vouchers.pageInfo), @@ -92,6 +171,14 @@ export const VoucherList: React.StatelessComponent = ({ title={intl.formatMessage(sectionNames.vouchers)} /> changeFilterField({ query })} + onAll={() => navigate(voucherListUrl())} + onTabChange={handleTabChange} + onTabDelete={() => openModal("delete-search")} + onTabSave={() => openModal("save-search")} + tabs={tabs.map(tab => tab.name)} defaultCurrency={maybe(() => shop.defaultCurrency)} settings={settings} vouchers={maybe(() => @@ -153,6 +240,19 @@ export const VoucherList: React.StatelessComponent = ({ )} + + tabs[currentTab - 1].name, "...")} + /> ); }} diff --git a/src/discounts/views/VoucherList/filter.ts b/src/discounts/views/VoucherList/filter.ts new file mode 100644 index 000000000..6f5d7ce57 --- /dev/null +++ b/src/discounts/views/VoucherList/filter.ts @@ -0,0 +1,31 @@ +import { VoucherFilterInput } from "@saleor/types/globalTypes"; +import { + createFilterTabUtils, + createFilterUtils +} from "../../../utils/filters"; +import { + VoucherListUrlFilters, + VoucherListUrlFiltersEnum, + VoucherListUrlQueryParams +} from "../../urls"; + +export const VOUCHER_FILTERS_KEY = "VoucherFilters"; + +export function getFilterVariables( + params: VoucherListUrlFilters +): VoucherFilterInput { + return { + search: params.query + }; +} + +export const { + deleteFilterTab, + getFilterTabs, + saveFilterTab +} = createFilterTabUtils(VOUCHER_FILTERS_KEY); + +export const { areFiltersApplied, getActiveFilters } = createFilterUtils< + VoucherListUrlQueryParams, + VoucherListUrlFilters +>(VoucherListUrlFiltersEnum); diff --git a/src/discounts/views/VoucherList/index.ts b/src/discounts/views/VoucherList/index.ts new file mode 100644 index 000000000..c84409632 --- /dev/null +++ b/src/discounts/views/VoucherList/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherList"; +export * from "./VoucherList"; diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 232a4a074..3cb2b2084 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -230,6 +230,12 @@ export enum TaxRateType { WINE = "WINE", } +export enum VoucherDiscountType { + FIXED = "FIXED", + PERCENTAGE = "PERCENTAGE", + SHIPPING = "SHIPPING", +} + export enum VoucherTypeEnum { ENTIRE_ORDER = "ENTIRE_ORDER", SHIPPING = "SHIPPING", @@ -673,6 +679,14 @@ export interface UserCreateInput { redirectUrl?: string | null; } +export interface VoucherFilterInput { + status?: (DiscountStatusEnum | null)[] | null; + timesUsed?: IntRangeInput | null; + discountType?: (VoucherDiscountType | null)[] | null; + started?: DateTimeRangeInput | null; + search?: string | null; +} + export interface VoucherInput { type?: VoucherTypeEnum | null; name?: string | null;