Add filters and search to gift cards list (#1466)
* Fix filters not handling autocomplete values properly * Add handling single selection to filter autocomplete field * Change giftCardsListUrl function name to GiftCardListUrl for consistency * Update schema * Add gift card currencies query and update types * Add validating function for filter number fields * Add util function for mapping person node to select choice, fix types * Add gift card list filters and search * Add handling of gift card list search and filters dialogs in dialogs provider * Add gift card search bar in gift card list * Update gift card list queries and types, add filters to gift card list provider * Fix types * Fix types * Fix currency filters in gift card list * Update messages * Remove unnecessary usages of maybe * Change gift card balance filters not to be send to api when currency filter not present * Update messages
This commit is contained in:
parent
22db86ed65
commit
185a48b421
25 changed files with 877 additions and 38 deletions
|
@ -3717,6 +3717,62 @@
|
||||||
"context": "GiftCardUpdateDetailsCard title",
|
"context": "GiftCardUpdateDetailsCard title",
|
||||||
"string": "Details"
|
"string": "Details"
|
||||||
},
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmount": {
|
||||||
|
"context": "Filter balance amount error",
|
||||||
|
"string": "Balance amount is missing"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmountLabel": {
|
||||||
|
"context": "amount filter label",
|
||||||
|
"string": "Amount"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceCurrency": {
|
||||||
|
"context": "Filter balance currency error",
|
||||||
|
"string": "Balance currency is missing"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_currencyLabel": {
|
||||||
|
"context": "currency filter label",
|
||||||
|
"string": "Currency"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_currentBalanceLabel": {
|
||||||
|
"context": "current balance filter label",
|
||||||
|
"string": "Current balance"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_defaultTabLabel": {
|
||||||
|
"context": "gift card default tab label",
|
||||||
|
"string": "All Gift Cards"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_disabledOptionLabel": {
|
||||||
|
"context": "disabled status option label",
|
||||||
|
"string": "Disabled"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_enabledOptionLabel": {
|
||||||
|
"context": "enabled status option label",
|
||||||
|
"string": "Enabled"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_initialBalanceLabel": {
|
||||||
|
"context": "initial balance filter label",
|
||||||
|
"string": "Initial balance"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_productLabel": {
|
||||||
|
"context": "product filter label",
|
||||||
|
"string": "Product"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_searchPlaceholder": {
|
||||||
|
"context": "gift card search placeholder",
|
||||||
|
"string": "Search Gift Cards, e.g {exampleGiftCardCode}"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_statusLabel": {
|
||||||
|
"context": "status filter label",
|
||||||
|
"string": "Status"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_tagLabel": {
|
||||||
|
"context": "tag filter label",
|
||||||
|
"string": "Tags"
|
||||||
|
},
|
||||||
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_usedByLabel": {
|
||||||
|
"context": "used by filter label",
|
||||||
|
"string": "Used by"
|
||||||
|
},
|
||||||
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": {
|
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": {
|
||||||
"context": "GiftCardEnableDisableSection enable label",
|
"context": "GiftCardEnableDisableSection enable label",
|
||||||
"string": "Deactivate"
|
"string": "Deactivate"
|
||||||
|
|
|
@ -2428,6 +2428,7 @@ input GiftCardFilterInput {
|
||||||
currency: String
|
currency: String
|
||||||
currentBalance: PriceRangeInput
|
currentBalance: PriceRangeInput
|
||||||
initialBalance: PriceRangeInput
|
initialBalance: PriceRangeInput
|
||||||
|
code: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiftCardResend {
|
type GiftCardResend {
|
||||||
|
@ -5863,6 +5864,7 @@ type Query {
|
||||||
menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
||||||
giftCard(id: ID!): GiftCard
|
giftCard(id: ID!): GiftCard
|
||||||
giftCards(sortBy: GiftCardSortingInput, filter: GiftCardFilterInput, before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
giftCards(sortBy: GiftCardSortingInput, filter: GiftCardFilterInput, before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
||||||
|
giftCardCurrencies: [String!]!
|
||||||
plugin(id: ID!): Plugin
|
plugin(id: ID!): Plugin
|
||||||
plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
||||||
sale(id: ID!, channel: String): Sale
|
sale(id: ID!, channel: String): Sale
|
||||||
|
|
|
@ -9,7 +9,7 @@ import translationIcon from "@assets/images/menu-translation-icon.svg";
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
import { getConfigMenuItemsPermissions } from "@saleor/configuration/utils";
|
import { getConfigMenuItemsPermissions } from "@saleor/configuration/utils";
|
||||||
import { User } from "@saleor/fragments/types/User";
|
import { User } from "@saleor/fragments/types/User";
|
||||||
import { giftCardsListUrl } from "@saleor/giftCards/urls";
|
import { giftCardListUrl } from "@saleor/giftCards/urls";
|
||||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||||
import { SidebarMenuItem } from "@saleor/macaw-ui";
|
import { SidebarMenuItem } from "@saleor/macaw-ui";
|
||||||
import { IntlShape } from "react-intl";
|
import { IntlShape } from "react-intl";
|
||||||
|
@ -66,7 +66,7 @@ function createMenuStructure(intl: IntlShape, user: User): SidebarMenuItem[] {
|
||||||
ariaLabel: "giftCards",
|
ariaLabel: "giftCards",
|
||||||
label: intl.formatMessage(sectionNames.giftCards),
|
label: intl.formatMessage(sectionNames.giftCards),
|
||||||
id: "giftCards",
|
id: "giftCards",
|
||||||
url: giftCardsListUrl(),
|
url: giftCardListUrl(),
|
||||||
permissions: [PermissionEnum.MANAGE_GIFT_CARD]
|
permissions: [PermissionEnum.MANAGE_GIFT_CARD]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -69,26 +69,36 @@ const FilterAutocompleteField: React.FC<FilterAutocompleteFieldProps> = ({
|
||||||
const displayNoResults =
|
const displayNoResults =
|
||||||
availableOptions.length === 0 && fieldDisplayValues.length === 0;
|
availableOptions.length === 0 && fieldDisplayValues.length === 0;
|
||||||
|
|
||||||
|
const getUpdatedFilterValue = (option: MultiAutocompleteChoiceType) => {
|
||||||
|
if (filterField.multiple) {
|
||||||
|
return toggle(option.value, filterField.value, (a, b) => a === b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [option.value];
|
||||||
|
};
|
||||||
|
|
||||||
const handleChange = (option: MultiAutocompleteChoiceType) => {
|
const handleChange = (option: MultiAutocompleteChoiceType) => {
|
||||||
onFilterPropertyChange({
|
onFilterPropertyChange({
|
||||||
payload: {
|
payload: {
|
||||||
name: filterField.name,
|
name: filterField.name,
|
||||||
update: {
|
update: {
|
||||||
active: true,
|
active: true,
|
||||||
value: toggle(option.value, filterField.value, (a, b) => a === b)
|
value: getUpdatedFilterValue(option)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
type: "set-property"
|
type: "set-property"
|
||||||
});
|
});
|
||||||
|
|
||||||
setDisplayValues({
|
if (filterField.multiple) {
|
||||||
...displayValues,
|
setDisplayValues({
|
||||||
[filterField.name]: toggle(
|
...displayValues,
|
||||||
option,
|
[filterField.name]: toggle(
|
||||||
fieldDisplayValues,
|
option,
|
||||||
(a, b) => a.value === b.value
|
fieldDisplayValues,
|
||||||
)
|
(a, b) => a.value === b.value
|
||||||
});
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValueChecked = (displayValue: MultiAutocompleteChoiceType) =>
|
const isValueChecked = (displayValue: MultiAutocompleteChoiceType) =>
|
||||||
|
@ -106,18 +116,20 @@ const FilterAutocompleteField: React.FC<FilterAutocompleteFieldProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...rest}>
|
<div {...rest}>
|
||||||
<TextField
|
{filterField?.onSearchChange && (
|
||||||
data-test="filterFieldAutocompleteInput"
|
<TextField
|
||||||
className={classes.inputContainer}
|
data-test="filterFieldAutocompleteInput"
|
||||||
fullWidth
|
className={classes.inputContainer}
|
||||||
name={filterField.name + "_autocomplete"}
|
fullWidth
|
||||||
InputProps={{
|
name={filterField.name + "_autocomplete"}
|
||||||
classes: {
|
InputProps={{
|
||||||
input: classes.input
|
classes: {
|
||||||
}
|
input: classes.input
|
||||||
}}
|
}
|
||||||
onChange={event => filterField.onSearchChange(event.target.value)}
|
}}
|
||||||
/>
|
onChange={event => filterField.onSearchChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{filteredValuesChecked.map(displayValue => (
|
{filteredValuesChecked.map(displayValue => (
|
||||||
<div className={classes.option} key={displayValue.value}>
|
<div className={classes.option} key={displayValue.value}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
|
|
@ -119,7 +119,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
if (filterField.multipleFields) {
|
if (filterField.multipleFields) {
|
||||||
return filterField.multipleFields.reduce(
|
return filterField.multipleFields.reduce(
|
||||||
getAutocompleteValuesWithNewValues,
|
getAutocompleteValuesWithNewValues,
|
||||||
{}
|
acc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ExpansionPanelSummary>
|
</ExpansionPanelSummary>
|
||||||
{currentFilter.active && (
|
{currentFilter?.active && (
|
||||||
<FilterErrorsList
|
<FilterErrorsList
|
||||||
errors={errors?.[filter.name]}
|
errors={errors?.[filter.name]}
|
||||||
errorMessages={errorMessages}
|
errorMessages={errorMessages}
|
||||||
|
|
|
@ -156,7 +156,7 @@ const FilterContentBody: React.FC<FilterContentBodyProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{filter.type === FieldType.autocomplete && filter.multiple && (
|
{filter.type === FieldType.autocomplete && (
|
||||||
<FilterAutocompleteField
|
<FilterAutocompleteField
|
||||||
data-test={filterTestingContext}
|
data-test={filterTestingContext}
|
||||||
data-test-id={filter.name}
|
data-test-id={filter.name}
|
||||||
|
|
|
@ -16,12 +16,26 @@ export const isAutocompleteFilterFieldValid = function<T extends string>({
|
||||||
return !!compact(value).length;
|
return !!compact(value).length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isNumberFilterFieldValid = function<T extends string>({
|
||||||
|
value
|
||||||
|
}: IFilterElement<T>) {
|
||||||
|
const [min, max] = value;
|
||||||
|
|
||||||
|
if (!min && !max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
export const isFilterFieldValid = function<T extends string>(
|
export const isFilterFieldValid = function<T extends string>(
|
||||||
filter: IFilterElement<T>
|
filter: IFilterElement<T>
|
||||||
) {
|
) {
|
||||||
const { type } = filter;
|
const { type } = filter;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case FieldType.number:
|
||||||
|
return isNumberFilterFieldValid(filter);
|
||||||
case FieldType.boolean:
|
case FieldType.boolean:
|
||||||
case FieldType.autocomplete:
|
case FieldType.autocomplete:
|
||||||
return isAutocompleteFilterFieldValid(filter);
|
return isAutocompleteFilterFieldValid(filter);
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
|
import SaveFilterTabDialog, {
|
||||||
|
SaveFilterTabDialogFormData
|
||||||
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
|
import useGiftCardTagsSearch from "@saleor/giftCards/components/GiftCardTagInput/useGiftCardTagsSearch";
|
||||||
|
import { giftCardListUrl } from "@saleor/giftCards/urls";
|
||||||
|
import { getSearchFetchMoreProps } from "@saleor/hooks/makeTopLevelSearch/utils";
|
||||||
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
|
import useCustomerSearch from "@saleor/searches/useCustomerSearch";
|
||||||
|
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||||
|
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
||||||
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
|
import compact from "lodash/compact";
|
||||||
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
|
||||||
|
import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
|
||||||
|
import useGiftCardListBulkActions from "../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
|
||||||
|
import { GiftCardListActionParamsEnum } from "../types";
|
||||||
|
import {
|
||||||
|
createFilterStructure,
|
||||||
|
deleteFilterTab,
|
||||||
|
getActiveFilters,
|
||||||
|
getFilterOpts,
|
||||||
|
getFilterQueryParam,
|
||||||
|
getFiltersCurrentTab,
|
||||||
|
getFilterTabs,
|
||||||
|
saveFilterTab
|
||||||
|
} from "./filters";
|
||||||
|
import { giftCardListFilterErrorMessages as errorMessages } from "./messages";
|
||||||
|
import { giftCardListSearchAndFiltersMessages as messages } from "./messages";
|
||||||
|
import { useGiftCardCurrenciesQuery } from "./queries";
|
||||||
|
|
||||||
|
const GiftCardListSearchAndFilters: React.FC = () => {
|
||||||
|
const navigate = useNavigator();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { params } = useGiftCardList();
|
||||||
|
const { reset } = useGiftCardListBulkActions();
|
||||||
|
|
||||||
|
const {
|
||||||
|
closeDialog,
|
||||||
|
openSearchDeleteDialog,
|
||||||
|
openSearchSaveDialog
|
||||||
|
} = useGiftCardListDialogs();
|
||||||
|
|
||||||
|
const defaultSearchVariables = {
|
||||||
|
variables: { ...DEFAULT_INITIAL_SEARCH_DATA, first: 5 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
loadMore: fetchMoreCustomers,
|
||||||
|
search: searchCustomers,
|
||||||
|
result: searchCustomersResult
|
||||||
|
} = useCustomerSearch(defaultSearchVariables);
|
||||||
|
|
||||||
|
const {
|
||||||
|
loadMore: fetchMoreProducts,
|
||||||
|
search: searchProducts,
|
||||||
|
result: searchProductsResult
|
||||||
|
} = useProductSearch(defaultSearchVariables);
|
||||||
|
|
||||||
|
const {
|
||||||
|
loadMore: fetchMoreGiftCardTags,
|
||||||
|
search: searchGiftCardTags,
|
||||||
|
result: searchGiftCardTagsResult
|
||||||
|
} = useGiftCardTagsSearch(defaultSearchVariables);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: giftCardCurrenciesData,
|
||||||
|
loading: loadingGiftCardCurrencies
|
||||||
|
} = useGiftCardCurrenciesQuery();
|
||||||
|
|
||||||
|
const filterOpts = getFilterOpts({
|
||||||
|
params,
|
||||||
|
productSearchProps: {
|
||||||
|
...getSearchFetchMoreProps(searchProductsResult, fetchMoreProducts),
|
||||||
|
onSearchChange: searchProducts
|
||||||
|
},
|
||||||
|
products: mapEdgesToItems(searchProductsResult?.data?.search),
|
||||||
|
currencies: giftCardCurrenciesData?.giftCardCurrencies,
|
||||||
|
loadingCurrencies: loadingGiftCardCurrencies,
|
||||||
|
customerSearchProps: {
|
||||||
|
...getSearchFetchMoreProps(searchCustomersResult, fetchMoreCustomers),
|
||||||
|
onSearchChange: searchCustomers
|
||||||
|
},
|
||||||
|
customers: mapEdgesToItems(searchCustomersResult?.data?.search),
|
||||||
|
tagSearchProps: {
|
||||||
|
...getSearchFetchMoreProps(
|
||||||
|
searchGiftCardTagsResult,
|
||||||
|
fetchMoreGiftCardTags
|
||||||
|
),
|
||||||
|
onSearchChange: searchGiftCardTags
|
||||||
|
},
|
||||||
|
tags: compact(
|
||||||
|
mapEdgesToItems(searchGiftCardTagsResult?.data?.search)?.map(
|
||||||
|
({ tag }) => tag
|
||||||
|
)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
|
const tabs = getFilterTabs();
|
||||||
|
const currentTab = getFiltersCurrentTab(params, tabs);
|
||||||
|
|
||||||
|
const [
|
||||||
|
changeFilters,
|
||||||
|
resetFilters,
|
||||||
|
handleSearchChange
|
||||||
|
] = createFilterHandlers({
|
||||||
|
createUrl: giftCardListUrl,
|
||||||
|
getFilterQueryParam,
|
||||||
|
navigate,
|
||||||
|
params,
|
||||||
|
cleanupFn: reset
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTabChange = (tab: number) => {
|
||||||
|
reset();
|
||||||
|
navigate(
|
||||||
|
giftCardListUrl({
|
||||||
|
activeTab: tab.toString(),
|
||||||
|
...getFilterTabs()[tab - 1].data
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabDelete = () => {
|
||||||
|
deleteFilterTab(currentTab);
|
||||||
|
reset();
|
||||||
|
navigate(giftCardListUrl());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||||
|
saveFilterTab(data.name, getActiveFilters(params));
|
||||||
|
handleTabChange(tabs.length + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FilterBar
|
||||||
|
errorMessages={{
|
||||||
|
initialBalanceAmount: errorMessages.balanceAmount,
|
||||||
|
initialBalanceCurrency: errorMessages.balanceCurrency,
|
||||||
|
currentBalanceAmount: errorMessages.balanceAmount,
|
||||||
|
currentBalanceCurrency: errorMessages.balanceCurrency
|
||||||
|
}}
|
||||||
|
tabs={tabs.map(tab => tab.name)}
|
||||||
|
currentTab={currentTab}
|
||||||
|
filterStructure={filterStructure}
|
||||||
|
initialSearch={params?.query || ""}
|
||||||
|
onAll={resetFilters}
|
||||||
|
onFilterChange={changeFilters}
|
||||||
|
onSearchChange={handleSearchChange}
|
||||||
|
onTabChange={handleTabChange}
|
||||||
|
onTabDelete={openSearchDeleteDialog}
|
||||||
|
onTabSave={openSearchSaveDialog}
|
||||||
|
searchPlaceholder={intl.formatMessage(messages.searchPlaceholder, {
|
||||||
|
exampleGiftCardCode: "21F1-39DY-V4U2"
|
||||||
|
})}
|
||||||
|
allTabLabel={intl.formatMessage(messages.defaultTabLabel)}
|
||||||
|
/>
|
||||||
|
<SaveFilterTabDialog
|
||||||
|
open={params.action === GiftCardListActionParamsEnum.SAVE_SEARCH}
|
||||||
|
confirmButtonState="default"
|
||||||
|
onClose={closeDialog}
|
||||||
|
onSubmit={handleTabSave}
|
||||||
|
/>
|
||||||
|
<DeleteFilterTabDialog
|
||||||
|
open={params.action === GiftCardListActionParamsEnum.DELETE_SEARCH}
|
||||||
|
confirmButtonState="default"
|
||||||
|
onClose={closeDialog}
|
||||||
|
onSubmit={handleTabDelete}
|
||||||
|
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GiftCardListSearchAndFilters;
|
|
@ -0,0 +1,387 @@
|
||||||
|
import { IFilter, IFilterElement } from "@saleor/components/Filter";
|
||||||
|
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
||||||
|
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
|
||||||
|
import { GiftCardFilterInput } from "@saleor/types/globalTypes";
|
||||||
|
import {
|
||||||
|
createFilterTabUtils,
|
||||||
|
createFilterUtils,
|
||||||
|
dedupeFilter,
|
||||||
|
getMinMaxQueryParam,
|
||||||
|
getMultipleValueQueryParam,
|
||||||
|
getSingleValueQueryParam
|
||||||
|
} from "@saleor/utils/filters";
|
||||||
|
import {
|
||||||
|
createAutocompleteField,
|
||||||
|
createNumberField,
|
||||||
|
createOptionsField
|
||||||
|
} from "@saleor/utils/filters/fields";
|
||||||
|
import {
|
||||||
|
mapNodeToChoice,
|
||||||
|
mapPersonNodeToChoice,
|
||||||
|
mapSingleValueNodeToChoice
|
||||||
|
} from "@saleor/utils/maps";
|
||||||
|
import { defineMessages, IntlShape } from "react-intl";
|
||||||
|
|
||||||
|
import { GiftCardListUrlQueryParams } from "../types";
|
||||||
|
import {
|
||||||
|
GiftCardListFilterKeys,
|
||||||
|
GiftCardListFilterOpts,
|
||||||
|
GiftCardListUrlFilters,
|
||||||
|
GiftCardListUrlFiltersEnum,
|
||||||
|
GiftCardStatusFilterEnum,
|
||||||
|
SearchWithFetchMoreProps
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
export const GIFT_CARD_FILTERS_KEY = "giftCardFilters";
|
||||||
|
|
||||||
|
interface GiftCardFilterOptsProps {
|
||||||
|
params: GiftCardListUrlFilters;
|
||||||
|
currencies: string[];
|
||||||
|
loadingCurrencies: boolean;
|
||||||
|
products: SearchProducts_search_edges_node[];
|
||||||
|
productSearchProps: SearchWithFetchMoreProps;
|
||||||
|
customers: SearchCustomers_search_edges_node[];
|
||||||
|
customerSearchProps: SearchWithFetchMoreProps;
|
||||||
|
tags: string[];
|
||||||
|
tagSearchProps: SearchWithFetchMoreProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getFilterOpts = ({
|
||||||
|
params,
|
||||||
|
currencies,
|
||||||
|
loadingCurrencies,
|
||||||
|
products,
|
||||||
|
productSearchProps,
|
||||||
|
customers,
|
||||||
|
customerSearchProps,
|
||||||
|
tags,
|
||||||
|
tagSearchProps
|
||||||
|
}: GiftCardFilterOptsProps): GiftCardListFilterOpts => ({
|
||||||
|
currency: {
|
||||||
|
active: !!params?.currency,
|
||||||
|
value: params?.currency,
|
||||||
|
choices: mapSingleValueNodeToChoice(currencies),
|
||||||
|
displayValues: mapSingleValueNodeToChoice(currencies),
|
||||||
|
initialSearch: "",
|
||||||
|
loading: loadingCurrencies
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
active: !!params?.product,
|
||||||
|
value: params?.product,
|
||||||
|
choices: mapNodeToChoice(products),
|
||||||
|
displayValues: mapSingleValueNodeToChoice(products),
|
||||||
|
initialSearch: "",
|
||||||
|
hasMore: productSearchProps.hasMore,
|
||||||
|
loading: productSearchProps.loading,
|
||||||
|
onFetchMore: productSearchProps.onFetchMore,
|
||||||
|
onSearchChange: productSearchProps.onSearchChange
|
||||||
|
},
|
||||||
|
usedBy: {
|
||||||
|
active: !!params?.usedBy,
|
||||||
|
value: params?.usedBy,
|
||||||
|
choices: mapPersonNodeToChoice(customers),
|
||||||
|
displayValues: mapPersonNodeToChoice(customers),
|
||||||
|
initialSearch: "",
|
||||||
|
hasMore: customerSearchProps.hasMore,
|
||||||
|
loading: customerSearchProps.loading,
|
||||||
|
onFetchMore: customerSearchProps.onFetchMore,
|
||||||
|
onSearchChange: customerSearchProps.onSearchChange
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
active: !!params?.tag,
|
||||||
|
value: dedupeFilter(params?.tag || []),
|
||||||
|
choices: mapSingleValueNodeToChoice(tags),
|
||||||
|
displayValues: mapSingleValueNodeToChoice(tags),
|
||||||
|
initialSearch: "",
|
||||||
|
hasMore: tagSearchProps.hasMore,
|
||||||
|
loading: tagSearchProps.loading,
|
||||||
|
onFetchMore: tagSearchProps.onFetchMore,
|
||||||
|
onSearchChange: tagSearchProps.onSearchChange
|
||||||
|
},
|
||||||
|
initialBalanceAmount: {
|
||||||
|
active:
|
||||||
|
[params.initialBalanceAmountFrom, params.initialBalanceAmountTo].some(
|
||||||
|
field => field !== undefined
|
||||||
|
) || false,
|
||||||
|
value: {
|
||||||
|
max: params.initialBalanceAmountTo || "",
|
||||||
|
min: params.initialBalanceAmountFrom || ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
currentBalanceAmount: {
|
||||||
|
active:
|
||||||
|
[params.currentBalanceAmountFrom, params.currentBalanceAmountTo].some(
|
||||||
|
field => field !== undefined
|
||||||
|
) || false,
|
||||||
|
value: {
|
||||||
|
max: params.currentBalanceAmountTo || "",
|
||||||
|
min: params.currentBalanceAmountFrom || ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
active: !!params?.status,
|
||||||
|
value: params?.status
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function getFilterQueryParam(
|
||||||
|
filter: IFilterElement<GiftCardListFilterKeys>
|
||||||
|
): GiftCardListUrlFilters {
|
||||||
|
const { name } = filter;
|
||||||
|
|
||||||
|
const {
|
||||||
|
initialBalanceAmount,
|
||||||
|
currentBalanceAmount,
|
||||||
|
tag,
|
||||||
|
currency,
|
||||||
|
usedBy,
|
||||||
|
product,
|
||||||
|
status
|
||||||
|
} = GiftCardListFilterKeys;
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case currency:
|
||||||
|
case status:
|
||||||
|
return getSingleValueQueryParam(filter, name);
|
||||||
|
|
||||||
|
case tag:
|
||||||
|
case product:
|
||||||
|
case usedBy:
|
||||||
|
return getMultipleValueQueryParam(filter, name);
|
||||||
|
|
||||||
|
case initialBalanceAmount:
|
||||||
|
return getMinMaxQueryParam(
|
||||||
|
filter,
|
||||||
|
GiftCardListUrlFiltersEnum.initialBalanceAmountFrom,
|
||||||
|
GiftCardListUrlFiltersEnum.initialBalanceAmountTo
|
||||||
|
);
|
||||||
|
|
||||||
|
case currentBalanceAmount:
|
||||||
|
return getMinMaxQueryParam(
|
||||||
|
filter,
|
||||||
|
GiftCardListUrlFiltersEnum.currentBalanceAmountFrom,
|
||||||
|
GiftCardListUrlFiltersEnum.currentBalanceAmountTo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
balanceAmountLabel: {
|
||||||
|
defaultMessage: "Amount",
|
||||||
|
description: "amount filter label"
|
||||||
|
},
|
||||||
|
tagLabel: {
|
||||||
|
defaultMessage: "Tags",
|
||||||
|
description: "tag filter label"
|
||||||
|
},
|
||||||
|
currencyLabel: {
|
||||||
|
defaultMessage: "Currency",
|
||||||
|
description: "currency filter label"
|
||||||
|
},
|
||||||
|
productLabel: {
|
||||||
|
defaultMessage: "Product",
|
||||||
|
description: "product filter label"
|
||||||
|
},
|
||||||
|
usedByLabel: {
|
||||||
|
defaultMessage: "Used by",
|
||||||
|
description: "used by filter label"
|
||||||
|
},
|
||||||
|
statusLabel: {
|
||||||
|
defaultMessage: "Status",
|
||||||
|
description: "status filter label"
|
||||||
|
},
|
||||||
|
enabledOptionLabel: {
|
||||||
|
defaultMessage: "Enabled",
|
||||||
|
description: "enabled status option label"
|
||||||
|
},
|
||||||
|
disabledOptionLabel: {
|
||||||
|
defaultMessage: "Disabled",
|
||||||
|
description: "disabled status option label"
|
||||||
|
},
|
||||||
|
initialBalanceLabel: {
|
||||||
|
defaultMessage: "Initial balance",
|
||||||
|
description: "initial balance filter label"
|
||||||
|
},
|
||||||
|
currentBalanceLabel: {
|
||||||
|
defaultMessage: "Current balance",
|
||||||
|
description: "current balance filter label"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function createFilterStructure(
|
||||||
|
intl: IntlShape,
|
||||||
|
opts: GiftCardListFilterOpts
|
||||||
|
): IFilter<GiftCardListFilterKeys> {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...createNumberField(
|
||||||
|
GiftCardListFilterKeys.initialBalanceAmount,
|
||||||
|
intl.formatMessage(messages.initialBalanceLabel),
|
||||||
|
opts.initialBalanceAmount.value
|
||||||
|
),
|
||||||
|
multiple:
|
||||||
|
opts?.initialBalanceAmount?.value?.min !==
|
||||||
|
opts?.initialBalanceAmount?.value?.max,
|
||||||
|
active: opts.initialBalanceAmount.active
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
...createNumberField(
|
||||||
|
GiftCardListFilterKeys.currentBalanceAmount,
|
||||||
|
intl.formatMessage(messages.currentBalanceLabel),
|
||||||
|
opts.currentBalanceAmount.value
|
||||||
|
),
|
||||||
|
multiple:
|
||||||
|
opts?.currentBalanceAmount?.value?.min !==
|
||||||
|
opts?.currentBalanceAmount?.value?.max,
|
||||||
|
active: opts.currentBalanceAmount.active
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createAutocompleteField(
|
||||||
|
GiftCardListFilterKeys.currency,
|
||||||
|
intl.formatMessage(messages.currencyLabel),
|
||||||
|
[opts.currency.value],
|
||||||
|
opts.currency.displayValues,
|
||||||
|
false,
|
||||||
|
opts.currency.choices,
|
||||||
|
{
|
||||||
|
hasMore: opts.currency.hasMore,
|
||||||
|
initialSearch: "",
|
||||||
|
loading: opts.currency.loading,
|
||||||
|
onFetchMore: opts.currency.onFetchMore,
|
||||||
|
onSearchChange: opts.currency.onSearchChange
|
||||||
|
}
|
||||||
|
),
|
||||||
|
active: opts.currency.active
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createAutocompleteField(
|
||||||
|
GiftCardListFilterKeys.tag,
|
||||||
|
intl.formatMessage(messages.tagLabel),
|
||||||
|
opts.tag.value,
|
||||||
|
opts.tag.displayValues,
|
||||||
|
true,
|
||||||
|
opts.tag.choices,
|
||||||
|
{
|
||||||
|
hasMore: opts.tag.hasMore,
|
||||||
|
initialSearch: "",
|
||||||
|
loading: opts.tag.loading,
|
||||||
|
onFetchMore: opts.tag.onFetchMore,
|
||||||
|
onSearchChange: opts.tag.onSearchChange
|
||||||
|
}
|
||||||
|
),
|
||||||
|
active: opts.tag.active
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createAutocompleteField(
|
||||||
|
GiftCardListFilterKeys.product,
|
||||||
|
intl.formatMessage(messages.productLabel),
|
||||||
|
opts.product.value,
|
||||||
|
opts.product.displayValues,
|
||||||
|
true,
|
||||||
|
opts.product.choices,
|
||||||
|
{
|
||||||
|
hasMore: opts.product.hasMore,
|
||||||
|
initialSearch: "",
|
||||||
|
loading: opts.product.loading,
|
||||||
|
onFetchMore: opts.product.onFetchMore,
|
||||||
|
onSearchChange: opts.product.onSearchChange
|
||||||
|
}
|
||||||
|
),
|
||||||
|
active: opts.product.active
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createAutocompleteField(
|
||||||
|
GiftCardListFilterKeys.usedBy,
|
||||||
|
intl.formatMessage(messages.usedByLabel),
|
||||||
|
opts.usedBy.value,
|
||||||
|
opts.usedBy.displayValues,
|
||||||
|
true,
|
||||||
|
opts.usedBy.choices,
|
||||||
|
{
|
||||||
|
hasMore: opts.usedBy.hasMore,
|
||||||
|
initialSearch: "",
|
||||||
|
loading: opts.usedBy.loading,
|
||||||
|
onFetchMore: opts.usedBy.onFetchMore,
|
||||||
|
onSearchChange: opts.usedBy.onSearchChange
|
||||||
|
}
|
||||||
|
),
|
||||||
|
active: opts.usedBy.active
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createOptionsField(
|
||||||
|
GiftCardListFilterKeys.status,
|
||||||
|
intl.formatMessage(messages.statusLabel),
|
||||||
|
[opts.status.value],
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: intl.formatMessage(messages.enabledOptionLabel),
|
||||||
|
value: GiftCardStatusFilterEnum.enabled
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage(messages.disabledOptionLabel),
|
||||||
|
value: GiftCardStatusFilterEnum.disabled
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
active: opts.status.active
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const {
|
||||||
|
deleteFilterTab,
|
||||||
|
getFilterTabs,
|
||||||
|
saveFilterTab
|
||||||
|
} = createFilterTabUtils<GiftCardListUrlFilters>(GIFT_CARD_FILTERS_KEY);
|
||||||
|
|
||||||
|
export const {
|
||||||
|
areFiltersApplied,
|
||||||
|
getActiveFilters,
|
||||||
|
getFiltersCurrentTab
|
||||||
|
} = createFilterUtils<GiftCardListUrlQueryParams, GiftCardListUrlFilters>(
|
||||||
|
GiftCardListUrlFiltersEnum
|
||||||
|
);
|
||||||
|
|
||||||
|
export function getFilterVariables({
|
||||||
|
status,
|
||||||
|
tag,
|
||||||
|
usedBy,
|
||||||
|
product,
|
||||||
|
currency,
|
||||||
|
currentBalanceAmountTo,
|
||||||
|
currentBalanceAmountFrom,
|
||||||
|
initialBalanceAmountTo,
|
||||||
|
initialBalanceAmountFrom,
|
||||||
|
query
|
||||||
|
}: GiftCardListUrlQueryParams): GiftCardFilterInput {
|
||||||
|
const balanceData = currency
|
||||||
|
? {
|
||||||
|
currentBalance:
|
||||||
|
currentBalanceAmountFrom && currentBalanceAmountTo
|
||||||
|
? {
|
||||||
|
gte: parseFloat(currentBalanceAmountFrom),
|
||||||
|
lte: parseFloat(currentBalanceAmountTo)
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
initialBalance:
|
||||||
|
initialBalanceAmountFrom && initialBalanceAmountTo
|
||||||
|
? {
|
||||||
|
gte: parseFloat(initialBalanceAmountFrom),
|
||||||
|
lte: parseFloat(initialBalanceAmountTo)
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: query,
|
||||||
|
isActive: !!status ? status === "enabled" : undefined,
|
||||||
|
tags: tag,
|
||||||
|
usedBy,
|
||||||
|
products: product,
|
||||||
|
currency,
|
||||||
|
...balanceData
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./GiftCardListSearchAndFilters";
|
||||||
|
export { default } from "./GiftCardListSearchAndFilters";
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const giftCardListFilterErrorMessages = defineMessages({
|
||||||
|
balanceAmount: {
|
||||||
|
defaultMessage: "Balance amount is missing",
|
||||||
|
description: "Filter balance amount error"
|
||||||
|
},
|
||||||
|
balanceCurrency: {
|
||||||
|
defaultMessage: "Balance currency is missing",
|
||||||
|
description: "Filter balance currency error"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const giftCardListSearchAndFiltersMessages = defineMessages({
|
||||||
|
searchPlaceholder: {
|
||||||
|
defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}",
|
||||||
|
description: "gift card search placeholder"
|
||||||
|
},
|
||||||
|
defaultTabLabel: {
|
||||||
|
defaultMessage: "All Gift Cards",
|
||||||
|
description: "gift card default tab label"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,14 @@
|
||||||
|
import makeQuery from "@saleor/hooks/makeQuery";
|
||||||
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
import { GiftCardCurrencies } from "./types/GiftCardCurrencies";
|
||||||
|
|
||||||
|
const useGiftCardCurrencies = gql`
|
||||||
|
query GiftCardCurrencies {
|
||||||
|
giftCardCurrencies
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const useGiftCardCurrenciesQuery = makeQuery<GiftCardCurrencies, {}>(
|
||||||
|
useGiftCardCurrencies
|
||||||
|
);
|
|
@ -0,0 +1,58 @@
|
||||||
|
import {
|
||||||
|
AutocompleteFilterOpts,
|
||||||
|
FetchMoreProps,
|
||||||
|
FilterOpts,
|
||||||
|
Filters,
|
||||||
|
FiltersWithMultipleValues,
|
||||||
|
MinMax,
|
||||||
|
Search,
|
||||||
|
SearchProps
|
||||||
|
} from "@saleor/types";
|
||||||
|
|
||||||
|
export enum GiftCardListUrlFiltersEnum {
|
||||||
|
currency = "currency",
|
||||||
|
initialBalanceAmountFrom = "initialBalanceAmountFrom",
|
||||||
|
initialBalanceAmountTo = "initialBalanceAmountTo",
|
||||||
|
currentBalanceAmountFrom = "currentBalanceAmountFrom",
|
||||||
|
currentBalanceAmountTo = "currentBalanceAmountTo",
|
||||||
|
status = "status"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GiftCardListUrlFiltersWithMultipleValuesEnum {
|
||||||
|
tag = "tag",
|
||||||
|
product = "product",
|
||||||
|
usedBy = "usedBy"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GiftCardListFilterKeys {
|
||||||
|
currency = "currency",
|
||||||
|
balance = "balance",
|
||||||
|
initialBalance = "initialBalance",
|
||||||
|
currentBalance = "currentBalance",
|
||||||
|
initialBalanceAmount = "initialBalanceAmount",
|
||||||
|
currentBalanceAmount = "currentBalanceAmount",
|
||||||
|
tag = "tag",
|
||||||
|
product = "product",
|
||||||
|
usedBy = "usedBy",
|
||||||
|
status = "status"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GiftCardListUrlFilters = Filters<GiftCardListUrlFiltersEnum> &
|
||||||
|
FiltersWithMultipleValues<GiftCardListUrlFiltersWithMultipleValuesEnum>;
|
||||||
|
|
||||||
|
export interface GiftCardListFilterOpts {
|
||||||
|
tag: FilterOpts<string[]> & AutocompleteFilterOpts;
|
||||||
|
currency: FilterOpts<string> & AutocompleteFilterOpts;
|
||||||
|
product: FilterOpts<string[]> & AutocompleteFilterOpts;
|
||||||
|
usedBy: FilterOpts<string[]> & AutocompleteFilterOpts;
|
||||||
|
initialBalanceAmount: FilterOpts<MinMax>;
|
||||||
|
currentBalanceAmount: FilterOpts<MinMax>;
|
||||||
|
status: FilterOpts<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SearchWithFetchMoreProps = FetchMoreProps & Search & SearchProps;
|
||||||
|
|
||||||
|
export enum GiftCardStatusFilterEnum {
|
||||||
|
enabled = "enabled",
|
||||||
|
disabled = "disabled"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @generated
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL query operation: GiftCardCurrencies
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface GiftCardCurrencies {
|
||||||
|
giftCardCurrencies: string[];
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
|
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
|
||||||
|
import GiftCardListSearchAndFilters from "../GiftCardListSearchAndFilters";
|
||||||
import { giftCardsListTableMessages as messages } from "../messages";
|
import { giftCardsListTableMessages as messages } from "../messages";
|
||||||
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
|
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
|
||||||
import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
|
import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
|
||||||
|
@ -77,6 +78,7 @@ const GiftCardsListTable: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
<GiftCardListSearchAndFilters />
|
||||||
<ResponsiveTable>
|
<ResponsiveTable>
|
||||||
<GiftCardsListTableHeader />
|
<GiftCardsListTableHeader />
|
||||||
<GiftCardsListTableFooter />
|
<GiftCardsListTableFooter />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import GiftCardListPageDeleteDialog from "@saleor/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog";
|
import GiftCardListPageDeleteDialog from "@saleor/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog";
|
||||||
import GiftCardCreateDialog from "@saleor/giftCards/GiftCardCreateDialog";
|
import GiftCardCreateDialog from "@saleor/giftCards/GiftCardCreateDialog";
|
||||||
import { giftCardsListUrl } from "@saleor/giftCards/urls";
|
import { giftCardListUrl } from "@saleor/giftCards/urls";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import React, { createContext } from "react";
|
import React, { createContext } from "react";
|
||||||
|
@ -18,6 +18,8 @@ interface GiftCardListDialogsProviderProps {
|
||||||
export interface GiftCardListDialogsConsumerProps {
|
export interface GiftCardListDialogsConsumerProps {
|
||||||
openCreateDialog: () => void;
|
openCreateDialog: () => void;
|
||||||
openDeleteDialog: (id?: string | React.MouseEvent) => void;
|
openDeleteDialog: (id?: string | React.MouseEvent) => void;
|
||||||
|
openSearchSaveDialog: () => void;
|
||||||
|
openSearchDeleteDialog: () => void;
|
||||||
closeDialog: () => void;
|
closeDialog: () => void;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,7 @@ const GiftCardListDialogsProvider: React.FC<GiftCardListDialogsProviderProps> =
|
||||||
const [openDialog, closeDialog] = createDialogActionHandlers<
|
const [openDialog, closeDialog] = createDialogActionHandlers<
|
||||||
GiftCardListActionParamsEnum,
|
GiftCardListActionParamsEnum,
|
||||||
GiftCardListUrlQueryParams
|
GiftCardListUrlQueryParams
|
||||||
>(navigate, giftCardsListUrl, params);
|
>(navigate, giftCardListUrl, params);
|
||||||
|
|
||||||
const openCreateDialog = () =>
|
const openCreateDialog = () =>
|
||||||
openDialog(GiftCardListActionParamsEnum.CREATE);
|
openDialog(GiftCardListActionParamsEnum.CREATE);
|
||||||
|
@ -55,9 +57,17 @@ const GiftCardListDialogsProvider: React.FC<GiftCardListDialogsProviderProps> =
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openSearchDeleteDialog = () =>
|
||||||
|
openDialog(GiftCardListActionParamsEnum.DELETE_SEARCH);
|
||||||
|
|
||||||
|
const openSearchSaveDialog = () =>
|
||||||
|
openDialog(GiftCardListActionParamsEnum.SAVE_SEARCH);
|
||||||
|
|
||||||
const providerValues: GiftCardListDialogsConsumerProps = {
|
const providerValues: GiftCardListDialogsConsumerProps = {
|
||||||
openCreateDialog,
|
openCreateDialog,
|
||||||
openDeleteDialog: handleDeleteDialogOpen,
|
openDeleteDialog: handleDeleteDialogOpen,
|
||||||
|
openSearchSaveDialog,
|
||||||
|
openSearchDeleteDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { ListViews } from "@saleor/types";
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
import React, { createContext } from "react";
|
import React, { createContext } from "react";
|
||||||
|
|
||||||
|
import { getFilterVariables } from "../../GiftCardListSearchAndFilters/filters";
|
||||||
import { useGiftCardListQuery } from "../../queries";
|
import { useGiftCardListQuery } from "../../queries";
|
||||||
import { GiftCardListColummns, GiftCardListUrlQueryParams } from "../../types";
|
import { GiftCardListColummns, GiftCardListUrlQueryParams } from "../../types";
|
||||||
import {
|
import {
|
||||||
|
@ -65,7 +66,8 @@ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
|
||||||
|
|
||||||
const queryVariables = React.useMemo<GiftCardListVariables>(
|
const queryVariables = React.useMemo<GiftCardListVariables>(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState
|
...paginationState,
|
||||||
|
filter: getFilterVariables(params)
|
||||||
}),
|
}),
|
||||||
[params]
|
[params]
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,8 +9,20 @@ import { GiftCardProductsCount } from "./types/GiftCardProductsCount";
|
||||||
export const giftCardList = gql`
|
export const giftCardList = gql`
|
||||||
${fragmentUserBase}
|
${fragmentUserBase}
|
||||||
${fragmentMoney}
|
${fragmentMoney}
|
||||||
query GiftCardList($first: Int, $after: String, $last: Int, $before: String) {
|
query GiftCardList(
|
||||||
giftCards(first: $first, after: $after, before: $before, last: $last) {
|
$first: Int
|
||||||
|
$after: String
|
||||||
|
$last: Int
|
||||||
|
$before: String
|
||||||
|
$filter: GiftCardFilterInput
|
||||||
|
) {
|
||||||
|
giftCards(
|
||||||
|
first: $first
|
||||||
|
after: $after
|
||||||
|
before: $before
|
||||||
|
last: $last
|
||||||
|
filter: $filter
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
import { Dialog, Pagination, SingleAction } from "@saleor/types";
|
import {
|
||||||
|
ActiveTab,
|
||||||
|
Dialog,
|
||||||
|
Pagination,
|
||||||
|
Search,
|
||||||
|
SingleAction
|
||||||
|
} from "@saleor/types";
|
||||||
|
|
||||||
|
import { GiftCardListUrlFilters } from "./GiftCardListSearchAndFilters/types";
|
||||||
|
|
||||||
export type GiftCardListColummns =
|
export type GiftCardListColummns =
|
||||||
| "giftCardCode"
|
| "giftCardCode"
|
||||||
|
@ -9,11 +17,16 @@ export type GiftCardListColummns =
|
||||||
|
|
||||||
export enum GiftCardListActionParamsEnum {
|
export enum GiftCardListActionParamsEnum {
|
||||||
CREATE = "gift-card-create",
|
CREATE = "gift-card-create",
|
||||||
DELETE = "gift-card-delete"
|
DELETE = "gift-card-delete",
|
||||||
|
SAVE_SEARCH = "save-search",
|
||||||
|
DELETE_SEARCH = "delete-search"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GiftCardListUrlQueryParams = Pagination &
|
export type GiftCardListUrlQueryParams = Pagination &
|
||||||
Dialog<GiftCardListActionParamsEnum> &
|
Dialog<GiftCardListActionParamsEnum> &
|
||||||
SingleAction;
|
SingleAction &
|
||||||
|
GiftCardListUrlFilters &
|
||||||
|
ActiveTab &
|
||||||
|
Search;
|
||||||
|
|
||||||
export const GIFT_CARD_LIST_QUERY = "GiftCardList";
|
export const GIFT_CARD_LIST_QUERY = "GiftCardList";
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// @generated
|
// @generated
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { GiftCardFilterInput } from "./../../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: GiftCardList
|
// GraphQL query operation: GiftCardList
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
@ -67,4 +69,5 @@ export interface GiftCardListVariables {
|
||||||
after?: string | null;
|
after?: string | null;
|
||||||
last?: number | null;
|
last?: number | null;
|
||||||
before?: string | null;
|
before?: string | null;
|
||||||
|
filter?: GiftCardFilterInput | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const giftCardsSectionUrlName = "/gift-cards";
|
||||||
|
|
||||||
export const giftCardsListPath = `${giftCardsSectionUrlName}/`;
|
export const giftCardsListPath = `${giftCardsSectionUrlName}/`;
|
||||||
|
|
||||||
export const giftCardsListUrl = (params?: GiftCardListUrlQueryParams) =>
|
export const giftCardListUrl = (params?: GiftCardListUrlQueryParams) =>
|
||||||
giftCardsListPath + "?" + stringifyQs(params);
|
giftCardsListPath + "?" + stringifyQs(params);
|
||||||
|
|
||||||
export const giftCardUrl = (
|
export const giftCardUrl = (
|
||||||
|
|
|
@ -209,8 +209,8 @@ export interface FilterOpts<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutocompleteFilterOpts
|
export interface AutocompleteFilterOpts
|
||||||
extends FetchMoreProps,
|
extends Partial<FetchMoreProps>,
|
||||||
SearchPageProps {
|
Partial<SearchPageProps> {
|
||||||
choices: MultiAutocompleteChoiceType[];
|
choices: MultiAutocompleteChoiceType[];
|
||||||
displayValues: MultiAutocompleteChoiceType[];
|
displayValues: MultiAutocompleteChoiceType[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2209,6 +2209,18 @@ export interface GiftCardCreateInput {
|
||||||
note?: string | null;
|
note?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GiftCardFilterInput {
|
||||||
|
isActive?: boolean | null;
|
||||||
|
tag?: string | null;
|
||||||
|
tags?: (string | null)[] | null;
|
||||||
|
products?: (string | null)[] | null;
|
||||||
|
usedBy?: (string | null)[] | null;
|
||||||
|
currency?: string | null;
|
||||||
|
currentBalance?: PriceRangeInput | null;
|
||||||
|
initialBalance?: PriceRangeInput | null;
|
||||||
|
code?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GiftCardResendInput {
|
export interface GiftCardResendInput {
|
||||||
id: string;
|
id: string;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
|
|
|
@ -83,7 +83,7 @@ export function createOptionsField<T extends string>(
|
||||||
export function createAutocompleteField<T extends string>(
|
export function createAutocompleteField<T extends string>(
|
||||||
name: T,
|
name: T,
|
||||||
label: string,
|
label: string,
|
||||||
defaultValue: string[],
|
defaultValue: string[] = [],
|
||||||
displayValues: MultiAutocompleteChoiceType[],
|
displayValues: MultiAutocompleteChoiceType[],
|
||||||
multiple: boolean,
|
multiple: boolean,
|
||||||
options: MultiAutocompleteChoiceType[],
|
options: MultiAutocompleteChoiceType[],
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
SingleAutocompleteChoiceType
|
SingleAutocompleteChoiceType
|
||||||
} from "@saleor/components/SingleAutocompleteSelectField";
|
} from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import { MetadataItem } from "@saleor/fragments/types/MetadataItem";
|
import { MetadataItem } from "@saleor/fragments/types/MetadataItem";
|
||||||
|
import { getFullName } from "@saleor/misc";
|
||||||
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
|
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
|
||||||
import { Node, SlugNode, TagNode } from "@saleor/types";
|
import { Node, SlugNode, TagNode } from "@saleor/types";
|
||||||
import { MetadataInput } from "@saleor/types/globalTypes";
|
import { MetadataInput } from "@saleor/types/globalTypes";
|
||||||
|
@ -92,3 +93,22 @@ export function mapSingleValueNodeToChoice<T extends Record<string, any>>(
|
||||||
|
|
||||||
return (nodes as T[]).map(node => ({ label: node[key], value: node[key] }));
|
return (nodes as T[]).map(node => ({ label: node[key], value: node[key] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Person {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapPersonNodeToChoice<T extends Person>(
|
||||||
|
nodes: T[]
|
||||||
|
): SingleAutocompleteChoiceType[] {
|
||||||
|
if (!nodes) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes.map(({ firstName, lastName, id }) => ({
|
||||||
|
value: id,
|
||||||
|
label: getFullName({ firstName, lastName })
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue