From dc8a72b44e12eb9032ff1392b8a94d391a3a20d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chy=C5=82a?= Date: Thu, 13 Feb 2025 16:56:16 +0100 Subject: [PATCH 1/4] Migrate from tabs to filter presets --- locale/defaultMessages.json | 14 +-- .../ProductTypeListPage.tsx | 112 ++++++++++++------ .../views/ProductTypeList/ProductTypeList.tsx | 73 ++++++------ .../views/ProductTypeList/filters.ts | 3 +- 4 files changed, 114 insertions(+), 88 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index a32f6309c4e..4456dcc0adf 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -582,10 +582,6 @@ "context": "expiry date section header", "string": "Expiry date" }, - "1KSqnn": { - "context": "tab name", - "string": "All Product Types" - }, "1LBYpE": { "context": "dialog header", "string": "Delete Menus" @@ -1675,6 +1671,9 @@ "context": "navigation section name", "string": "Navigation" }, + "9CC8JI": { + "string": "Search product types..." + }, "9CEu8k": { "context": "modal button images upload", "string": "Upload Images" @@ -7270,6 +7269,10 @@ "ivJ1qt": { "string": "Manage your permission groups and their permissions" }, + "ivmwpV": { + "context": "tab name", + "string": "All product types" + }, "ixjvkM": { "string": "We’ve created your default token. Make sure to copy your new personal access token now. You won’t be able to see it again." }, @@ -8572,9 +8575,6 @@ "context": "expires in label", "string": "Expires in" }, - "rpFdD1": { - "string": "Search Product Type" - }, "rqiCWU": { "string": "Saved changes" }, diff --git a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx index 30327eb944b..407964c07c2 100644 --- a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx +++ b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx @@ -1,32 +1,30 @@ // @ts-strict-ignore +import { ListFilters } from "@dashboard/components/AppLayout/ListFilters"; import { TopNav } from "@dashboard/components/AppLayout/TopNav"; -import { Button } from "@dashboard/components/Button"; import { DashboardCard } from "@dashboard/components/Card"; -import FilterBar from "@dashboard/components/FilterBar"; -import { configurationMenuUrl } from "@dashboard/configuration"; +import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect"; +import { ListPageLayout } from "@dashboard/components/Layouts"; import { ProductTypeFragment } from "@dashboard/graphql"; +import useNavigator from "@dashboard/hooks/useNavigator"; import { sectionNames } from "@dashboard/intl"; import ProductTypeList from "@dashboard/productTypes/components/ProductTypeList/ProductTypeList"; import { productTypeAddUrl, ProductTypeListUrlSortField } from "@dashboard/productTypes/urls"; -import React from "react"; +import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui-next"; +import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { - FilterPageProps, - ListActions, - PageListProps, - SortPage, - TabPageProps, -} from "../../../types"; +import { FilterPageProps, ListActions, PageListProps, SortPage } from "../../../types"; import { createFilterStructure, ProductTypeFilterKeys, ProductTypeListFilterOpts } from "./filters"; export interface ProductTypeListPageProps extends PageListProps, ListActions, - FilterPageProps, - SortPage, - TabPageProps { + Omit, "onTabDelete">, + SortPage { productTypes: ProductTypeFragment[]; + onTabUpdate: (tabName: string) => void; + onTabDelete: (id: number) => void; + hasPresetsChanged: () => boolean; } const ProductTypeListPage: React.FC = ({ @@ -39,44 +37,80 @@ const ProductTypeListPage: React.FC = ({ onTabChange, onTabDelete, onTabSave, + onTabUpdate, tabs, + hasPresetsChanged, + disabled, ...listProps }) => { const intl = useIntl(); - const structure = createFilterStructure(intl, filterOpts); + const navigate = useNavigator(); + const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); + const filterStructure = createFilterStructure(intl, filterOpts); return ( - <> - - + + + + + + + + + + + + + + - - + - + + - + ); }; diff --git a/src/productTypes/views/ProductTypeList/ProductTypeList.tsx b/src/productTypes/views/ProductTypeList/ProductTypeList.tsx index c9f41c7cc9e..6d2418ed7d6 100644 --- a/src/productTypes/views/ProductTypeList/ProductTypeList.tsx +++ b/src/productTypes/views/ProductTypeList/ProductTypeList.tsx @@ -1,10 +1,9 @@ // @ts-strict-ignore import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog"; -import SaveFilterTabDialog, { - SaveFilterTabDialogFormData, -} from "@dashboard/components/SaveFilterTabDialog"; +import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog"; import { useProductTypeBulkDeleteMutation, useProductTypeListQuery } from "@dashboard/graphql"; import useBulkActions from "@dashboard/hooks/useBulkActions"; +import { useFilterPresets } from "@dashboard/hooks/useFilterPresets"; import useListSettings from "@dashboard/hooks/useListSettings"; import useNavigator from "@dashboard/hooks/useNavigator"; import useNotifier from "@dashboard/hooks/useNotifier"; @@ -33,16 +32,7 @@ import { ProductTypeListUrlDialog, ProductTypeListUrlQueryParams, } from "../../urls"; -import { - deleteFilterTab, - getActiveFilters, - getFilterOpts, - getFilterQueryParam, - getFiltersCurrentTab, - getFilterTabs, - getFilterVariables, - saveFilterTab, -} from "./filters"; +import { getFilterOpts, getFilterQueryParam, getFilterVariables, storageUtils } from "./filters"; import { getSortQueryVariables } from "./sort"; interface ProductTypeListProps { @@ -77,8 +67,6 @@ export const ProductTypeList: React.FC = ({ params }) => { displayLoader: true, variables: queryVariables, }); - const tabs = getFilterTabs(); - const currentTab = getFiltersCurrentTab(params, tabs); const [changeFilters, resetFilters, handleSearchChange] = createFilterHandlers({ cleanupFn: reset, createUrl: productTypeListUrl, @@ -90,24 +78,24 @@ export const ProductTypeList: React.FC = ({ params }) => { ProductTypeListUrlDialog, ProductTypeListUrlQueryParams >(navigate, productTypeListUrl, params); - const handleTabChange = (tab: number) => { - reset(); - navigate( - productTypeListUrl({ - activeTab: tab.toString(), - ...getFilterTabs()[tab - 1].data, - }), - ); - }; - const handleTabDelete = () => { - deleteFilterTab(currentTab); - reset(); - navigate(productTypeListUrl()); - }; - const handleTabSave = (data: SaveFilterTabDialogFormData) => { - saveFilterTab(data.name, getActiveFilters(params)); - handleTabChange(tabs.length + 1); - }; + + const { + selectedPreset, + presets, + hasPresetsChanged, + onPresetChange, + onPresetDelete, + onPresetSave, + onPresetUpdate, + setPresetIdToDelete, + getPresetNameToDelete, + } = useFilterPresets({ + params, + reset, + getUrl: productTypeListUrl, + storageUtils, + }); + const paginationValues = usePaginator({ pageInfo: maybe(() => data.productTypes.pageInfo), paginationState, @@ -150,16 +138,20 @@ export const ProductTypeList: React.FC = ({ params }) => { return ( openModal("delete-search")} + onTabChange={onPresetChange} + onTabDelete={(id: number) => { + setPresetIdToDelete(id); + openModal("delete-search"); + }} onTabSave={() => openModal("save-search")} - tabs={tabs.map(tab => tab.name)} + onTabUpdate={onPresetUpdate} + tabs={presets.map(tab => tab.name)} disabled={loading} productTypes={productTypesData} onSort={handleSort} @@ -182,6 +174,7 @@ export const ProductTypeList: React.FC = ({ params }) => { } + hasPresetsChanged={hasPresetsChanged} /> {productTypesData && ( = ({ params }) => { open={params.action === "save-search"} confirmButtonState="default" onClose={closeModal} - onSubmit={handleTabSave} + onSubmit={onPresetSave} /> tabs[currentTab - 1].name, "...")} + onSubmit={onPresetDelete} + tabName={getPresetNameToDelete()} /> ); diff --git a/src/productTypes/views/ProductTypeList/filters.ts b/src/productTypes/views/ProductTypeList/filters.ts index 8cba86f2edd..e494d2372fe 100644 --- a/src/productTypes/views/ProductTypeList/filters.ts +++ b/src/productTypes/views/ProductTypeList/filters.ts @@ -61,8 +61,7 @@ export function getFilterQueryParam( } } -export const { deleteFilterTab, getFilterTabs, saveFilterTab } = - createFilterTabUtils(PRODUCT_TYPE_FILTERS_KEY); +export const storageUtils = createFilterTabUtils(PRODUCT_TYPE_FILTERS_KEY); export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } = createFilterUtils< ProductTypeListUrlQueryParams, From 318d71d15145c41e5c2bbe05c71703b85d3d87c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chy=C5=82a?= Date: Thu, 13 Feb 2025 17:05:56 +0100 Subject: [PATCH 2/4] Add changeset --- .changeset/flat-squids-bow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flat-squids-bow.md diff --git a/.changeset/flat-squids-bow.md b/.changeset/flat-squids-bow.md new file mode 100644 index 00000000000..a561e0cb714 --- /dev/null +++ b/.changeset/flat-squids-bow.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +You can now save,update and delete filter presets on product types list From 8a25ea24a34ceca1cd18be6230af36e86c4f38cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chy=C5=82a?= Date: Fri, 14 Feb 2025 11:03:24 +0100 Subject: [PATCH 3/4] Update test id --- .../components/ProductTypeListPage/ProductTypeListPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx index 407964c07c2..634e929af87 100644 --- a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx +++ b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx @@ -84,7 +84,7 @@ const ProductTypeListPage: React.FC = ({ disabled={disabled} variant="primary" onClick={() => navigate(productTypeAddUrl())} - data-test-id="create-product-type" + data-test-id="add-product-type" > Date: Fri, 14 Feb 2025 15:36:56 +0100 Subject: [PATCH 4/4] CR fixes --- .changeset/flat-squids-bow.md | 2 +- locale/defaultMessages.json | 7 ++++--- .../ProductTypeListPage/ProductTypeListPage.tsx | 4 ++-- .../views/ProductTypeList/ProductTypeList.tsx | 9 ++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.changeset/flat-squids-bow.md b/.changeset/flat-squids-bow.md index a561e0cb714..bed8eab6b73 100644 --- a/.changeset/flat-squids-bow.md +++ b/.changeset/flat-squids-bow.md @@ -2,4 +2,4 @@ "saleor-dashboard": patch --- -You can now save,update and delete filter presets on product types list +You can now save, update, and delete filter presets on a product types list \ No newline at end of file diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 4456dcc0adf..86bf8507a34 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1671,9 +1671,6 @@ "context": "navigation section name", "string": "Navigation" }, - "9CC8JI": { - "string": "Search product types..." - }, "9CEu8k": { "context": "modal button images upload", "string": "Upload Images" @@ -4014,6 +4011,10 @@ "context": "header field name, header", "string": "Name" }, + "Nqh0na": { + "context": "Product types search input placeholder", + "string": "Search product types..." + }, "NqxvFh": { "context": "navigator placeholder", "string": "Type Command" diff --git a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx index 634e929af87..7501f5742f5 100644 --- a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx +++ b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import { ListFilters } from "@dashboard/components/AppLayout/ListFilters"; import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { DashboardCard } from "@dashboard/components/Card"; @@ -101,8 +100,9 @@ const ProductTypeListPage: React.FC = ({ initialSearch={initialSearch} onSearchChange={onSearchChange} searchPlaceholder={intl.formatMessage({ - id: "9CC8JI", + id: "Nqh0na", defaultMessage: "Search product types...", + description: "Product types search input placeholder", })} onFilterChange={onFilterChange} filterStructure={filterStructure} diff --git a/src/productTypes/views/ProductTypeList/ProductTypeList.tsx b/src/productTypes/views/ProductTypeList/ProductTypeList.tsx index 6d2418ed7d6..396414030df 100644 --- a/src/productTypes/views/ProductTypeList/ProductTypeList.tsx +++ b/src/productTypes/views/ProductTypeList/ProductTypeList.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog"; import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog"; import { useProductTypeBulkDeleteMutation, useProductTypeListQuery } from "@dashboard/graphql"; @@ -97,12 +96,12 @@ export const ProductTypeList: React.FC = ({ params }) => { }); const paginationValues = usePaginator({ - pageInfo: maybe(() => data.productTypes.pageInfo), + pageInfo: maybe(() => data?.productTypes?.pageInfo), paginationState, queryString: params, }); const handleSort = createSortHandler(navigate, productTypeListUrl, params); - const productTypesData = mapEdgesToItems(data?.productTypes); + const productTypesData = mapEdgesToItems(data?.productTypes) ?? []; const productTypeDeleteData = useProductTypeDelete({ selectedTypes: selectedProductTypes, @@ -111,7 +110,7 @@ export const ProductTypeList: React.FC = ({ params }) => { }); const [productTypeBulkDelete, productTypeBulkDeleteOpts] = useProductTypeBulkDeleteMutation({ onCompleted: data => { - if (data.productTypeBulkDelete.errors.length === 0) { + if (data?.productTypeBulkDelete?.errors?.length === 0) { notify({ status: "success", text: intl.formatMessage(commonMessages.savedChanges), @@ -131,7 +130,7 @@ export const ProductTypeList: React.FC = ({ params }) => { const onProductTypeBulkDelete = () => productTypeBulkDelete({ variables: { - ids: params.ids, + ids: params.ids ?? [], }, });