From 29f6c5496bff39b920a926a3c47af4c9e7f89978 Mon Sep 17 00:00:00 2001 From: Jakub Majorek Date: Mon, 28 Jun 2021 15:51:19 +0200 Subject: [PATCH] Use root level channel argument (#1126) * Use root level channel argument * Update schema after rebase * Adjust list views (#1187) * Use root level channel argument * Use channel from filters * Handle sort keys that require channel * Add channel filter on sales and vouchers pages * Update queries * Fix tests/fixtures * Block sort on discount pages * Add generic filter validation * Fix tests, update changelog * Channels availability status changes, code cleanup * Update snapshots --- CHANGELOG.md | 2 + locale/defaultMessages.json | 48 +- schema.graphql | 39 +- .../components/CategoryList/CategoryList.tsx | 6 +- .../CategoryProductList.tsx | 1 - .../CollectionDetailsPage.tsx | 1 - .../CollectionList/CollectionList.tsx | 2 +- .../CollectionProducts/CollectionProducts.tsx | 13 +- .../ChannelsAvailabilityDropdown.stories.tsx | 2 +- .../ChannelsAvailabilityDropdown.tsx | 16 +- src/components/Filter/Filter.tsx | 23 +- .../Filter/FilterContent/FilterContent.tsx | 110 +- .../Filter/FilterContent/FilterErrorsList.tsx | 30 +- src/components/Filter/messages.ts | 18 + src/components/Filter/types.ts | 9 + src/components/Filter/utils.ts | 74 +- .../SingleAutocompleteSelectFieldContent.tsx | 9 +- src/components/StatusLabel/StatusLabel.tsx | 8 +- .../TableCellHeader/TableCellHeader.tsx | 15 +- .../DiscountProducts/DiscountProducts.tsx | 14 +- .../SaleDetailsPage/SaleDetailsPage.tsx | 1 - .../components/SaleList/SaleList.tsx | 4 + .../components/SaleListPage/filters.ts | 19 +- .../VoucherDetailsPage/VoucherDetailsPage.tsx | 1 - .../components/VoucherList/VoucherList.tsx | 7 + .../components/VoucherListPage/filters.ts | 19 +- src/discounts/queries.ts | 4 + src/discounts/types/SaleList.ts | 1 + src/discounts/types/VoucherList.ts | 1 + src/discounts/urls.ts | 6 +- src/discounts/views/SaleList/SaleList.tsx | 32 +- .../__snapshots__/filters.test.ts.snap | 3 +- src/discounts/views/SaleList/filters.test.ts | 10 + src/discounts/views/SaleList/filters.ts | 17 +- src/discounts/views/SaleList/sort.ts | 19 + .../views/VoucherList/VoucherList.tsx | 32 +- .../__snapshots__/filters.test.ts.snap | 3 +- .../views/VoucherList/filters.test.ts | 10 + src/discounts/views/VoucherList/filters.ts | 18 +- src/discounts/views/VoucherList/sort.ts | 21 + src/home/queries.ts | 3 +- src/misc.ts | 6 +- .../components/PluginsListPage/messages.ts | 4 +- .../ProductAvailabilityStatusLabel.tsx | 35 + .../ProductAvailabilityStatusLabel/index.ts | 2 + .../messages.ts | 16 + .../components/ProductList/ProductList.tsx | 34 +- .../components/ProductListPage/filters.ts | 22 +- src/products/queries.ts | 6 +- src/products/types/ProductCount.ts | 1 + src/products/types/ProductList.ts | 1 + src/products/urls.ts | 3 +- .../views/ProductList/ProductList.tsx | 42 +- .../__snapshots__/filters.test.ts.snap | 3 +- .../views/ProductList/filters.test.ts | 4 +- src/products/views/ProductList/filters.ts | 23 +- src/products/views/ProductList/fixtures.ts | 10 + src/products/views/ProductList/sort.ts | 28 +- .../__snapshots__/Stories.test.ts.snap | 2214 ++++++----------- .../stories/discounts/SaleListPage.tsx | 10 + .../stories/discounts/VoucherListPage.tsx | 10 + src/types/globalTypes.ts | 31 +- src/utils/maps.ts | 44 +- src/utils/sort.ts | 7 +- 64 files changed, 1529 insertions(+), 1698 deletions(-) create mode 100644 src/components/Filter/messages.ts create mode 100644 src/products/components/ProductAvailabilityStatusLabel/ProductAvailabilityStatusLabel.tsx create mode 100644 src/products/components/ProductAvailabilityStatusLabel/index.ts create mode 100644 src/products/components/ProductAvailabilityStatusLabel/messages.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c0f47dc7..e3f20e18b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ All notable, unreleased changes to this project will be documented in this file. - Fix label names in reference attributes - #1184 by @orzechdev - Fix failing product update with file attribute - #1190 by @orzechdev - Fix breaking select popups in filters - #1193 by @orzechdev +- Create channel filters in product, sales and voucher lists - #1187 by @jwm0 +- Add generic filter validation - #1187 by @jwm0 # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index ca7378749..e5709ddc9 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1986,14 +1986,14 @@ "context": "product channel publication status", "string": "hidden" }, - "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1484966255": { - "context": "product status title", - "string": "Available in {count}/{allCount}" - }, "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1702481199": { "context": "product channel publication date", "string": "published since {date}" }, + "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1944644572": { + "context": "product status title", + "string": "{count}/{allCount} channels" + }, "src_dot_components_dot_ChannelsAvailabilityDropdown_dot_3285520461": { "context": "product channel publication date", "string": "Will become available on {date}" @@ -2103,6 +2103,10 @@ "context": "button", "string": "Filters" }, + "src_dot_components_dot_Filter_dot_DEPENDENCIES_MISSING": { + "context": "filters error messages dependencies missing", + "string": "Filter requires other filters: {dependencies}" + }, "src_dot_components_dot_Filter_dot_FilterContent_dot_2779594451": { "context": "filter range separator", "string": "and" @@ -2110,6 +2114,14 @@ "src_dot_components_dot_Filter_dot_FilterContent_dot_996289613": { "string": "Filters" }, + "src_dot_components_dot_Filter_dot_UNKNOWN_ERROR": { + "context": "filters error messages unknown error", + "string": "Unknown error occurred" + }, + "src_dot_components_dot_Filter_dot_VALUE_REQUIRED": { + "context": "filters error messages value required", + "string": "Choose a value" + }, "src_dot_components_dot_ImageUpload_dot_1731007575": { "context": "image upload", "string": "Drop here to upload" @@ -2893,6 +2905,10 @@ "context": "sale status", "string": "Active" }, + "src_dot_discounts_dot_components_dot_SaleListPage_dot_channel": { + "context": "sale channel", + "string": "Channel" + }, "src_dot_discounts_dot_components_dot_SaleListPage_dot_expired": { "context": "sale status", "string": "Expired" @@ -3054,6 +3070,10 @@ "context": "voucher status", "string": "Active" }, + "src_dot_discounts_dot_components_dot_VoucherListPage_dot_channel": { + "context": "voucher channel", + "string": "Channel" + }, "src_dot_discounts_dot_components_dot_VoucherListPage_dot_expired": { "context": "voucher status", "string": "Expired" @@ -5007,8 +5027,8 @@ "string": "All Plugins" }, "src_dot_plugins_dot_components_dot_PluginsListPage_dot_active": { - "context": "plugin filters error messages active", - "string": "Active is not selected" + "context": "plugin filters error messages status", + "string": "Status is not selected" }, "src_dot_plugins_dot_components_dot_PluginsListPage_dot_channelStatusSectionSubtitle": { "context": "status section subtitle", @@ -5199,6 +5219,18 @@ "context": "products section name", "string": "Products" }, + "src_dot_products_dot_components_dot_ProductAvailabilityStatusLabel_dot_published": { + "context": "product publication date", + "string": "Published on {date}" + }, + "src_dot_products_dot_components_dot_ProductAvailabilityStatusLabel_dot_unpublished": { + "context": "product publication date", + "string": "Unpublished" + }, + "src_dot_products_dot_components_dot_ProductAvailabilityStatusLabel_dot_willBePublished": { + "context": "product publication date", + "string": "Becomes published on {date}" + }, "src_dot_products_dot_components_dot_ProductCategoryAndCollectionsForm_dot_1755013298": { "string": "Category" }, @@ -5364,6 +5396,10 @@ "context": "product status", "string": "Available" }, + "src_dot_products_dot_components_dot_ProductListPage_dot_channel": { + "context": "sales channel", + "string": "Channel" + }, "src_dot_products_dot_components_dot_ProductListPage_dot_hidden": { "context": "product is hidden", "string": "Hidden" diff --git a/schema.graphql b/schema.graphql index 6ff5c8903..d9ec858c8 100644 --- a/schema.graphql +++ b/schema.graphql @@ -506,13 +506,12 @@ input AttributeFilterInput { filterableInStorefront: Boolean filterableInDashboard: Boolean availableInGrid: Boolean - metadata: [MetadataInput] + metadata: [MetadataFilter] search: String ids: [ID] type: AttributeTypeEnum inCollection: ID inCategory: ID - channel: String } input AttributeInput { @@ -777,7 +776,7 @@ type CategoryDelete { input CategoryFilterInput { search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] ids: [ID] } @@ -798,7 +797,6 @@ enum CategorySortField { input CategorySortingInput { direction: OrderDirection! - channel: String field: CategorySortField! } @@ -1230,9 +1228,8 @@ enum CollectionErrorCode { input CollectionFilterInput { published: CollectionPublished search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] ids: [ID] - channel: String } input CollectionInput { @@ -1272,7 +1269,6 @@ enum CollectionSortField { input CollectionSortingInput { direction: OrderDirection! - channel: String field: CollectionSortField! } @@ -1670,7 +1666,7 @@ input CustomerFilterInput { numberOfOrders: IntRangeInput placedOrders: DateRangeInput search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] } input CustomerInput { @@ -2523,7 +2519,7 @@ enum MenuErrorCode { input MenuFilterInput { search: String slug: [String] - metadata: [MetadataInput] + metadata: [MetadataFilter] } input MenuInput { @@ -2588,7 +2584,7 @@ type MenuItemDelete { input MenuItemFilterInput { search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] } input MenuItemInput { @@ -2674,6 +2670,11 @@ enum MetadataErrorCode { REQUIRED } +input MetadataFilter { + key: String! + value: String +} + input MetadataInput { key: String! value: String! @@ -3132,7 +3133,7 @@ input OrderDraftFilterInput { customer: String created: DateRangeInput search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] channels: [ID] } @@ -3293,7 +3294,7 @@ input OrderFilterInput { customer: String created: DateRangeInput search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] channels: [ID] } @@ -3624,7 +3625,7 @@ enum PageErrorCode { input PageFilterInput { search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] pageTypes: [ID] ids: [ID] } @@ -4284,12 +4285,11 @@ input ProductFilterInput { stockAvailability: StockAvailability stocks: ProductStockFilterInput search: String - metadata: [MetadataInput] + metadata: [MetadataFilter] price: PriceRangeInput minimalPrice: PriceRangeInput productTypes: [ID] ids: [ID] - channel: String } type ProductImage { @@ -4374,7 +4374,6 @@ input ProductMediaUpdateInput { input ProductOrder { direction: OrderDirection! - channel: String attributeId: ID field: ProductOrderField } @@ -4499,7 +4498,7 @@ input ProductTypeFilterInput { search: String configurable: ProductTypeConfigurable productType: ProductTypeEnum - metadata: [MetadataInput] + metadata: [MetadataFilter] ids: [ID] } @@ -4644,7 +4643,7 @@ type ProductVariantDelete { input ProductVariantFilterInput { search: String sku: [String] - metadata: [MetadataInput] + metadata: [MetadataFilter] } input ProductVariantInput { @@ -4781,7 +4780,7 @@ type Query { checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection channel(id: ID): Channel channels: [Channel!] - attributes(filter: AttributeFilterInput, sortBy: AttributeSortingInput, before: String, after: String, first: Int, last: Int): AttributeCountableConnection + attributes(filter: AttributeFilterInput, sortBy: AttributeSortingInput, channel: String, before: String, after: String, first: Int, last: Int): AttributeCountableConnection attribute(id: ID, slug: String): Attribute appsInstallations: [AppInstallation!]! apps(filter: AppFilterInput, sortBy: AppSortingInput, before: String, after: String, first: Int, last: Int): AppCountableConnection @@ -4938,7 +4937,6 @@ enum SaleSortField { input SaleSortingInput { direction: OrderDirection! - channel: String field: SaleSortField! } @@ -5885,7 +5883,6 @@ enum VoucherSortField { input VoucherSortingInput { direction: OrderDirection! - channel: String field: VoucherSortField! } diff --git a/src/categories/components/CategoryList/CategoryList.tsx b/src/categories/components/CategoryList/CategoryList.tsx index 1f4685aea..63fe5e30e 100644 --- a/src/categories/components/CategoryList/CategoryList.tsx +++ b/src/categories/components/CategoryList/CategoryList.tsx @@ -94,7 +94,7 @@ const CategoryList: React.FC = props => { } arrowPosition="right" className={classes.colName} - disableClick={!isRoot} + disabled={!isRoot} onClick={() => isRoot && onSort(CategoryListUrlSortField.name)} > @@ -106,7 +106,7 @@ const CategoryList: React.FC = props => { : undefined } className={classes.colSubcategories} - disableClick={!isRoot} + disabled={!isRoot} onClick={() => isRoot && onSort(CategoryListUrlSortField.subcategoryCount) } @@ -123,7 +123,7 @@ const CategoryList: React.FC = props => { : undefined } className={classes.colProducts} - disableClick={!isRoot} + disabled={!isRoot} onClick={() => isRoot && onSort(CategoryListUrlSortField.productCount) } diff --git a/src/categories/components/CategoryProductList/CategoryProductList.tsx b/src/categories/components/CategoryProductList/CategoryProductList.tsx index 223908f34..9910e6436 100644 --- a/src/categories/components/CategoryProductList/CategoryProductList.tsx +++ b/src/categories/components/CategoryProductList/CategoryProductList.tsx @@ -194,7 +194,6 @@ export const CategoryProductList: React.FC = props => ) : product?.channelListings !== undefined ? ( ) : ( diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index b17bcef6f..d9bd1a0b0 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -102,7 +102,6 @@ const CollectionDetailsPage: React.FC = ({ diff --git a/src/collections/components/CollectionList/CollectionList.tsx b/src/collections/components/CollectionList/CollectionList.tsx index 9c4435fd1..199f038af 100644 --- a/src/collections/components/CollectionList/CollectionList.tsx +++ b/src/collections/components/CollectionList/CollectionList.tsx @@ -184,8 +184,8 @@ const CollectionList: React.FC = props => { channel ? ( ) : null ) : ( diff --git a/src/collections/components/CollectionProducts/CollectionProducts.tsx b/src/collections/components/CollectionProducts/CollectionProducts.tsx index d6e05dbc7..a37655f3b 100644 --- a/src/collections/components/CollectionProducts/CollectionProducts.tsx +++ b/src/collections/components/CollectionProducts/CollectionProducts.tsx @@ -23,7 +23,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { maybe, renderCollection } from "../../../misc"; -import { ChannelProps, ListActions, PageListProps } from "../../../types"; +import { ListActions, PageListProps } from "../../../types"; import { CollectionDetails_collection } from "../../types/CollectionDetails"; const useStyles = makeStyles( @@ -57,10 +57,7 @@ const useStyles = makeStyles( { name: "CollectionProducts" } ); -export interface CollectionProductsProps - extends PageListProps, - ListActions, - ChannelProps { +export interface CollectionProductsProps extends PageListProps, ListActions { collection: CollectionDetails_collection; channelsCount: number; onProductUnassign: (id: string, event: React.MouseEvent) => void; @@ -79,7 +76,6 @@ const CollectionProducts: React.FC = props => { onProductUnassign, onRowClick, pageInfo, - selectedChannelId, isChecked, selected, toggle, @@ -170,10 +166,6 @@ const CollectionProducts: React.FC = props => { mapEdgesToItems(collection?.products), product => { const isSelected = product ? isChecked(product.id) : false; - const channel = - product?.channelListings.find( - listing => listing.channel.id === selectedChannelId - ) || product?.channelListings[0]; return ( = props => { ) : product?.channelListings !== undefined ? ( ) : ( diff --git a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx index b54afa524..619225247 100644 --- a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx +++ b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.stories.tsx @@ -10,7 +10,7 @@ import ChannelsAvailabilityDropdown, { const props: ChannelsAvailabilityDropdownProps = { allChannelsCount: 6, channels: productChannels, - currentChannel: productChannels[0] + showStatus: true }; storiesOf("Generics / ChannelsAvailabilityDropdown", module) diff --git a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx index ce7e44e64..5e4078f30 100644 --- a/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx +++ b/src/components/ChannelsAvailabilityDropdown/ChannelsAvailabilityDropdown.tsx @@ -16,7 +16,7 @@ type Channels = Pick< export interface ChannelsAvailabilityDropdownProps { allChannelsCount: number; channels: Channels[]; - currentChannel: Channels; + showStatus?: boolean; } const isActive = (channelData: Channels) => channelData?.isPublished; @@ -24,7 +24,7 @@ const isActive = (channelData: Channels) => channelData?.isPublished; export const ChannelsAvailabilityDropdown: React.FC = ({ allChannelsCount, channels, - currentChannel + showStatus = false }) => { const intl = useIntl(); const classes = useStyles({}); @@ -32,8 +32,12 @@ export const ChannelsAvailabilityDropdown: React.FC setAnchorEl(event.currentTarget); - const handleClose = () => setAnchorEl(null); + const activeInAllChannels = React.useMemo( + () => showStatus && channels.every(isActive), + [channels, showStatus] + ); + return (
e.stopPropagation()}>
= props => { const anchor = React.useRef(); const [isFilterMenuOpened, setFilterMenuOpened] = useState(false); - const [filterErrors, setFilterErrors] = useState([]); + const [filterErrors, setFilterErrors] = useState>({}); const [data, dispatch, reset] = useFilter(menu); const isFilterActive = menu.some(filterElement => filterElement.active); @@ -111,17 +116,21 @@ const Filter: React.FC = props => { const handleSubmit = () => { const invalidFilters = extractInvalidFilters(data, menu); - if (!!invalidFilters.length) { - const parsedFilterErrors = invalidFilters.map(({ name }) => name); - setFilterErrors(parsedFilterErrors); + if (Object.keys(invalidFilters).length > 0) { + setFilterErrors(invalidFilters); return; } - setFilterErrors([]); + setFilterErrors({}); onFilterAdd(data); setFilterMenuOpened(false); }; + const handleClear = () => { + reset(); + setFilterErrors({}); + }; + return ( { @@ -196,7 +205,7 @@ const Filter: React.FC = props => { dataStructure={menu} currencySymbol={currencySymbol} filters={data} - onClear={reset} + onClear={handleClear} onFilterPropertyChange={dispatch} onFilterAttributeFocus={onFilterAttributeFocus} onSubmit={handleSubmit} diff --git a/src/components/Filter/FilterContent/FilterContent.tsx b/src/components/Filter/FilterContent/FilterContent.tsx index 54dbeac5a..7398d4c3f 100644 --- a/src/components/Filter/FilterContent/FilterContent.tsx +++ b/src/components/Filter/FilterContent/FilterContent.tsx @@ -16,9 +16,9 @@ import { FilterReducerAction } from "../reducer"; import { FieldType, FilterErrorMessages, - FilterErrors, IFilter, - IFilterElement + IFilterElement, + InvalidFilters } from "../types"; import FilterContentBody, { FilterContentBodyProps } from "./FilterContentBody"; import FilterContentBodyNameField from "./FilterContentBodyNameField"; @@ -80,7 +80,7 @@ export interface FilterContentProps { onSubmit: () => void; currencySymbol?: string; dataStructure: IFilter; - errors?: FilterErrors; + errors?: InvalidFilters; errorMessages?: FilterErrorMessages; } @@ -204,55 +204,63 @@ const FilterContent: React.FC = ({
{dataStructure .sort((a, b) => (a.name > b.name ? 1 : -1)) - .map(filter => ( - - } - classes={summaryClasses} - onClick={() => handleFilterOpen(filter)} + .map(filter => { + const currentFilter = getFilterFromCurrentData(filter); + + return ( + - - handleFilterPropertyGroupChange(action, filter) - } - /> - - - {filter.multipleFields ? ( - ( - - {filterField.label} - - )} - /> - ) : ( - - )} - - ))} + } + classes={summaryClasses} + onClick={() => handleFilterOpen(filter)} + > + + handleFilterPropertyGroupChange(action, filter) + } + /> + + {currentFilter.active && ( + + )} + {filter.multipleFields ? ( + ( + + {filterField.label} + + )} + /> + ) : ( + + )} + + ); + })} ); diff --git a/src/components/Filter/FilterContent/FilterErrorsList.tsx b/src/components/Filter/FilterContent/FilterErrorsList.tsx index 07ef5415e..c91ab3070 100644 --- a/src/components/Filter/FilterContent/FilterErrorsList.tsx +++ b/src/components/Filter/FilterContent/FilterErrorsList.tsx @@ -2,10 +2,12 @@ import { Typography } from "@material-ui/core"; import { fade, makeStyles } from "@material-ui/core/styles"; import InlineAlert from "@saleor/components/Alert/InlineAlert"; import { useStyles as useDotStyles } from "@saleor/components/StatusLabel"; +import errorTracker from "@saleor/services/errorTracking"; import classNames from "classnames"; import React from "react"; import { useIntl } from "react-intl"; +import { validationMessages } from "../messages"; import { FilterErrorMessages, FilterErrors, IFilterElement } from "../types"; const useStyles = makeStyles( @@ -36,7 +38,7 @@ interface FilterErrorsListProps { } const FilterErrorsList: React.FC = ({ - filter: { name, multipleFields }, + filter: { dependencies }, errors = [], errorMessages }) => { @@ -44,18 +46,20 @@ const FilterErrorsList: React.FC = ({ const dotClasses = useDotStyles({}); const intl = useIntl(); - const hasError = (fieldName: string) => - !!errors.find(errorName => errorName === fieldName); - - const hasErrorsToShow = () => { - if (!!multipleFields?.length) { - return multipleFields.some(multipleField => hasError(multipleField.name)); + const getErrorMessage = (code: string) => { + try { + return intl.formatMessage( + errorMessages?.[code] || validationMessages[code], + { dependencies: dependencies?.join() } + ); + } catch (e) { + errorTracker.captureException(e); + console.warn("Translation missing for filter error code: ", code); + return intl.formatMessage(validationMessages.UNKNOWN_ERROR); } - - return hasError(name); }; - if (!errors.length || !hasErrorsToShow()) { + if (!errors.length) { return null; } @@ -63,11 +67,11 @@ const FilterErrorsList: React.FC = ({
{!!errors.length && ( - {errors.map(fieldName => ( -
+ {errors.map(code => ( +
- {intl.formatMessage(errorMessages?.[fieldName])} + {getErrorMessage(code)}
))} diff --git a/src/components/Filter/messages.ts b/src/components/Filter/messages.ts new file mode 100644 index 000000000..ae272a2d8 --- /dev/null +++ b/src/components/Filter/messages.ts @@ -0,0 +1,18 @@ +import { defineMessages } from "react-intl"; + +import { ValidationErrorCode } from "./types"; + +export const validationMessages = defineMessages({ + VALUE_REQUIRED: { + defaultMessage: "Choose a value", + description: "filters error messages value required" + }, + DEPENDENCIES_MISSING: { + defaultMessage: "Filter requires other filters: {dependencies}", + description: "filters error messages dependencies missing" + }, + UNKNOWN_ERROR: { + defaultMessage: "Unknown error occurred", + description: "filters error messages unknown error" + } +}); diff --git a/src/components/Filter/types.ts b/src/components/Filter/types.ts index 0337ea79d..ff2720707 100644 --- a/src/components/Filter/types.ts +++ b/src/components/Filter/types.ts @@ -33,6 +33,7 @@ export interface IFilterElement required?: boolean; multipleFields?: IFilterElement[]; id?: string; + dependencies?: string[]; } export interface FilterBaseFieldProps { @@ -53,3 +54,11 @@ export enum FilterType { MULTIPLE = "MULTIPLE", SINGULAR = "SINGULAR" } + +export enum ValidationErrorCode { + VALUE_REQUIRED = "VALUE_REQUIRED", + DEPENDENCIES_MISSING = "DEPENDENCIES_MISSING", + UNKNOWN_ERROR = "UNKNOWN_ERROR" +} + +export type InvalidFilters = Record; diff --git a/src/components/Filter/utils.ts b/src/components/Filter/utils.ts index 739d52e9d..77a01855f 100644 --- a/src/components/Filter/utils.ts +++ b/src/components/Filter/utils.ts @@ -1,6 +1,11 @@ import compact from "lodash/compact"; -import { FieldType, IFilterElement } from "./types"; +import { + FieldType, + IFilterElement, + InvalidFilters, + ValidationErrorCode +} from "./types"; export const getByName = (nameToCompare: string) => (obj: { name: string }) => obj.name === nameToCompare; @@ -20,6 +25,8 @@ export const isFilterFieldValid = function( case FieldType.boolean: case FieldType.autocomplete: return isAutocompleteFilterFieldValid(filter); + case FieldType.options: + return !!filter.value[0]; default: return true; @@ -27,42 +34,73 @@ export const isFilterFieldValid = function( }; export const isFilterValid = function( - resultFilters: Array>, filter: IFilterElement ) { const { required, active } = filter; - if (!required || !active) { - return resultFilters; + if (!required && !active) { + return true; } - return isFilterFieldValid(filter) - ? resultFilters - : [...resultFilters, filter]; + return isFilterFieldValid(filter); }; export const extractInvalidFilters = function( filtersData: Array>, filtersDataStructure: Array> -) { +): InvalidFilters { return filtersDataStructure.reduce( - (resultFilters, { name, multipleFields }) => { + (invalidFilters, { name, multipleFields, dependencies }) => { const filter = filtersData.find(getByName(name)); + let errors: string[] = []; const shouldExtractChildrenFields = filter.active && !!multipleFields?.length; - if (shouldExtractChildrenFields) { - return multipleFields - .map(field => { - const dataField = filtersData.find(getByName(field.name)); - return { ...dataField, active: true }; - }) - .reduce(isFilterValid, resultFilters); + // if filter is inactive we skip entire validation + if (!filter.active) { + return invalidFilters; } - return isFilterValid(resultFilters, filter); + if (!isFilterValid(filter)) { + errors.push(ValidationErrorCode.VALUE_REQUIRED); + } + + if (shouldExtractChildrenFields) { + const multipleFieldErrors = multipleFields + .map(field => { + const filter = filtersData.find(getByName(field.name)); + return { ...filter, active: true }; + }) + .filter(el => !isFilterValid(el)) + .map(({ name }) => name); + + errors = [...errors, ...multipleFieldErrors]; + } + + // check if filter depends on other filters + if (dependencies?.length > 0) { + const deps = dependencies + .map(name => { + const filter = filtersData.find(getByName(name)); + return { ...filter, required: true }; + }) + .filter(el => !isFilterValid(el)); + + if (deps.length > 0) { + errors.push(ValidationErrorCode.DEPENDENCIES_MISSING); + } + } + + if (errors.length === 0) { + return invalidFilters; + } + + return { + ...invalidFilters, + [name]: errors + }; }, - [] + {} as InvalidFilters ); }; diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx index df244f096..5e26d0f8f 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectFieldContent.tsx @@ -23,9 +23,12 @@ const menuItemHeight = 46; const maxMenuItems = 5; const offset = 24; -export interface SingleAutocompleteChoiceType { +export type ChoiceValue = string; +export interface SingleAutocompleteChoiceType< + T extends ChoiceValue = ChoiceValue +> { label: string; - value: any; + value: T; } export interface SingleAutocompleteActionType { label: string; @@ -303,7 +306,7 @@ const SingleAutocompleteSelectFieldContent: React.FC
= props => { @@ -65,16 +66,22 @@ const TableCellHeader: React.FC = props => { children, className, direction, - disableClick, textAlign, + disabled = false, + onClick, ...rest } = props; return ( { + if (!disabled) { + onClick(e); + } + }} className={classNames(classes.root, className, { - [classes.disableClick]: disableClick + [classes.disabled]: disabled })} >
void; @@ -83,7 +80,6 @@ const DiscountProducts: React.FC = props => { onNextPage, isChecked, selected, - selectedChannelId, toggle, toggleAll, toolbar @@ -162,10 +158,7 @@ const DiscountProducts: React.FC = props => { mapEdgesToItems(sale?.products), product => { const isSelected = product ? isChecked(product.id) : false; - const channel = - product?.channelListings.find( - listing => listing.channel.id === selectedChannelId - ) || product?.channelListings[0]; + return ( = props => { ) : product?.channelListings !== undefined ? ( ) : ( diff --git a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx index 92d3ea97f..edceb442b 100644 --- a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx +++ b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx @@ -274,7 +274,6 @@ const SaleDetailsPage: React.FC = ({ pageInfo={pageInfo} discount={sale} channelsCount={allChannelsCount} - selectedChannelId={selectedChannelId} isChecked={isChecked} selected={selected} toggle={toggle} diff --git a/src/discounts/components/SaleList/SaleList.tsx b/src/discounts/components/SaleList/SaleList.tsx index adb11c9b1..f6d650a56 100644 --- a/src/discounts/components/SaleList/SaleList.tsx +++ b/src/discounts/components/SaleList/SaleList.tsx @@ -9,6 +9,7 @@ import TableCellHeader from "@saleor/components/TableCellHeader"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; import { SaleListUrlSortField } from "@saleor/discounts/urls"; +import { canBeSorted } from "@saleor/discounts/views/SaleList/sort"; import { maybe, renderCollection } from "@saleor/misc"; import { makeStyles } from "@saleor/theme"; import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types"; @@ -141,6 +142,9 @@ const SaleList: React.FC = props => { } textAlign="right" onClick={() => onSort(SaleListUrlSortField.value)} + disabled={ + !canBeSorted(SaleListUrlSortField.value, !!selectedChannelId) + } className={classes.colValue} > diff --git a/src/discounts/components/SaleListPage/filters.ts b/src/discounts/components/SaleListPage/filters.ts index eff413b00..6c11c026b 100644 --- a/src/discounts/components/SaleListPage/filters.ts +++ b/src/discounts/components/SaleListPage/filters.ts @@ -1,4 +1,5 @@ import { IFilter } from "@saleor/components/Filter"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { FilterOpts, MinMax } from "@saleor/types"; import { DiscountStatusEnum, @@ -13,13 +14,15 @@ import { defineMessages, IntlShape } from "react-intl"; export enum SaleFilterKeys { saleType = "saleType", started = "started", - status = "status" + status = "status", + channel = "channel" } export interface SaleListFilterOpts { saleType: FilterOpts; started: FilterOpts; status: FilterOpts; + channel: FilterOpts & { choices: MultiAutocompleteChoiceType[] }; } const messages = defineMessages({ @@ -27,6 +30,10 @@ const messages = defineMessages({ defaultMessage: "Active", description: "sale status" }, + channel: { + defaultMessage: "Channel", + description: "sale channel" + }, expired: { defaultMessage: "Expired", description: "sale status" @@ -61,6 +68,16 @@ export function createFilterStructure( opts: SaleListFilterOpts ): IFilter { return [ + { + ...createOptionsField( + SaleFilterKeys.channel, + intl.formatMessage(messages.channel), + [opts.channel.value], + false, + opts.channel.choices + ), + active: opts.channel.active + }, { ...createDateField( SaleFilterKeys.started, diff --git a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx index 4f3f65ecf..639fb6c30 100644 --- a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx +++ b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx @@ -346,7 +346,6 @@ const VoucherDetailsPage: React.FC = ({ onRowClick={onProductClick} pageInfo={pageInfo} discount={voucher} - selectedChannelId={selectedChannelId} channelsCount={allChannelsCount} isChecked={isChecked} selected={selected} diff --git a/src/discounts/components/VoucherList/VoucherList.tsx b/src/discounts/components/VoucherList/VoucherList.tsx index 0ca931d46..6e21214b6 100644 --- a/src/discounts/components/VoucherList/VoucherList.tsx +++ b/src/discounts/components/VoucherList/VoucherList.tsx @@ -9,6 +9,7 @@ import TableCellHeader from "@saleor/components/TableCellHeader"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; import { VoucherListUrlSortField } from "@saleor/discounts/urls"; +import { canBeSorted } from "@saleor/discounts/views/VoucherList/sort"; import { maybe, renderCollection } from "@saleor/misc"; import { makeStyles } from "@saleor/theme"; import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types"; @@ -130,6 +131,9 @@ const VoucherList: React.FC = props => { } textAlign="right" onClick={() => onSort(VoucherListUrlSortField.minSpent)} + disabled={ + !canBeSorted(VoucherListUrlSortField.minSpent, !!selectedChannelId) + } className={classes.colMinSpent} > = props => { } textAlign="right" onClick={() => onSort(VoucherListUrlSortField.value)} + disabled={ + !canBeSorted(VoucherListUrlSortField.minSpent, !!selectedChannelId) + } className={classes.colValue} > ; status: FilterOpts; timesUsed: FilterOpts; + channel: FilterOpts & { choices: MultiAutocompleteChoiceType[] }; } const messages = defineMessages({ @@ -30,6 +33,10 @@ const messages = defineMessages({ defaultMessage: "Active", description: "voucher status" }, + channel: { + defaultMessage: "Channel", + description: "voucher channel" + }, expired: { defaultMessage: "Expired", description: "voucher status" @@ -68,6 +75,16 @@ export function createFilterStructure( opts: VoucherListFilterOpts ): IFilter { return [ + { + ...createOptionsField( + VoucherFilterKeys.channel, + intl.formatMessage(messages.channel), + [opts.channel.value], + false, + opts.channel.choices + ), + active: opts.channel.active + }, { ...createDateField( VoucherFilterKeys.started, diff --git a/src/discounts/queries.ts b/src/discounts/queries.ts index 606002089..d45203fb9 100644 --- a/src/discounts/queries.ts +++ b/src/discounts/queries.ts @@ -26,6 +26,7 @@ export const saleList = gql` $last: Int $filter: SaleFilterInput $sort: SaleSortingInput + $channel: String ) { sales( after: $after @@ -34,6 +35,7 @@ export const saleList = gql` last: $last filter: $filter sortBy: $sort + channel: $channel ) { edges { node { @@ -60,6 +62,7 @@ export const voucherList = gql` $last: Int $filter: VoucherFilterInput $sort: VoucherSortingInput + $channel: String ) { vouchers( after: $after @@ -68,6 +71,7 @@ export const voucherList = gql` last: $last filter: $filter sortBy: $sort + channel: $channel ) { edges { node { diff --git a/src/discounts/types/SaleList.ts b/src/discounts/types/SaleList.ts index cb60d72cc..7ab2a80db 100644 --- a/src/discounts/types/SaleList.ts +++ b/src/discounts/types/SaleList.ts @@ -64,4 +64,5 @@ export interface SaleListVariables { last?: number | null; filter?: SaleFilterInput | null; sort?: SaleSortingInput | null; + channel?: string | null; } diff --git a/src/discounts/types/VoucherList.ts b/src/discounts/types/VoucherList.ts index be2643991..b792fdec9 100644 --- a/src/discounts/types/VoucherList.ts +++ b/src/discounts/types/VoucherList.ts @@ -81,4 +81,5 @@ export interface VoucherListVariables { last?: number | null; filter?: VoucherFilterInput | null; sort?: VoucherSortingInput | null; + channel?: string | null; } diff --git a/src/discounts/urls.ts b/src/discounts/urls.ts index 5d555e4ad..b26fffced 100644 --- a/src/discounts/urls.ts +++ b/src/discounts/urls.ts @@ -23,7 +23,8 @@ export enum SaleListUrlFiltersEnum { type = "type", startedFrom = "startedFrom", startedTo = "startedTo", - query = "query" + query = "query", + channel = "channel" } export enum SaleListUrlFiltersWithMultipleValues { status = "status" @@ -75,7 +76,8 @@ export enum VoucherListUrlFiltersEnum { startedTo = "startedTo", timesUsedFrom = "timesUsedFrom", timesUsedTo = "timesUsedTo", - query = "query" + query = "query", + channel = "channel" } export enum VoucherListUrlFiltersWithMultipleValues { status = "status", diff --git a/src/discounts/views/SaleList/SaleList.tsx b/src/discounts/views/SaleList/SaleList.tsx index 3e24d5ab1..d4e46c44c 100644 --- a/src/discounts/views/SaleList/SaleList.tsx +++ b/src/discounts/views/SaleList/SaleList.tsx @@ -20,9 +20,9 @@ import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; -import { mapEdgesToItems } from "@saleor/utils/maps"; +import { mapEdgesToItems, mapNodeToChoice } from "@saleor/utils/maps"; import { getSortParams } from "@saleor/utils/sort"; -import React from "react"; +import React, { useEffect } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import SaleListPage from "../../components/SaleListPage"; @@ -46,7 +46,7 @@ import { getFilterVariables, saveFilterTab } from "./filters"; -import { getSortQueryVariables } from "./sort"; +import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort"; interface SaleListProps { params: SaleListUrlQueryParams; @@ -63,7 +63,13 @@ export const SaleList: React.FC = ({ params }) => { ListViews.SALES_LIST ); const intl = useIntl(); - const { channel } = useAppChannel(); + const { availableChannels } = useAppChannel(false); + const selectedChannel = availableChannels.find( + channel => channel.slug === params.channel + ); + const channelOpts = availableChannels + ? mapNodeToChoice(availableChannels, channel => channel.slug) + : null; const [openModal, closeModal] = createDialogActionHandlers< SaleListUrlDialog, @@ -75,7 +81,8 @@ export const SaleList: React.FC = ({ params }) => { () => ({ ...paginationState, filter: getFilterVariables(params), - sort: getSortQueryVariables(params, channel?.slug) + sort: getSortQueryVariables(params), + channel: params.channel }), [params] ); @@ -105,6 +112,17 @@ export const SaleList: React.FC = ({ params }) => { params }); + useEffect(() => { + if (!canBeSorted(params.sort, !!selectedChannel)) { + navigate( + saleListUrl({ + ...params, + sort: DEFAULT_SORT_KEY + }) + ); + } + }, [params]); + const handleTabChange = (tab: number) => { reset(); navigate( @@ -163,7 +181,7 @@ export const SaleList: React.FC = ({ params }) => { changeFilters(filter)} @@ -199,7 +217,7 @@ export const SaleList: React.FC = ({ params }) => { } - selectedChannelId={channel?.id} + selectedChannelId={selectedChannel?.id} /> { const intl = createIntl(config); const filters = createFilterStructure(intl, { + channel: { + active: false, + choices: [ + { + value: "default-channel", + label: "Default channel" + } + ], + value: "default-channel" + }, saleType: { active: false, value: DiscountValueTypeEnum.FIXED diff --git a/src/discounts/views/SaleList/filters.ts b/src/discounts/views/SaleList/filters.ts index 7e40dfba0..6f9d6e0dc 100644 --- a/src/discounts/views/SaleList/filters.ts +++ b/src/discounts/views/SaleList/filters.ts @@ -1,4 +1,5 @@ import { IFilterElement } from "@saleor/components/Filter"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { SaleFilterKeys, SaleListFilterOpts @@ -17,7 +18,8 @@ import { getGteLteVariables, getMinMaxQueryParam, getMultipleEnumValueQueryParam, - getSingleEnumValueQueryParam + getSingleEnumValueQueryParam, + getSingleValueQueryParam } from "../../../utils/filters"; import { SaleListUrlFilters, @@ -28,8 +30,16 @@ import { export const SALE_FILTERS_KEY = "saleFilters"; -export function getFilterOpts(params: SaleListUrlFilters): SaleListFilterOpts { +export function getFilterOpts( + params: SaleListUrlFilters, + channels: SingleAutocompleteChoiceType[] +): SaleListFilterOpts { return { + channel: { + active: params?.channel !== undefined, + choices: channels, + value: params?.channel + }, saleType: { active: !!maybe(() => params.type), value: maybe(() => findValueInEnum(params.type, DiscountValueTypeEnum)) @@ -105,6 +115,9 @@ export function getFilterQueryParam( SaleListUrlFiltersWithMultipleValues.status, DiscountStatusEnum ); + + case SaleFilterKeys.channel: + return getSingleValueQueryParam(filter, SaleListUrlFiltersEnum.channel); } } diff --git a/src/discounts/views/SaleList/sort.ts b/src/discounts/views/SaleList/sort.ts index dc13157aa..6b93172df 100644 --- a/src/discounts/views/SaleList/sort.ts +++ b/src/discounts/views/SaleList/sort.ts @@ -2,6 +2,25 @@ import { SaleListUrlSortField } from "@saleor/discounts/urls"; import { SaleSortField } from "@saleor/types/globalTypes"; import { createGetSortQueryVariables } from "@saleor/utils/sort"; +export const DEFAULT_SORT_KEY = SaleListUrlSortField.name; + +export function canBeSorted( + sort: SaleListUrlSortField, + isChannelSelected: boolean +) { + switch (sort) { + case SaleListUrlSortField.name: + case SaleListUrlSortField.startDate: + case SaleListUrlSortField.endDate: + case SaleListUrlSortField.type: + return true; + case SaleListUrlSortField.value: + return isChannelSelected; + default: + return false; + } +} + export function getSortQueryField(sort: SaleListUrlSortField): SaleSortField { switch (sort) { case SaleListUrlSortField.name: diff --git a/src/discounts/views/VoucherList/VoucherList.tsx b/src/discounts/views/VoucherList/VoucherList.tsx index 1992b6e3d..9fc0b45f1 100644 --- a/src/discounts/views/VoucherList/VoucherList.tsx +++ b/src/discounts/views/VoucherList/VoucherList.tsx @@ -20,9 +20,9 @@ import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createSortHandler from "@saleor/utils/handlers/sortHandler"; -import { mapEdgesToItems } from "@saleor/utils/maps"; +import { mapEdgesToItems, mapNodeToChoice } from "@saleor/utils/maps"; import { getSortParams } from "@saleor/utils/sort"; -import React from "react"; +import React, { useEffect } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import VoucherListPage from "../../components/VoucherListPage"; @@ -46,7 +46,7 @@ import { getFilterVariables, saveFilterTab } from "./filters"; -import { getSortQueryVariables } from "./sort"; +import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort"; interface VoucherListProps { params: VoucherListUrlQueryParams; @@ -64,7 +64,13 @@ export const VoucherList: React.FC = ({ params }) => { ); const intl = useIntl(); - const { channel } = useAppChannel(); + const { availableChannels } = useAppChannel(false); + const selectedChannel = availableChannels.find( + channel => channel.slug === params.channel + ); + const channelOpts = availableChannels + ? mapNodeToChoice(availableChannels, channel => channel.slug) + : null; const [openModal, closeModal] = createDialogActionHandlers< VoucherListUrlDialog, @@ -76,7 +82,8 @@ export const VoucherList: React.FC = ({ params }) => { () => ({ ...paginationState, filter: getFilterVariables(params), - sort: getSortQueryVariables(params, channel?.slug) + sort: getSortQueryVariables(params), + channel: params.channel }), [params] ); @@ -106,6 +113,17 @@ export const VoucherList: React.FC = ({ params }) => { params }); + useEffect(() => { + if (!canBeSorted(params.sort, !!selectedChannel)) { + navigate( + voucherListUrl({ + ...params, + sort: DEFAULT_SORT_KEY + }) + ); + } + }, [params]); + const handleTabChange = (tab: number) => { reset(); navigate( @@ -164,7 +182,7 @@ export const VoucherList: React.FC = ({ params }) => { changeFilters(filter)} @@ -200,7 +218,7 @@ export const VoucherList: React.FC = ({ params }) => { } - selectedChannelId={channel?.id} + selectedChannelId={selectedChannel?.id} /> { const intl = createIntl(config); const filters = createFilterStructure(intl, { + channel: { + active: false, + choices: [ + { + value: "default-channel", + label: "Default channel" + } + ], + value: "default-channel" + }, saleType: { active: false, value: [VoucherDiscountType.FIXED, VoucherDiscountType.SHIPPING] diff --git a/src/discounts/views/VoucherList/filters.ts b/src/discounts/views/VoucherList/filters.ts index ff3623fe0..aab32f5aa 100644 --- a/src/discounts/views/VoucherList/filters.ts +++ b/src/discounts/views/VoucherList/filters.ts @@ -1,4 +1,5 @@ import { IFilterElement } from "@saleor/components/Filter"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { VoucherFilterKeys, VoucherListFilterOpts @@ -16,7 +17,8 @@ import { dedupeFilter, getGteLteVariables, getMinMaxQueryParam, - getMultipleEnumValueQueryParam + getMultipleEnumValueQueryParam, + getSingleValueQueryParam } from "../../../utils/filters"; import { VoucherListUrlFilters, @@ -28,9 +30,15 @@ import { export const VOUCHER_FILTERS_KEY = "voucherFilters"; export function getFilterOpts( - params: VoucherListUrlFilters + params: VoucherListUrlFilters, + channels: SingleAutocompleteChoiceType[] ): VoucherListFilterOpts { return { + channel: { + active: params?.channel !== undefined, + choices: channels, + value: params?.channel + }, saleType: { active: !!maybe(() => params.type), value: maybe( @@ -137,6 +145,12 @@ export function getFilterQueryParam( VoucherListUrlFiltersWithMultipleValues.status, DiscountStatusEnum ); + + case VoucherFilterKeys.channel: + return getSingleValueQueryParam( + filter, + VoucherListUrlFiltersEnum.channel + ); } } diff --git a/src/discounts/views/VoucherList/sort.ts b/src/discounts/views/VoucherList/sort.ts index 21721ec54..d7603f07e 100644 --- a/src/discounts/views/VoucherList/sort.ts +++ b/src/discounts/views/VoucherList/sort.ts @@ -2,6 +2,27 @@ import { VoucherListUrlSortField } from "@saleor/discounts/urls"; import { VoucherSortField } from "@saleor/types/globalTypes"; import { createGetSortQueryVariables } from "@saleor/utils/sort"; +export const DEFAULT_SORT_KEY = VoucherListUrlSortField.code; + +export function canBeSorted( + sort: VoucherListUrlSortField, + isChannelSelected: boolean +) { + switch (sort) { + case VoucherListUrlSortField.code: + case VoucherListUrlSortField.startDate: + case VoucherListUrlSortField.endDate: + case VoucherListUrlSortField.type: + case VoucherListUrlSortField.limit: + return true; + case VoucherListUrlSortField.value: + case VoucherListUrlSortField.minSpent: + return isChannelSelected; + default: + return false; + } +} + export function getSortQueryField( sort: VoucherListUrlSortField ): VoucherSortField { diff --git a/src/home/queries.ts b/src/home/queries.ts index 88be73634..c998d3210 100644 --- a/src/home/queries.ts +++ b/src/home/queries.ts @@ -34,7 +34,8 @@ const home = gql` totalCount } productsOutOfStock: products( - filter: { stockAvailability: OUT_OF_STOCK, channel: $channel } + filter: { stockAvailability: OUT_OF_STOCK } + channel: $channel ) { totalCount } diff --git a/src/misc.ts b/src/misc.ts index 875acea6b..bde1e26b8 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -5,6 +5,7 @@ import urlJoin from "url-join"; import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton"; import { StatusType } from "./components/StatusChip/types"; +import { StatusLabelProps } from "./components/StatusLabel"; import { APP_MOUNT_URI } from "./config"; import { AddressType, AddressTypeInput } from "./customers/types"; import { @@ -90,7 +91,10 @@ const paymentStatusMessages = defineMessages({ } }); -export const transformPaymentStatus = (status: string, intl: IntlShape) => { +export const transformPaymentStatus = ( + status: string, + intl: IntlShape +): { localized: string; status: StatusLabelProps["status"] } => { switch (status) { case PaymentChargeStatusEnum.PARTIALLY_CHARGED: return { diff --git a/src/plugins/components/PluginsListPage/messages.ts b/src/plugins/components/PluginsListPage/messages.ts index b502a559e..389a2b7be 100644 --- a/src/plugins/components/PluginsListPage/messages.ts +++ b/src/plugins/components/PluginsListPage/messages.ts @@ -2,8 +2,8 @@ import { defineMessages } from "react-intl"; export const pluginsFilterErrorMessages = defineMessages({ active: { - defaultMessage: "Active is not selected", - description: "plugin filters error messages active" + defaultMessage: "Status is not selected", + description: "plugin filters error messages status" }, channels: { defaultMessage: "No channels selected", diff --git a/src/products/components/ProductAvailabilityStatusLabel/ProductAvailabilityStatusLabel.tsx b/src/products/components/ProductAvailabilityStatusLabel/ProductAvailabilityStatusLabel.tsx new file mode 100644 index 000000000..cd6051044 --- /dev/null +++ b/src/products/components/ProductAvailabilityStatusLabel/ProductAvailabilityStatusLabel.tsx @@ -0,0 +1,35 @@ +import StatusLabel from "@saleor/components/StatusLabel"; +import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import React from "react"; +import { useIntl } from "react-intl"; + +import { messages } from "./messages"; + +export const ProductAvailabilityStatusLabel = ({ channel }) => { + const intl = useIntl(); + const localizeDate = useDateLocalize(); + + return ( + + ); +}; + +export default ProductAvailabilityStatusLabel; diff --git a/src/products/components/ProductAvailabilityStatusLabel/index.ts b/src/products/components/ProductAvailabilityStatusLabel/index.ts new file mode 100644 index 000000000..602f172eb --- /dev/null +++ b/src/products/components/ProductAvailabilityStatusLabel/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ProductAvailabilityStatusLabel"; +export * from "./ProductAvailabilityStatusLabel"; diff --git a/src/products/components/ProductAvailabilityStatusLabel/messages.ts b/src/products/components/ProductAvailabilityStatusLabel/messages.ts new file mode 100644 index 000000000..63bf0377e --- /dev/null +++ b/src/products/components/ProductAvailabilityStatusLabel/messages.ts @@ -0,0 +1,16 @@ +import { defineMessages } from "react-intl"; + +export const messages = defineMessages({ + published: { + defaultMessage: "Published on {date}", + description: "product publication date" + }, + unpublished: { + defaultMessage: "Unpublished", + description: "product publication date" + }, + willBePublished: { + defaultMessage: "Becomes published on {date}", + description: "product publication date" + } +}); diff --git a/src/products/components/ProductList/ProductList.tsx b/src/products/components/ProductList/ProductList.tsx index 4e0aa9ab2..dcac727a5 100644 --- a/src/products/components/ProductList/ProductList.tsx +++ b/src/products/components/ProductList/ProductList.tsx @@ -24,6 +24,7 @@ import { import { GridAttributes_grid_edges_node } from "@saleor/products/types/GridAttributes"; import { ProductList_products_edges_node } from "@saleor/products/types/ProductList"; import { ProductListUrlSortField } from "@saleor/products/urls"; +import { canBeSorted } from "@saleor/products/views/ProductList/sort"; import { makeStyles } from "@saleor/theme"; import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types"; import TDisplayColumn, { @@ -34,6 +35,8 @@ import classNames from "classnames"; import React from "react"; import { FormattedMessage } from "react-intl"; +import ProductAvailabilityStatusLabel from "../ProductAvailabilityStatusLabel"; + const useStyles = makeStyles( theme => ({ [theme.breakpoints.up("lg")]: { @@ -215,6 +218,12 @@ export const ProductList: React.FC = props => { : undefined } onClick={() => onSort(ProductListUrlSortField.status)} + disabled={ + !canBeSorted( + ProductListUrlSortField.status, + !!selectedChannelId + ) + } > = props => { } textAlign="right" onClick={() => onSort(ProductListUrlSortField.price)} + disabled={ + !canBeSorted(ProductListUrlSortField.price, !!selectedChannelId) + } > = props => { !!product?.channelListings?.length } > - {product && !product?.channelListings?.length ? ( - "-" - ) : product?.channelListings !== undefined ? ( - - ) : ( - - )} + {(!product && ) || + (!product?.channelListings?.length && "-") || + (product?.channelListings !== undefined && channel ? ( + + ) : ( + + ))} {gridAttributesFromSettings.map(gridAttribute => ( diff --git a/src/products/components/ProductListPage/filters.ts b/src/products/components/ProductListPage/filters.ts index 78ec3b135..3b7462f7c 100644 --- a/src/products/components/ProductListPage/filters.ts +++ b/src/products/components/ProductListPage/filters.ts @@ -1,4 +1,5 @@ import { IFilter } from "@saleor/components/Filter"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { commonMessages, sectionNames } from "@saleor/intl"; import { AutocompleteFilterOpts, FilterOpts, MinMax } from "@saleor/types"; import { @@ -19,7 +20,8 @@ export enum ProductFilterKeys { collections = "collections", price = "price", productType = "productType", - stock = "stock" + stock = "stock", + channel = "channel" } export interface ProductListFilterOpts { @@ -37,6 +39,7 @@ export interface ProductListFilterOpts { price: FilterOpts; productType: FilterOpts & AutocompleteFilterOpts; stockStatus: FilterOpts; + channel: FilterOpts & { choices: SingleAutocompleteChoiceType[] }; } const messages = defineMessages({ @@ -44,6 +47,10 @@ const messages = defineMessages({ defaultMessage: "Available", description: "product status" }, + channel: { + defaultMessage: "Channel", + description: "sales channel" + }, hidden: { defaultMessage: "Hidden", description: "product is hidden" @@ -81,6 +88,16 @@ export function createFilterStructure( ); return [ + { + ...createOptionsField( + ProductFilterKeys.channel, + intl.formatMessage(messages.channel), + [opts.channel.value], + false, + opts.channel.choices + ), + active: opts.channel.active + }, { ...createOptionsField( ProductFilterKeys.stock, @@ -98,7 +115,8 @@ export function createFilterStructure( } ] ), - active: opts.stockStatus.active + active: opts.stockStatus.active, + dependencies: [ProductFilterKeys.channel] }, { ...createPriceField( diff --git a/src/products/queries.ts b/src/products/queries.ts index eac8de0a4..bf0401de0 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -139,6 +139,7 @@ const productListQuery = gql` $last: Int $before: String $filter: ProductFilterInput + $channel: String $sort: ProductOrder ) { products( @@ -148,6 +149,7 @@ const productListQuery = gql` last: $last filter: $filter sortBy: $sort + channel: $channel ) { edges { node { @@ -177,8 +179,8 @@ export const useProductListQuery = makeQuery( ); const productCountQuery = gql` - query ProductCount($filter: ProductFilterInput) { - products(filter: $filter) { + query ProductCount($filter: ProductFilterInput, $channel: String) { + products(filter: $filter, channel: $channel) { totalCount } } diff --git a/src/products/types/ProductCount.ts b/src/products/types/ProductCount.ts index ccef6bf33..302ae6b37 100644 --- a/src/products/types/ProductCount.ts +++ b/src/products/types/ProductCount.ts @@ -20,4 +20,5 @@ export interface ProductCount { export interface ProductCountVariables { filter?: ProductFilterInput | null; + channel?: string | null; } diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts index 593cd623f..031a9b896 100644 --- a/src/products/types/ProductList.ts +++ b/src/products/types/ProductList.ts @@ -140,5 +140,6 @@ export interface ProductListVariables { last?: number | null; before?: string | null; filter?: ProductFilterInput | null; + channel?: string | null; sort?: ProductOrder | null; } diff --git a/src/products/urls.ts b/src/products/urls.ts index 427871847..a7b73b68c 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -28,7 +28,8 @@ export enum ProductListUrlFiltersEnum { priceTo = "priceTo", status = "status", stockStatus = "stockStatus", - query = "query" + query = "query", + channel = "channel" } export enum ProductListUrlFiltersWithMultipleValues { categories = "categories", diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index 22d797043..578de111d 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -55,7 +55,7 @@ import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { ListViews } from "@saleor/types"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; -import { mapEdgesToItems } from "@saleor/utils/maps"; +import { mapEdgesToItems, mapNodeToChoice } from "@saleor/utils/maps"; import { getSortUrlVariables } from "@saleor/utils/sort"; import { useWarehouseList } from "@saleor/warehouses/queries"; import React, { useEffect, useState } from "react"; @@ -76,7 +76,7 @@ import { getFilterVariables, saveFilterTab } from "./filters"; -import { getSortQueryVariables } from "./sort"; +import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort"; interface ProductListProps { params: ProductListUrlQueryParams; @@ -160,14 +160,16 @@ export const ProductList: React.FC = ({ params }) => { }, skip: params.action !== "export" }); - const { availableChannels, channel } = useAppChannel(); + const { availableChannels } = useAppChannel(false); const limitOpts = useShopLimitsQuery({ variables: { productVariants: true } }); - const noChannel = !channel && typeof channel !== "undefined"; + const selectedChannel = availableChannels.find( + channel => channel.slug === params.channel + ); const [openModal, closeModal] = createDialogActionHandlers< ProductListUrlDialog, @@ -238,7 +240,7 @@ export const ProductList: React.FC = ({ params }) => { const sortWithQuery = ProductListUrlSortField.rank; const sortWithoutQuery = params.sort === ProductListUrlSortField.rank - ? ProductListUrlSortField.name + ? DEFAULT_SORT_KEY : params.sort; navigate( productListUrl({ @@ -249,6 +251,17 @@ export const ProductList: React.FC = ({ params }) => { ); }, [params.query]); + useEffect(() => { + if (!canBeSorted(params.sort, !!selectedChannel)) { + navigate( + productListUrl({ + ...params, + sort: DEFAULT_SORT_KEY + }) + ); + } + }, [params]); + const handleTabChange = (tab: number) => { reset(); navigate( @@ -281,17 +294,21 @@ export const ProductList: React.FC = ({ params }) => { ); const paginationState = createPaginationState(settings.rowNumber, params); - const channelSlug = noChannel ? null : channel.slug; - const filter = getFilterVariables(params, channelSlug); - const sort = getSortQueryVariables(params, channelSlug); + const channelOpts = availableChannels + ? mapNodeToChoice(availableChannels, channel => channel.slug) + : null; + const filter = getFilterVariables(params, !!selectedChannel); + const sort = getSortQueryVariables(params, !!selectedChannel); const queryVariables = React.useMemo( () => ({ ...paginationState, filter, - sort + sort, + channel: selectedChannel?.slug }), [params, settings.rowNumber] ); + // TODO: When channel is undefined we should skip detailed pricing listings const { data, loading, refetch } = useProductListQuery({ displayLoader: true, variables: queryVariables @@ -339,7 +356,8 @@ export const ProductList: React.FC = ({ params }) => { { initial: mapEdgesToItems(initialFilterProductTypes?.productTypes), search: searchProductTypes - } + }, + channelOpts ); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( @@ -360,7 +378,7 @@ export const ProductList: React.FC = ({ params }) => { availableInGridAttributes={mapEdgesToItems( attributes?.data?.availableInGrid )} - currencySymbol={channel?.currencyCode || ""} + currencySymbol={selectedChannel?.currencyCode || ""} currentTab={currentTab} defaultSettings={defaultListSettings[ListViews.PRODUCT_LIST]} filterOpts={filterOpts} @@ -437,7 +455,7 @@ export const ProductList: React.FC = ({ params }) => { tabs={getFilterTabs().map(tab => tab.name)} onExport={() => openModal("export")} channelsCount={availableChannels?.length} - selectedChannelId={channel?.id} + selectedChannelId={selectedChannel?.id} /> { status: true.toString(), stockStatus: StockAvailability.IN_STOCK }; - const filterVariables = getFilterVariables(params, "default-channel"); + const filterVariables = getFilterVariables(params, true); - expect(getExistingKeys(filterVariables)).toHaveLength(3); + expect(getExistingKeys(filterVariables)).toHaveLength(2); }); }); diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 600811f7c..091adb089 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -1,3 +1,4 @@ +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { UseSearchResult } from "@saleor/hooks/makeSearch"; import { findValueInEnum, maybe } from "@saleor/misc"; import { @@ -43,7 +44,8 @@ import { getGteLteVariables, getMinMaxQueryParam, getMultipleValueQueryParam, - getSingleEnumValueQueryParam + getSingleEnumValueQueryParam, + getSingleValueQueryParam } from "../../../utils/filters"; import { ProductListUrlFilters, @@ -73,7 +75,8 @@ export function getFilterOpts( productTypes: { initial: InitialProductFilterProductTypes_productTypes_edges_node[]; search: UseSearchResult; - } + }, + channels: SingleAutocompleteChoiceType[] ): ProductListFilterOpts { return { attributes: attributes @@ -131,6 +134,11 @@ export function getFilterOpts( onSearchChange: categories.search.search, value: maybe(() => dedupeFilter(params.categories), []) }, + channel: { + active: params?.channel !== undefined, + choices: channels, + value: params?.channel + }, collections: { active: !!params.collections, choices: mapNodeToChoice( @@ -224,14 +232,13 @@ function getFilteredAttributeValue( export function getFilterVariables( params: ProductListUrlFilters, - channel: string | undefined + isChannelSelected: boolean ): ProductFilterInput { return { attributes: getFilteredAttributeValue(params), categories: params.categories !== undefined ? params.categories : null, - channel: channel || null, collections: params.collections !== undefined ? params.collections : null, - price: channel + price: isChannelSelected ? getGteLteVariables({ gte: parseFloat(params.priceFrom), lte: parseFloat(params.priceTo) @@ -298,6 +305,12 @@ export function getFilterQueryParam( ProductListUrlFiltersEnum.stockStatus, StockAvailability ); + + case ProductFilterKeys.channel: + return getSingleValueQueryParam( + filter, + ProductListUrlFiltersEnum.channel + ); } } diff --git a/src/products/views/ProductList/fixtures.ts b/src/products/views/ProductList/fixtures.ts index dac49f17b..4ed9fc7d9 100644 --- a/src/products/views/ProductList/fixtures.ts +++ b/src/products/views/ProductList/fixtures.ts @@ -43,6 +43,16 @@ export const productListFilterOpts: ProductListFilterOpts = { ], value: [categories[5].id] }, + channel: { + active: false, + value: "default-channel", + choices: [ + { + value: "default-channel", + label: "Default channel" + } + ] + }, collections: { ...fetchMoreProps, ...searchPageProps, diff --git a/src/products/views/ProductList/sort.ts b/src/products/views/ProductList/sort.ts index 9d1726318..b5dfafcec 100644 --- a/src/products/views/ProductList/sort.ts +++ b/src/products/views/ProductList/sort.ts @@ -5,6 +5,26 @@ import { import { ProductOrder, ProductOrderField } from "@saleor/types/globalTypes"; import { getOrderDirection } from "@saleor/utils/sort"; +export const DEFAULT_SORT_KEY = ProductListUrlSortField.name; + +export function canBeSorted( + sort: ProductListUrlSortField, + isChannelSelected: boolean +) { + switch (sort) { + case ProductListUrlSortField.name: + case ProductListUrlSortField.productType: + case ProductListUrlSortField.attribute: + case ProductListUrlSortField.rank: + return true; + case ProductListUrlSortField.price: + case ProductListUrlSortField.status: + return isChannelSelected; + default: + return false; + } +} + export function getSortQueryField( sort: ProductListUrlSortField ): ProductOrderField { @@ -26,8 +46,12 @@ export function getSortQueryField( export function getSortQueryVariables( params: ProductListUrlQueryParams, - channel: string + isChannelSelected: boolean ): ProductOrder { + if (!canBeSorted(params.sort, isChannelSelected)) { + return; + } + const direction = getOrderDirection(params.asc); if (params.sort === ProductListUrlSortField.attribute) { return { @@ -35,9 +59,9 @@ export function getSortQueryVariables( direction }; } + const field = getSortQueryField(params.sort); return { - channel, direction, field }; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index c45aac27f..056110a80 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -4798,7 +4798,7 @@ exports[`Storyshots Generics / ChannelsAvailabilityDropdown default 1`] = ` class="StatusLabel-container-id" >
- Available in 3/6 + 3/6 channels
@@ -48765,7 +48765,7 @@ exports[`Storyshots Views / Categories / Update category default 1`] = ` class="MuiTableRow-root-id MuiTableRow-head-id" >
- Available in 2/2 + 2/2 channels
@@ -53484,7 +53484,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -53581,7 +53581,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -53678,7 +53678,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -53775,7 +53775,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -53872,7 +53872,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -53969,7 +53969,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -54066,7 +54066,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -54163,7 +54163,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -54260,7 +54260,7 @@ exports[`Storyshots Views / Categories / Update category products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/2 + 1/2 channels
@@ -59606,7 +59606,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details d class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -59722,7 +59722,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details d class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -59838,7 +59838,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details d class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -59954,7 +59954,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details d class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -61103,7 +61103,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details f class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -61219,7 +61219,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details f class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -61335,7 +61335,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details f class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -61451,7 +61451,7 @@ exports[`Storyshots Views / Collections / Collection detailsCollection details f class="StatusLabel-container-id" >
- Available in 2/2 + 2/2 channels
@@ -63953,7 +63953,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
- Available in 1/2 + 1/2 channels
@@ -91776,7 +91776,7 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/7 + 1/7 channels
@@ -91892,7 +91892,7 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/7 + 1/7 channels
@@ -92008,7 +92008,7 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/7 + 1/7 channels
@@ -92124,7 +92124,7 @@ exports[`Storyshots Views / Discounts / Sale details products 1`] = ` class="StatusLabel-container-id" >
- Available in 1/7 + 1/7 channels
@@ -93893,7 +93893,7 @@ exports[`Storyshots Views / Discounts / Sale list no channels 1`] = `
-
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -207477,27 +207469,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -207593,27 +207577,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -207709,27 +207685,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -207825,27 +207793,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -207941,27 +207901,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208057,27 +208009,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208173,27 +208117,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208289,27 +208225,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208405,27 +208333,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208521,27 +208441,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208637,27 +208549,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208753,27 +208657,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208869,27 +208765,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -208985,27 +208873,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -209101,27 +208981,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -209217,27 +209089,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -209333,27 +209197,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -209449,27 +209305,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -209565,27 +209413,19 @@ exports[`Storyshots Views / Products / Product list default 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210174,27 +210014,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210290,27 +210122,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210406,27 +210230,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210522,27 +210338,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210638,27 +210446,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210754,27 +210554,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210870,27 +210662,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -210986,27 +210770,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211102,27 +210878,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211218,27 +210986,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211334,27 +211094,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211450,27 +211202,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211566,27 +211310,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211682,27 +211418,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211798,27 +211526,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -211914,27 +211634,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -212030,27 +211742,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -212146,27 +211850,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -212262,27 +211958,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -212378,27 +212066,19 @@ exports[`Storyshots Views / Products / Product list limits reached 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -213287,7 +212967,7 @@ exports[`Storyshots Views / Products / Product list no channels 1`] = `
@@ -213302,7 +212982,7 @@ exports[`Storyshots Views / Products / Product list no channels 1`] = `
@@ -216234,27 +215914,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216350,27 +216022,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216466,27 +216130,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216582,27 +216238,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216698,27 +216346,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216814,27 +216454,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -216930,27 +216562,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217046,27 +216670,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217162,27 +216778,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217278,27 +216886,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217394,27 +216994,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217510,27 +217102,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217626,27 +217210,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217742,27 +217318,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217858,27 +217426,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -217974,27 +217534,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -218090,27 +217642,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -218206,27 +217750,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -218322,27 +217858,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -218438,27 +217966,19 @@ exports[`Storyshots Views / Products / Product list no limits 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219001,27 +218521,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219117,27 +218629,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219233,27 +218737,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219349,27 +218845,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219465,27 +218953,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219581,27 +219061,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219697,27 +219169,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219813,27 +219277,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -219929,27 +219385,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220045,27 +219493,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220161,27 +219601,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220277,27 +219709,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220393,27 +219817,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220509,27 +219925,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220625,27 +220033,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220741,27 +220141,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220857,27 +220249,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -220973,27 +220357,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -221089,27 +220465,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
@@ -221205,27 +220573,19 @@ exports[`Storyshots Views / Products / Product list with data 1`] = ` data-test="availability" data-test-availability="true" > -
+
+
-
-
-
- Available in 2/6 -
-
+ Published on 07/14/2020
diff --git a/src/storybook/stories/discounts/SaleListPage.tsx b/src/storybook/stories/discounts/SaleListPage.tsx index 27be6c9f9..3af66cb18 100644 --- a/src/storybook/stories/discounts/SaleListPage.tsx +++ b/src/storybook/stories/discounts/SaleListPage.tsx @@ -26,6 +26,16 @@ const props: SaleListPageProps = { ...sortPageProps, ...tabPageProps, filterOpts: { + channel: { + active: false, + value: "default-channel", + choices: [ + { + value: "default-channel", + label: "Default channel" + } + ] + }, saleType: { active: false, value: DiscountValueTypeEnum.FIXED diff --git a/src/storybook/stories/discounts/VoucherListPage.tsx b/src/storybook/stories/discounts/VoucherListPage.tsx index 49079996e..2da828f6d 100644 --- a/src/storybook/stories/discounts/VoucherListPage.tsx +++ b/src/storybook/stories/discounts/VoucherListPage.tsx @@ -28,6 +28,16 @@ const props: VoucherListPageProps = { ...tabPageProps, ...filterPageProps, filterOpts: { + channel: { + active: false, + value: "default-channel", + choices: [ + { + value: "default-channel", + label: "Default channel" + } + ] + }, saleType: { active: false, value: [VoucherDiscountType.FIXED, VoucherDiscountType.PERCENTAGE] diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 22318cdfd..93d34bf0d 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -1118,13 +1118,12 @@ export interface AttributeFilterInput { filterableInStorefront?: boolean | null; filterableInDashboard?: boolean | null; availableInGrid?: boolean | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; search?: string | null; ids?: (string | null)[] | null; type?: AttributeTypeEnum | null; inCollection?: string | null; inCategory?: string | null; - channel?: string | null; } export interface AttributeInput { @@ -1188,7 +1187,7 @@ export interface CatalogueInput { export interface CategoryFilterInput { search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; ids?: (string | null)[] | null; } @@ -1203,7 +1202,6 @@ export interface CategoryInput { export interface CategorySortingInput { direction: OrderDirection; - channel?: string | null; field: CategorySortField; } @@ -1247,9 +1245,8 @@ export interface CollectionCreateInput { export interface CollectionFilterInput { published?: CollectionPublished | null; search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; ids?: (string | null)[] | null; - channel?: string | null; } export interface CollectionInput { @@ -1265,7 +1262,6 @@ export interface CollectionInput { export interface CollectionSortingInput { direction: OrderDirection; - channel?: string | null; field: CollectionSortField; } @@ -1279,7 +1275,7 @@ export interface CustomerFilterInput { numberOfOrders?: IntRangeInput | null; placedOrders?: DateRangeInput | null; search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; } export interface CustomerInput { @@ -1394,6 +1390,11 @@ export interface MenuSortingInput { field: MenuSortField; } +export interface MetadataFilter { + key: string; + value?: string | null; +} + export interface MetadataInput { key: string; value: string; @@ -1417,7 +1418,7 @@ export interface OrderDraftFilterInput { customer?: string | null; created?: DateRangeInput | null; search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; channels?: (string | null)[] | null; } @@ -1427,7 +1428,7 @@ export interface OrderFilterInput { customer?: string | null; created?: DateRangeInput | null; search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; channels?: (string | null)[] | null; } @@ -1524,7 +1525,7 @@ export interface PageCreateInput { export interface PageFilterInput { search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; pageTypes?: (string | null)[] | null; ids?: (string | null)[] | null; } @@ -1667,12 +1668,11 @@ export interface ProductFilterInput { stockAvailability?: StockAvailability | null; stocks?: ProductStockFilterInput | null; search?: string | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; price?: PriceRangeInput | null; minimalPrice?: PriceRangeInput | null; productTypes?: (string | null)[] | null; ids?: (string | null)[] | null; - channel?: string | null; } export interface ProductInput { @@ -1691,7 +1691,6 @@ export interface ProductInput { export interface ProductOrder { direction: OrderDirection; - channel?: string | null; attributeId?: string | null; field?: ProductOrderField | null; } @@ -1705,7 +1704,7 @@ export interface ProductTypeFilterInput { search?: string | null; configurable?: ProductTypeConfigurable | null; productType?: ProductTypeEnum | null; - metadata?: (MetadataInput | null)[] | null; + metadata?: (MetadataFilter | null)[] | null; ids?: (string | null)[] | null; } @@ -1798,7 +1797,6 @@ export interface SaleInput { export interface SaleSortingInput { direction: OrderDirection; - channel?: string | null; field: SaleSortField; } @@ -1987,7 +1985,6 @@ export interface VoucherInput { export interface VoucherSortingInput { direction: OrderDirection; - channel?: string | null; field: VoucherSortField; } diff --git a/src/utils/maps.ts b/src/utils/maps.ts index 8b93e2ee6..8b1183de2 100644 --- a/src/utils/maps.ts +++ b/src/utils/maps.ts @@ -1,6 +1,8 @@ -import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; -import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { + ChoiceValue, + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; import { MetadataItem } from "@saleor/fragments/types/MetadataItem"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { Node, SlugNode } from "@saleor/types"; @@ -18,48 +20,46 @@ export function mapEdgesToItems(data?: EdgesType): T[] { return data.edges.map(({ node }) => node); } -export function mapCountriesToChoices( - countries: ShopInfo_shop_countries[] -): Array { +export function mapCountriesToChoices(countries: ShopInfo_shop_countries[]) { return countries.map(country => ({ label: country.country, value: country.code })); } -export function mapPagesToChoices( - pages: SearchPages_search_edges_node[] -): Array { +export function mapPagesToChoices(pages: SearchPages_search_edges_node[]) { return pages.map(page => ({ label: page.title, value: page.id })); } -export function mapNodeToChoice( - nodes: Array> -): Array { +type ExtendedNode = Node & Record<"name", string>; +export function mapNodeToChoice( + nodes: T[] +): Array>; +export function mapNodeToChoice( + nodes: T[], + getterFn: (node: T) => K +): Array>; +export function mapNodeToChoice( + nodes: T[], + getterFn?: (node: T) => any +) { if (!nodes) { return []; } return nodes.map(node => ({ label: node.name, - value: node.id + value: getterFn ? getterFn(node) : node.id })); } export function mapSlugNodeToChoice( - nodes: Array> -): Array { - if (!nodes) { - return []; - } - - return nodes.map(node => ({ - label: node.name, - value: node.slug - })); + nodes: Array +): SingleAutocompleteChoiceType[] { + return mapNodeToChoice(nodes, nodes => nodes.slug); } export function mapMetadataItemToInput(item: MetadataItem): MetadataInput { diff --git a/src/utils/sort.ts b/src/utils/sort.ts index 74e8e7eb4..c35a3d75b 100644 --- a/src/utils/sort.ts +++ b/src/utils/sort.ts @@ -72,7 +72,7 @@ type GetSortQueryField = ( type GetSortQueryVariables< TSortField extends string, TParams extends Record -> = (params: TParams, channelSlug?: string) => SortingInput; +> = (params: TParams) => SortingInput; export function createGetSortQueryVariables< TUrlField extends string, TSortField extends string, @@ -80,14 +80,13 @@ export function createGetSortQueryVariables< >( getSortQueryField: GetSortQueryField ): GetSortQueryVariables { - return (params: TParams, channelSlug?: string) => { + return (params: TParams) => { const field = getSortQueryField(params.sort); if (!!field) { return { direction: getOrderDirection(params.asc), - field, - channel: channelSlug + field }; }