Introduce new column picker in Categories list (#3876)
This commit is contained in:
parent
5b4d07d547
commit
b86bb025a2
7 changed files with 143 additions and 116 deletions
5
.changeset/fast-rocks-punch.md
Normal file
5
.changeset/fast-rocks-punch.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-dashboard": minor
|
||||
---
|
||||
|
||||
Introduce new column picker in Categories list
|
|
@ -1,11 +1,10 @@
|
|||
// @ts-strict-ignore
|
||||
import {
|
||||
CategoryListUrlSortField,
|
||||
categoryUrl,
|
||||
} from "@dashboard/categories/urls";
|
||||
import ColumnPicker from "@dashboard/components/ColumnPicker";
|
||||
import { ColumnPicker } from "@dashboard/components/Datagrid/ColumnPicker/ColumnPicker";
|
||||
import { useColumns } from "@dashboard/components/Datagrid/ColumnPicker/useColumns";
|
||||
import Datagrid from "@dashboard/components/Datagrid/Datagrid";
|
||||
import { useColumnsDefault } from "@dashboard/components/Datagrid/hooks/useColumnsDefault";
|
||||
import {
|
||||
DatagridChangeStateContext,
|
||||
useDatagridChangeState,
|
||||
|
@ -18,14 +17,16 @@ import { Box } from "@saleor/macaw-ui/next";
|
|||
import React, { ReactNode, useCallback, useMemo } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { createGetCellContent, getColumns } from "./datagrid";
|
||||
import {
|
||||
categoryListStaticColumnsAdapter,
|
||||
createGetCellContent,
|
||||
} from "./datagrid";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface CategoryListDatagridProps
|
||||
extends Partial<SortPage<CategoryListUrlSortField>>,
|
||||
PageListProps {
|
||||
categories?: CategoryFragment[];
|
||||
disabled: boolean;
|
||||
extends PageListProps,
|
||||
Partial<SortPage<CategoryListUrlSortField>> {
|
||||
categories: CategoryFragment[];
|
||||
onSelectCategoriesIds: (ids: number[], clearSelection: () => void) => void;
|
||||
selectionActionButton?: ReactNode | null;
|
||||
hasRowHover?: boolean;
|
||||
|
@ -44,32 +45,40 @@ export const CategoryListDatagrid = ({
|
|||
}: CategoryListDatagridProps) => {
|
||||
const datagridState = useDatagridChangeState();
|
||||
const intl = useIntl();
|
||||
const availableColumns = useMemo(() => getColumns(intl, sort), [intl, sort]);
|
||||
|
||||
const {
|
||||
availableColumnsChoices,
|
||||
columnChoices,
|
||||
columns,
|
||||
defaultColumns,
|
||||
onColumnMoved,
|
||||
onColumnResize,
|
||||
onColumnsChange,
|
||||
picker,
|
||||
} = useColumnsDefault(availableColumns);
|
||||
const memoizedStaticColumns = useMemo(
|
||||
() => categoryListStaticColumnsAdapter(intl, sort),
|
||||
[intl, sort],
|
||||
);
|
||||
|
||||
const handleColumnChange = useCallback(
|
||||
picked => {
|
||||
if (onUpdateListSettings) {
|
||||
onUpdateListSettings("columns", picked.filter(Boolean));
|
||||
}
|
||||
},
|
||||
[onUpdateListSettings],
|
||||
);
|
||||
|
||||
const { handlers, selectedColumns, staticColumns, visibleColumns } =
|
||||
useColumns({
|
||||
staticColumns: memoizedStaticColumns,
|
||||
selectedColumns: settings?.columns ?? [],
|
||||
onSave: handleColumnChange,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const getCellContent = useCallback(
|
||||
createGetCellContent(categories, columns),
|
||||
[categories, columns],
|
||||
createGetCellContent(categories, visibleColumns),
|
||||
[categories, visibleColumns],
|
||||
);
|
||||
|
||||
const handleHeaderClick = useCallback(
|
||||
(col: number) => {
|
||||
if (sort !== undefined) {
|
||||
onSort(columns[col].id as CategoryListUrlSortField);
|
||||
if (sort !== undefined && onSort) {
|
||||
onSort(visibleColumns[col].id as CategoryListUrlSortField);
|
||||
}
|
||||
},
|
||||
[columns, onSort, sort],
|
||||
[visibleColumns, onSort, sort],
|
||||
);
|
||||
|
||||
const handleRowAnchor = useCallback(
|
||||
|
@ -86,7 +95,7 @@ export const CategoryListDatagrid = ({
|
|||
columnSelect={sort !== undefined ? "single" : undefined}
|
||||
verticalBorder={col => col > 0}
|
||||
rowMarkers="checkbox"
|
||||
availableColumns={columns}
|
||||
availableColumns={visibleColumns}
|
||||
rows={categories?.length ?? 0}
|
||||
getCellContent={getCellContent}
|
||||
getCellError={() => false}
|
||||
|
@ -96,21 +105,14 @@ export const CategoryListDatagrid = ({
|
|||
menuItems={() => []}
|
||||
actionButtonPosition="right"
|
||||
selectionActions={() => selectionActionButton}
|
||||
onColumnResize={onColumnResize}
|
||||
onColumnMoved={onColumnMoved}
|
||||
onColumnResize={handlers.onResize}
|
||||
onColumnMoved={handlers.onMove}
|
||||
onRowSelectionChange={onSelectCategoriesIds}
|
||||
renderColumnPicker={defaultProps => (
|
||||
renderColumnPicker={() => (
|
||||
<ColumnPicker
|
||||
{...defaultProps}
|
||||
availableColumns={availableColumnsChoices}
|
||||
initialColumns={columnChoices}
|
||||
defaultColumns={defaultColumns}
|
||||
onSave={onColumnsChange}
|
||||
hasMore={false}
|
||||
loading={false}
|
||||
onFetchMore={() => undefined}
|
||||
onQueryChange={picker.setQuery}
|
||||
query={picker.query}
|
||||
onSave={handlers.onChange}
|
||||
selectedColumns={selectedColumns}
|
||||
staticColumns={staticColumns}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-strict-ignore
|
||||
import { CategoryListUrlSortField } from "@dashboard/categories/urls";
|
||||
import { readonlyTextCell } from "@dashboard/components/Datagrid/customCells/cells";
|
||||
import { AvailableColumn } from "@dashboard/components/Datagrid/types";
|
||||
|
@ -10,57 +9,52 @@ import { IntlShape } from "react-intl";
|
|||
|
||||
import { columnsMessages } from "./messages";
|
||||
|
||||
export const getColumns = (
|
||||
export const categoryListStaticColumnsAdapter = (
|
||||
intl: IntlShape,
|
||||
sort?: Sort<CategoryListUrlSortField>,
|
||||
): AvailableColumn[] => [
|
||||
): AvailableColumn[] =>
|
||||
[
|
||||
{
|
||||
id: "name",
|
||||
title: intl.formatMessage(columnsMessages.categoryName),
|
||||
width: 350,
|
||||
icon: sort
|
||||
? getColumnSortDirectionIcon(sort, CategoryListUrlSortField.name)
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: "subcategories",
|
||||
title: intl.formatMessage(columnsMessages.subcategories),
|
||||
width: 300,
|
||||
icon: sort
|
||||
? getColumnSortDirectionIcon(
|
||||
sort,
|
||||
CategoryListUrlSortField.subcategoryCount,
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: "products",
|
||||
title: intl.formatMessage(columnsMessages.numberOfProducts),
|
||||
width: 300,
|
||||
icon: sort
|
||||
? getColumnSortDirectionIcon(sort, CategoryListUrlSortField.productCount)
|
||||
: undefined,
|
||||
},
|
||||
];
|
||||
].map(column => ({
|
||||
...column,
|
||||
icon: sort ? getColumnSortDirectionIcon(sort, column.id) : undefined,
|
||||
}));
|
||||
|
||||
export const createGetCellContent =
|
||||
(categories: CategoryFragment[], columns: AvailableColumn[]) =>
|
||||
([column, row]: Item): GridCell => {
|
||||
const columnId = columns[column]?.id;
|
||||
const rowData: CategoryFragment | undefined = categories[row];
|
||||
|
||||
if (!columnId) {
|
||||
if (!columnId || !rowData) {
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
|
||||
const rowData = categories[row];
|
||||
|
||||
switch (columnId) {
|
||||
case "name":
|
||||
return readonlyTextCell(rowData?.name ?? "");
|
||||
case "subcategories":
|
||||
return readonlyTextCell(rowData?.children?.totalCount.toString() ?? "");
|
||||
return readonlyTextCell(
|
||||
rowData?.children?.totalCount?.toString() ?? "",
|
||||
);
|
||||
case "products":
|
||||
return readonlyTextCell(rowData?.products?.totalCount.toString() ?? "");
|
||||
return readonlyTextCell(
|
||||
rowData?.products?.totalCount?.toString() ?? "",
|
||||
);
|
||||
default:
|
||||
return readonlyTextCell("", false);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { categoryAddUrl } from "@dashboard/categories/urls";
|
|||
import { DashboardCard } from "@dashboard/components/Card";
|
||||
import { InternalLink } from "@dashboard/components/InternalLink";
|
||||
import { CategoryDetailsQuery } from "@dashboard/graphql";
|
||||
import { RelayToFlat } from "@dashboard/types";
|
||||
import { ListProps, ListViews, RelayToFlat } from "@dashboard/types";
|
||||
import { Box, Button } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
@ -11,7 +11,11 @@ import { FormattedMessage } from "react-intl";
|
|||
import { CategoryDeleteButton } from "../CategoryDeleteButton";
|
||||
import { CategoryListDatagrid } from "../CategoryListDatagrid";
|
||||
|
||||
interface CategorySubcategoriesProps {
|
||||
interface CategorySubcategoriesProps
|
||||
extends Pick<
|
||||
ListProps<ListViews.CATEGORY_LIST>,
|
||||
"onUpdateListSettings" | "settings"
|
||||
> {
|
||||
categoryId: string;
|
||||
disabled: boolean;
|
||||
subcategories: RelayToFlat<CategoryDetailsQuery["category"]["children"]>;
|
||||
|
@ -25,7 +29,10 @@ export const CategorySubcategories = ({
|
|||
disabled,
|
||||
onCategoriesDelete,
|
||||
onSelectCategoriesIds,
|
||||
}: CategorySubcategoriesProps) => (
|
||||
settings,
|
||||
onUpdateListSettings,
|
||||
}: CategorySubcategoriesProps) => {
|
||||
return (
|
||||
<DashboardCard>
|
||||
<DashboardCard.Title>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
|
@ -47,6 +54,8 @@ export const CategorySubcategories = ({
|
|||
</DashboardCard.Title>
|
||||
|
||||
<CategoryListDatagrid
|
||||
settings={settings}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
categories={subcategories}
|
||||
disabled={disabled}
|
||||
onSelectCategoriesIds={onSelectCategoriesIds}
|
||||
|
@ -62,4 +71,5 @@ export const CategorySubcategories = ({
|
|||
}
|
||||
/>
|
||||
</DashboardCard>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { maybe } from "../../../misc";
|
||||
import { RelayToFlat } from "../../../types";
|
||||
import { ListProps, ListViews, RelayToFlat } from "../../../types";
|
||||
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
|
||||
import CategoryBackground from "../CategoryBackground";
|
||||
import { CategoryProducts } from "../CategoryProducts";
|
||||
|
@ -28,7 +28,11 @@ export enum CategoryPageTab {
|
|||
products = "products",
|
||||
}
|
||||
|
||||
export interface CategoryUpdatePageProps {
|
||||
export interface CategoryUpdatePageProps
|
||||
extends Pick<
|
||||
ListProps<ListViews.CATEGORY_LIST>,
|
||||
"onUpdateListSettings" | "settings"
|
||||
> {
|
||||
categoryId: string;
|
||||
changeTab: (index: CategoryPageTab) => void;
|
||||
currentTab: CategoryPageTab;
|
||||
|
@ -70,6 +74,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
|||
onCategoriesDelete,
|
||||
onProductsDelete,
|
||||
onSelectProductsIds,
|
||||
settings,
|
||||
onUpdateListSettings,
|
||||
}: CategoryUpdatePageProps) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
@ -161,6 +167,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
|||
{currentTab === CategoryPageTab.categories && (
|
||||
<CategorySubcategories
|
||||
disabled={disabled}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
settings={settings}
|
||||
subcategories={subcategories}
|
||||
onCategoriesDelete={onCategoriesDelete}
|
||||
onSelectCategoriesIds={onSelectCategoriesIds}
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
useUpdateMetadataMutation,
|
||||
useUpdatePrivateMetadataMutation,
|
||||
} from "@dashboard/graphql";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useLocalPaginator, {
|
||||
useSectionLocalPaginationState,
|
||||
} from "@dashboard/hooks/useLocalPaginator";
|
||||
|
@ -23,6 +24,7 @@ import useNotifier from "@dashboard/hooks/useNotifier";
|
|||
import { PaginatorContext } from "@dashboard/hooks/usePaginator";
|
||||
import { useRowSelection } from "@dashboard/hooks/useRowSelection";
|
||||
import { commonMessages, errorMessages } from "@dashboard/intl";
|
||||
import { ListViews } from "@dashboard/types";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import createMetadataUpdateHandler from "@dashboard/utils/handlers/metadataUpdateHandler";
|
||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||
|
@ -98,6 +100,9 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
setActiveTab(tab);
|
||||
};
|
||||
|
||||
const { settings, updateListSettings } =
|
||||
useListSettings<ListViews.CATEGORY_LIST>(ListViews.CATEGORY_LIST);
|
||||
|
||||
const { data, loading, refetch } = useCategoryDetailsQuery({
|
||||
displayLoader: true,
|
||||
variables: { ...paginationState, id },
|
||||
|
@ -274,6 +279,8 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
<WindowTitle title={maybe(() => data.category.name)} />
|
||||
<CategoryUpdatePage
|
||||
categoryId={id}
|
||||
settings={settings}
|
||||
onUpdateListSettings={updateListSettings}
|
||||
changeTab={changeTab}
|
||||
currentTab={activeTab}
|
||||
category={maybe(() => data.category)}
|
||||
|
|
|
@ -72,6 +72,7 @@ export const defaultListSettings: AppListViewSettings = {
|
|||
},
|
||||
[ListViews.CATEGORY_LIST]: {
|
||||
rowNumber: PAGINATE_BY,
|
||||
columns: ["name", "products", "subcategories"],
|
||||
},
|
||||
[ListViews.COLLECTION_LIST]: {
|
||||
rowNumber: PAGINATE_BY,
|
||||
|
|
Loading…
Reference in a new issue