Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flat-squids-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

You can now save, update, and delete filter presets on a product types list
15 changes: 8 additions & 7 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -4015,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"
Expand Down Expand Up @@ -7270,6 +7270,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."
},
Expand Down Expand Up @@ -8572,9 +8576,6 @@
"context": "expires in label",
"string": "Expires in"
},
"rpFdD1": {
"string": "Search Product Type"
},
"rqiCWU": {
"string": "Saved changes"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
// @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<ProductTypeFilterKeys, ProductTypeListFilterOpts>,
SortPage<ProductTypeListUrlSortField>,
TabPageProps {
Omit<FilterPageProps<ProductTypeFilterKeys, ProductTypeListFilterOpts>, "onTabDelete">,
SortPage<ProductTypeListUrlSortField> {
productTypes: ProductTypeFragment[];
onTabUpdate: (tabName: string) => void;
onTabDelete: (id: number) => void;
hasPresetsChanged: () => boolean;
}

const ProductTypeListPage: React.FC<ProductTypeListPageProps> = ({
Expand All @@ -39,44 +36,81 @@
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);

Check warning on line 48 in src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx

View check run for this annotation

Codecov / codecov/patch

src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx#L46-L48

Added lines #L46 - L48 were not covered by tests

return (
<>
<TopNav href={configurationMenuUrl} title={intl.formatMessage(sectionNames.productTypes)}>
<Button variant="primary" href={productTypeAddUrl()} data-test-id="add-product-type">
<FormattedMessage id="gksZwp" defaultMessage="Create product type" description="button" />
</Button>
<ListPageLayout>
<TopNav
withoutBorder
isAlignToRight={false}
title={intl.formatMessage(sectionNames.productTypes)}
>
<Box __flex={1} display="flex" justifyContent="space-between" alignItems="center">
<Box display="flex">
<Box marginX={3} display="flex" alignItems="center">
<ChevronRightIcon />
</Box>

<FilterPresetsSelect
presetsChanged={hasPresetsChanged()}
onSelect={onTabChange}
onRemove={onTabDelete}
onUpdate={onTabUpdate}
savedPresets={tabs}
activePreset={currentTab}
onSelectAll={onAll}
onSave={onTabSave}
isOpen={isFilterPresetOpen}
onOpenChange={setFilterPresetOpen}
selectAllLabel={intl.formatMessage({
id: "ivmwpV",
defaultMessage: "All product types",
description: "tab name",
})}
/>
</Box>
<Box>
<Button
disabled={disabled}
variant="primary"
onClick={() => navigate(productTypeAddUrl())}

Check warning on line 85 in src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx

View check run for this annotation

Codecov / codecov/patch

src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx#L85

Added line #L85 was not covered by tests
data-test-id="add-product-type"
>
<FormattedMessage
id="gksZwp"
defaultMessage="Create product type"
description="button"
/>
</Button>
</Box>
</Box>
</TopNav>
<DashboardCard>
<FilterBar
allTabLabel={intl.formatMessage({
id: "1KSqnn",
defaultMessage: "All Product Types",
description: "tab name",
})}
currentTab={currentTab}
filterStructure={structure}

<DashboardCard gap={0}>
<ListFilters
initialSearch={initialSearch}
onSearchChange={onSearchChange}
searchPlaceholder={intl.formatMessage({
id: "rpFdD1",
defaultMessage: "Search Product Type",
id: "Nqh0na",
defaultMessage: "Search product types...",
description: "Product types search input placeholder",
})}
tabs={tabs}
onAll={onAll}
onFilterChange={onFilterChange}
onSearchChange={onSearchChange}
onTabChange={onTabChange}
onTabDelete={onTabDelete}
onTabSave={onTabSave}
filterStructure={filterStructure}
/>
<ProductTypeList {...listProps} />

<ProductTypeList {...listProps} disabled={disabled} />
</DashboardCard>
</>
</ListPageLayout>
);
};

Expand Down
82 changes: 37 additions & 45 deletions src/productTypes/views/ProductTypeList/ProductTypeList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// @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";
Expand Down Expand Up @@ -33,16 +31,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 {
Expand Down Expand Up @@ -77,8 +66,6 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ params }) => {
displayLoader: true,
variables: queryVariables,
});
const tabs = getFilterTabs();
const currentTab = getFiltersCurrentTab(params, tabs);
const [changeFilters, resetFilters, handleSearchChange] = createFilterHandlers({
cleanupFn: reset,
createUrl: productTypeListUrl,
Expand All @@ -90,31 +77,31 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ 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),
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,
Expand All @@ -123,7 +110,7 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ 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),
Expand All @@ -143,23 +130,27 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ params }) => {
const onProductTypeBulkDelete = () =>
productTypeBulkDelete({
variables: {
ids: params.ids,
ids: params.ids ?? [],
},
});

return (
<PaginatorContext.Provider value={paginationValues}>
<ProductTypeListPage
currentTab={currentTab}
currentTab={selectedPreset}
filterOpts={getFilterOpts(params)}
initialSearch={params.query || ""}
onSearchChange={handleSearchChange}
onFilterChange={changeFilters}
onAll={resetFilters}
onTabChange={handleTabChange}
onTabDelete={() => 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}
Expand All @@ -182,6 +173,7 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
hasPresetsChanged={hasPresetsChanged}
/>
{productTypesData && (
<TypeDeleteWarningDialog
Expand All @@ -197,14 +189,14 @@ export const ProductTypeList: React.FC<ProductTypeListProps> = ({ params }) => {
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabSave}
onSubmit={onPresetSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
onSubmit={onPresetDelete}
tabName={getPresetNameToDelete()}
/>
</PaginatorContext.Provider>
);
Expand Down
3 changes: 1 addition & 2 deletions src/productTypes/views/ProductTypeList/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ export function getFilterQueryParam(
}
}

export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
createFilterTabUtils<ProductTypeListUrlFilters>(PRODUCT_TYPE_FILTERS_KEY);
export const storageUtils = createFilterTabUtils<string>(PRODUCT_TYPE_FILTERS_KEY);

export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } = createFilterUtils<
ProductTypeListUrlQueryParams,
Expand Down
Loading