Content list datagrid (#4024)
* Build types * (wip) Page list datagrid implementation * Bulk actions * Add changeset
This commit is contained in:
parent
63f2ef0a18
commit
0995b02dfb
18 changed files with 638 additions and 499 deletions
5
.changeset/spotty-horses-wink.md
Normal file
5
.changeset/spotty-horses-wink.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-dashboard": minor
|
||||
---
|
||||
|
||||
Introduce read-only datagrid on content view
|
|
@ -388,6 +388,10 @@
|
|||
"context": "menu item name",
|
||||
"string": "Name"
|
||||
},
|
||||
"0XHXTZ": {
|
||||
"context": "column header",
|
||||
"string": "Title"
|
||||
},
|
||||
"0a0fLZ": {
|
||||
"context": "countries list menu label when no countries are assigned",
|
||||
"string": "There are no countries assigned"
|
||||
|
@ -1028,10 +1032,6 @@
|
|||
"5BajZK": {
|
||||
"string": "View and update your site settings"
|
||||
},
|
||||
"5GSYCR": {
|
||||
"context": "page status",
|
||||
"string": "Visibility"
|
||||
},
|
||||
"5HwLx9": {
|
||||
"context": "alert",
|
||||
"string": "Warehouse limit reached"
|
||||
|
@ -2343,10 +2343,6 @@
|
|||
"F7DxHw": {
|
||||
"string": "Subcategories"
|
||||
},
|
||||
"F8gsds": {
|
||||
"context": "unpublish page, button",
|
||||
"string": "Unpublish"
|
||||
},
|
||||
"FA+MRz": {
|
||||
"string": "Set plugin as active"
|
||||
},
|
||||
|
@ -2804,10 +2800,6 @@
|
|||
"context": "order refund amount",
|
||||
"string": "Max Refund"
|
||||
},
|
||||
"I8dAAe": {
|
||||
"context": "page internal name",
|
||||
"string": "Slug"
|
||||
},
|
||||
"I8mqqj": {
|
||||
"context": "PageTypeDeleteWarningDialog single assigned items button label",
|
||||
"string": "View pages"
|
||||
|
@ -4505,10 +4497,6 @@
|
|||
"UN+yTt": {
|
||||
"string": "Staff Settings"
|
||||
},
|
||||
"UN3qWD": {
|
||||
"context": "page status",
|
||||
"string": "Not Published"
|
||||
},
|
||||
"UNwG+4": {
|
||||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to delete this page?} other{Are you sure you want to delete {displayQuantity} pages?}}"
|
||||
|
@ -4635,10 +4623,6 @@
|
|||
"context": "button",
|
||||
"string": "Create Permission Group"
|
||||
},
|
||||
"V2+HTM": {
|
||||
"context": "dialog header",
|
||||
"string": "Title"
|
||||
},
|
||||
"V2BBQu": {
|
||||
"string": "Currency in both channels must be the same"
|
||||
},
|
||||
|
@ -4890,6 +4874,10 @@
|
|||
"context": "error message",
|
||||
"string": "Login went wrong. Please try again."
|
||||
},
|
||||
"WvMDHa": {
|
||||
"context": "page status",
|
||||
"string": "Not published"
|
||||
},
|
||||
"Ww69SE": {
|
||||
"context": "search input placeholder",
|
||||
"string": "Search tax classes"
|
||||
|
@ -6119,6 +6107,10 @@
|
|||
"context": "button, form submit, grant refund create",
|
||||
"string": "Grant refund"
|
||||
},
|
||||
"ftOPoy": {
|
||||
"context": "bulk actions button label",
|
||||
"string": "Unpublish"
|
||||
},
|
||||
"ftcHpD": {
|
||||
"string": "Customer created"
|
||||
},
|
||||
|
@ -6295,6 +6287,10 @@
|
|||
"hJZwTS": {
|
||||
"string": "Email address"
|
||||
},
|
||||
"hJrzlT": {
|
||||
"context": "tab name",
|
||||
"string": "All content"
|
||||
},
|
||||
"hLOEeb": {
|
||||
"context": "button",
|
||||
"string": "Set as default billing address"
|
||||
|
@ -6513,6 +6509,10 @@
|
|||
"context": "label",
|
||||
"string": "Search pages"
|
||||
},
|
||||
"j/Oo0B": {
|
||||
"context": "bulk actions button label",
|
||||
"string": "Publish"
|
||||
},
|
||||
"j/vV0n": {
|
||||
"context": "channel name",
|
||||
"string": "Channel Name"
|
||||
|
@ -6697,6 +6697,10 @@
|
|||
"kFsTMN": {
|
||||
"string": "Delete customers"
|
||||
},
|
||||
"kIcyUo": {
|
||||
"context": "column header",
|
||||
"string": "Slug"
|
||||
},
|
||||
"kIvvax": {
|
||||
"string": "Search Products..."
|
||||
},
|
||||
|
@ -8367,6 +8371,10 @@
|
|||
"wL7VAE": {
|
||||
"string": "Actions"
|
||||
},
|
||||
"wLB8B3": {
|
||||
"context": "column header",
|
||||
"string": "Visibility"
|
||||
},
|
||||
"wNQzS/": {
|
||||
"string": "The primary address of this customer."
|
||||
},
|
||||
|
@ -8573,10 +8581,6 @@
|
|||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to delete this product?} other{Are you sure you want to delete {displayQuantity} products?}}"
|
||||
},
|
||||
"yEmwxD": {
|
||||
"context": "publish page, button",
|
||||
"string": "Publish"
|
||||
},
|
||||
"yH56V+": {
|
||||
"string": "Ooops!..."
|
||||
},
|
||||
|
@ -8767,6 +8771,10 @@
|
|||
"context": "sum of captured amount of all transactions",
|
||||
"string": "Total captured"
|
||||
},
|
||||
"zfBsje": {
|
||||
"context": "bulk actions button label",
|
||||
"string": "Delete content"
|
||||
},
|
||||
"zfjAc7": {
|
||||
"context": "grant refund, card header",
|
||||
"string": "Unfulfilled products"
|
||||
|
|
|
@ -96,6 +96,7 @@ export const defaultListSettings: AppListViewSettings = {
|
|||
},
|
||||
[ListViews.PAGES_LIST]: {
|
||||
rowNumber: PAGINATE_BY,
|
||||
columns: ["title", "slug", "visible"],
|
||||
},
|
||||
[ListViews.PLUGINS_LIST]: {
|
||||
rowNumber: PAGINATE_BY,
|
||||
|
|
|
@ -6,7 +6,10 @@ import {
|
|||
useBulkRemoveCustomersMutation,
|
||||
useListCustomersQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import { useFilterPresets } from "@dashboard/hooks/useFilterPresets";
|
||||
import {
|
||||
getPresetNameToDelete,
|
||||
useFilterPresets,
|
||||
} from "@dashboard/hooks/useFilterPresets";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
|
@ -38,7 +41,6 @@ import {
|
|||
getFilterOpts,
|
||||
getFilterQueryParam,
|
||||
getFilterVariables,
|
||||
getPresetNameToDelete,
|
||||
storageUtils,
|
||||
} from "./filters";
|
||||
import { getSortQueryVariables } from "./sort";
|
||||
|
|
|
@ -8,7 +8,6 @@ import { CustomerFilterInput } from "@dashboard/graphql";
|
|||
import {
|
||||
createFilterTabUtils,
|
||||
createFilterUtils,
|
||||
GetFilterTabsOutput,
|
||||
getGteLteVariables,
|
||||
getMinMaxQueryParam,
|
||||
} from "../../../utils/filters";
|
||||
|
@ -95,14 +94,3 @@ export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
|
|||
createFilterUtils<CustomerListUrlQueryParams, CustomerListUrlFilters>(
|
||||
CustomerListUrlFiltersEnum,
|
||||
);
|
||||
|
||||
export const getPresetNameToDelete = (
|
||||
presets: GetFilterTabsOutput<string>,
|
||||
presetIdToDelete: number | null,
|
||||
): string => {
|
||||
const presetIndex = presetIdToDelete ? presetIdToDelete - 1 : 0;
|
||||
const preset = presets?.[presetIndex];
|
||||
const tabName = preset?.name ?? "...";
|
||||
|
||||
return tabName;
|
||||
};
|
||||
|
|
|
@ -134,3 +134,14 @@ export const useFilterPresets = <
|
|||
hasPresetsChanged,
|
||||
};
|
||||
};
|
||||
|
||||
export const getPresetNameToDelete = (
|
||||
presets: GetFilterTabsOutput<string>,
|
||||
presetIdToDelete: number | null,
|
||||
): string => {
|
||||
const presetIndex = presetIdToDelete ? presetIdToDelete - 1 : 0;
|
||||
const preset = presets?.[presetIndex];
|
||||
const tabName = preset?.name ?? "...";
|
||||
|
||||
return tabName;
|
||||
};
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
// @ts-strict-ignore
|
||||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||
import TableHead from "@dashboard/components/TableHead";
|
||||
import { TablePaginationWithContext } from "@dashboard/components/TablePagination";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import { PageFragment } from "@dashboard/graphql";
|
||||
import { maybe, renderCollection } from "@dashboard/misc";
|
||||
import { PageListUrlSortField, pageUrl } from "@dashboard/pages/urls";
|
||||
import { ListActions, ListProps, SortPage } from "@dashboard/types";
|
||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
||||
import { Card, TableBody, TableCell, TableFooter } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export interface PageListProps
|
||||
extends ListProps,
|
||||
ListActions,
|
||||
SortPage<PageListUrlSortField> {
|
||||
pages: PageFragment[];
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colSlug: {
|
||||
width: 250,
|
||||
},
|
||||
colTitle: {},
|
||||
colVisibility: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
colSlug: {},
|
||||
colTitle: {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
colVisibility: {},
|
||||
link: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
}),
|
||||
{ name: "PageList" },
|
||||
);
|
||||
|
||||
const numberOfColumns = 4;
|
||||
|
||||
const PageList: React.FC<PageListProps> = props => {
|
||||
const {
|
||||
settings,
|
||||
pages,
|
||||
disabled,
|
||||
onSort,
|
||||
onUpdateListSettings,
|
||||
isChecked,
|
||||
selected,
|
||||
sort,
|
||||
toggle,
|
||||
toggleAll,
|
||||
toolbar,
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ResponsiveTable>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
selected={selected}
|
||||
disabled={disabled}
|
||||
items={pages}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
<TableCellHeader
|
||||
direction={
|
||||
sort.sort === PageListUrlSortField.title
|
||||
? getArrowDirection(sort.asc)
|
||||
: undefined
|
||||
}
|
||||
arrowPosition="right"
|
||||
onClick={() => onSort(PageListUrlSortField.title)}
|
||||
className={classes.colTitle}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="V2+HTM"
|
||||
defaultMessage="Title"
|
||||
description="dialog header"
|
||||
/>
|
||||
</TableCellHeader>
|
||||
<TableCellHeader
|
||||
direction={
|
||||
sort.sort === PageListUrlSortField.slug
|
||||
? getArrowDirection(sort.asc)
|
||||
: undefined
|
||||
}
|
||||
arrowPosition="right"
|
||||
onClick={() => onSort(PageListUrlSortField.slug)}
|
||||
className={classes.colSlug}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="I8dAAe"
|
||||
defaultMessage="Slug"
|
||||
description="page internal name"
|
||||
/>
|
||||
</TableCellHeader>
|
||||
<TableCellHeader
|
||||
direction={
|
||||
sort.sort === PageListUrlSortField.visible
|
||||
? getArrowDirection(sort.asc)
|
||||
: undefined
|
||||
}
|
||||
arrowPosition="right"
|
||||
onClick={() => onSort(PageListUrlSortField.visible)}
|
||||
className={classes.colVisibility}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="5GSYCR"
|
||||
defaultMessage="Visibility"
|
||||
description="page status"
|
||||
/>
|
||||
</TableCellHeader>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRowLink>
|
||||
<TablePaginationWithContext
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
disabled={disabled}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
/>
|
||||
</TableRowLink>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
pages,
|
||||
page => {
|
||||
const isSelected = page ? isChecked(page.id) : false;
|
||||
|
||||
return (
|
||||
<TableRowLink
|
||||
hover={!!page}
|
||||
className={!!page ? classes.link : undefined}
|
||||
href={page && pageUrl(page.id)}
|
||||
key={page ? page.id : "skeleton"}
|
||||
selected={isSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={disabled}
|
||||
disableClickPropagation
|
||||
onChange={() => toggle(page.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTitle}>
|
||||
{maybe<React.ReactNode>(() => page.title, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colSlug}>
|
||||
{maybe<React.ReactNode>(() => page.slug, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colVisibility}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => (
|
||||
<Pill
|
||||
label={
|
||||
page.isPublished
|
||||
? intl.formatMessage({
|
||||
id: "G1KzEx",
|
||||
defaultMessage: "Published",
|
||||
description: "page status",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: "UN3qWD",
|
||||
defaultMessage: "Not Published",
|
||||
description: "page status",
|
||||
})
|
||||
}
|
||||
color={page.isPublished ? "success" : "error"}
|
||||
/>
|
||||
),
|
||||
<Skeleton />,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRowLink>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage
|
||||
id="iMJka8"
|
||||
defaultMessage="No pages found"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PageList.displayName = "PageList";
|
||||
export default PageList;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./PageList";
|
||||
export * from "./PageList";
|
156
src/pages/components/PageListDatagrid/PageListDatagrid.tsx
Normal file
156
src/pages/components/PageListDatagrid/PageListDatagrid.tsx
Normal file
|
@ -0,0 +1,156 @@
|
|||
import { ColumnPicker } from "@dashboard/components/Datagrid/ColumnPicker/ColumnPicker";
|
||||
import { useColumns } from "@dashboard/components/Datagrid/ColumnPicker/useColumns";
|
||||
import Datagrid from "@dashboard/components/Datagrid/Datagrid";
|
||||
import {
|
||||
DatagridChangeStateContext,
|
||||
useDatagridChangeState,
|
||||
} from "@dashboard/components/Datagrid/hooks/useDatagridChange";
|
||||
import { TablePaginationWithContext } from "@dashboard/components/TablePagination";
|
||||
import { Page, Pages } from "@dashboard/pages/types";
|
||||
import { PageListUrlSortField } from "@dashboard/pages/urls";
|
||||
import { ListProps, SortPage } from "@dashboard/types";
|
||||
import { Item } from "@glideapps/glide-data-grid";
|
||||
import { Box, useTheme } from "@saleor/macaw-ui/next";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { createGetCellContent, pageListStaticColumnsAdapter } from "./datagrid";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface PageListDatagridProps
|
||||
extends ListProps,
|
||||
SortPage<PageListUrlSortField> {
|
||||
pages: Pages | undefined;
|
||||
loading: boolean;
|
||||
hasRowHover?: boolean;
|
||||
onSelectPageIds: (rowsIndex: number[], clearSelection: () => void) => void;
|
||||
onRowClick: (id: string) => void;
|
||||
rowAnchor?: (id: string) => string;
|
||||
}
|
||||
|
||||
export const PageListDatagrid = ({
|
||||
pages,
|
||||
sort,
|
||||
loading,
|
||||
settings,
|
||||
onUpdateListSettings,
|
||||
hasRowHover,
|
||||
onRowClick,
|
||||
rowAnchor,
|
||||
onSelectPageIds,
|
||||
onSort,
|
||||
}: PageListDatagridProps) => {
|
||||
const intl = useIntl();
|
||||
const datagrid = useDatagridChangeState();
|
||||
|
||||
const pageListStaticColumns = useMemo(
|
||||
() => pageListStaticColumnsAdapter(intl, sort),
|
||||
[intl, sort],
|
||||
);
|
||||
|
||||
const onColumnChange = useCallback(
|
||||
(picked: string[]) => {
|
||||
if (onUpdateListSettings) {
|
||||
onUpdateListSettings("columns", picked.filter(Boolean));
|
||||
}
|
||||
},
|
||||
[onUpdateListSettings],
|
||||
);
|
||||
|
||||
const {
|
||||
handlers,
|
||||
visibleColumns,
|
||||
staticColumns,
|
||||
selectedColumns,
|
||||
recentlyAddedColumn,
|
||||
} = useColumns({
|
||||
staticColumns: pageListStaticColumns,
|
||||
selectedColumns: settings?.columns ?? [],
|
||||
onSave: onColumnChange,
|
||||
});
|
||||
|
||||
const { themeValues } = useTheme();
|
||||
const getCellContent = useCallback(
|
||||
createGetCellContent({
|
||||
pages,
|
||||
columns: visibleColumns,
|
||||
intl,
|
||||
themeValues,
|
||||
}),
|
||||
[pages, visibleColumns],
|
||||
);
|
||||
|
||||
const handleRowClick = useCallback(
|
||||
([_, row]: Item) => {
|
||||
if (!onRowClick || !pages) {
|
||||
return;
|
||||
}
|
||||
const rowData: Page = pages[row];
|
||||
onRowClick(rowData.id);
|
||||
},
|
||||
[onRowClick, pages],
|
||||
);
|
||||
|
||||
const handleRowAnchor = useCallback(
|
||||
([, row]: Item) => {
|
||||
if (!rowAnchor || !pages) {
|
||||
return "";
|
||||
}
|
||||
const rowData: Page = pages[row];
|
||||
return rowAnchor(rowData.id);
|
||||
},
|
||||
[rowAnchor, pages],
|
||||
);
|
||||
|
||||
const handleHeaderClick = useCallback(
|
||||
(col: number) => {
|
||||
const columnName = visibleColumns[col].id as PageListUrlSortField;
|
||||
|
||||
onSort(columnName);
|
||||
},
|
||||
[visibleColumns, onSort],
|
||||
);
|
||||
|
||||
return (
|
||||
<DatagridChangeStateContext.Provider value={datagrid}>
|
||||
<Datagrid
|
||||
readonly
|
||||
loading={loading}
|
||||
rowMarkers="checkbox"
|
||||
columnSelect="single"
|
||||
hasRowHover={hasRowHover}
|
||||
onColumnMoved={handlers.onMove}
|
||||
onColumnResize={handlers.onResize}
|
||||
verticalBorder={col => col > 0}
|
||||
rows={pages?.length ?? 0}
|
||||
availableColumns={visibleColumns}
|
||||
emptyText={intl.formatMessage(messages.empty)}
|
||||
onRowSelectionChange={onSelectPageIds}
|
||||
getCellContent={getCellContent}
|
||||
getCellError={() => false}
|
||||
selectionActions={() => null}
|
||||
menuItems={() => []}
|
||||
onRowClick={handleRowClick}
|
||||
onHeaderClicked={handleHeaderClick}
|
||||
rowAnchor={handleRowAnchor}
|
||||
recentlyAddedColumn={recentlyAddedColumn}
|
||||
renderColumnPicker={() => (
|
||||
<ColumnPicker
|
||||
staticColumns={staticColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
onToggle={handlers.onToggle}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box paddingX={6}>
|
||||
<TablePaginationWithContext
|
||||
component="div"
|
||||
settings={settings}
|
||||
disabled={loading}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
/>
|
||||
</Box>
|
||||
</DatagridChangeStateContext.Provider>
|
||||
);
|
||||
};
|
88
src/pages/components/PageListDatagrid/datagrid.ts
Normal file
88
src/pages/components/PageListDatagrid/datagrid.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import {
|
||||
readonlyTextCell,
|
||||
tagsCell,
|
||||
} from "@dashboard/components/Datagrid/customCells/cells";
|
||||
import { AvailableColumn } from "@dashboard/components/Datagrid/types";
|
||||
import { getStatusColor } from "@dashboard/misc";
|
||||
import { Pages } from "@dashboard/pages/types";
|
||||
import { PageListUrlSortField } from "@dashboard/pages/urls";
|
||||
import { Sort } from "@dashboard/types";
|
||||
import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon";
|
||||
import { GridCell, Item } from "@glideapps/glide-data-grid";
|
||||
import { ThemeTokensValues } from "@saleor/macaw-ui/next";
|
||||
import { IntlShape } from "react-intl";
|
||||
|
||||
import { columnsMessages, messages } from "./messages";
|
||||
|
||||
export const pageListStaticColumnsAdapter = (
|
||||
intl: IntlShape,
|
||||
sort: Sort<PageListUrlSortField>,
|
||||
): AvailableColumn[] =>
|
||||
[
|
||||
{
|
||||
id: "title",
|
||||
title: intl.formatMessage(columnsMessages.title),
|
||||
width: 450,
|
||||
},
|
||||
{
|
||||
id: "slug",
|
||||
title: intl.formatMessage(columnsMessages.slug),
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
id: "visible",
|
||||
title: intl.formatMessage(columnsMessages.visible),
|
||||
width: 300,
|
||||
},
|
||||
].map(column => ({
|
||||
...column,
|
||||
icon: getColumnSortDirectionIcon(sort, column.id),
|
||||
}));
|
||||
|
||||
export const createGetCellContent =
|
||||
({
|
||||
pages,
|
||||
columns,
|
||||
intl,
|
||||
themeValues,
|
||||
}: {
|
||||
pages: Pages | undefined;
|
||||
columns: AvailableColumn[];
|
||||
intl: IntlShape;
|
||||
themeValues: ThemeTokensValues;
|
||||
}) =>
|
||||
([column, row]: Item): GridCell => {
|
||||
const rowData = pages?.[row];
|
||||
const columnId = columns[column]?.id;
|
||||
|
||||
if (!columnId || !rowData) {
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
|
||||
switch (columnId) {
|
||||
case "title":
|
||||
return readonlyTextCell(rowData?.title ?? "");
|
||||
case "slug":
|
||||
return readonlyTextCell(rowData?.slug ?? "");
|
||||
case "visible":
|
||||
const tag = rowData?.isPublished
|
||||
? intl.formatMessage(messages.published)
|
||||
: intl.formatMessage(messages.notPublished);
|
||||
return tagsCell(
|
||||
[
|
||||
{
|
||||
tag,
|
||||
color:
|
||||
themeValues.colors.background[
|
||||
getStatusColor(
|
||||
rowData?.isPublished ? "success" : "error",
|
||||
) as keyof ThemeTokensValues["colors"]["background"]
|
||||
],
|
||||
},
|
||||
],
|
||||
[tag],
|
||||
);
|
||||
default:
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
};
|
36
src/pages/components/PageListDatagrid/messages.ts
Normal file
36
src/pages/components/PageListDatagrid/messages.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
empty: {
|
||||
id: "iMJka8",
|
||||
defaultMessage: "No pages found",
|
||||
},
|
||||
published: {
|
||||
id: "G1KzEx",
|
||||
defaultMessage: "Published",
|
||||
description: "page status",
|
||||
},
|
||||
notPublished: {
|
||||
id: "WvMDHa",
|
||||
defaultMessage: "Not published",
|
||||
description: "page status",
|
||||
},
|
||||
});
|
||||
|
||||
export const columnsMessages = defineMessages({
|
||||
title: {
|
||||
id: "0XHXTZ",
|
||||
defaultMessage: "Title",
|
||||
description: "column header",
|
||||
},
|
||||
slug: {
|
||||
id: "kIcyUo",
|
||||
defaultMessage: "Slug",
|
||||
description: "column header",
|
||||
},
|
||||
visible: {
|
||||
id: "wLB8B3",
|
||||
defaultMessage: "Visibility",
|
||||
description: "column header",
|
||||
},
|
||||
});
|
|
@ -1,7 +1,10 @@
|
|||
// @ts-strict-ignore
|
||||
import {
|
||||
filterPageProps,
|
||||
filterPresetsProps,
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
sortPageProps,
|
||||
} from "@dashboard/fixtures";
|
||||
import { pageList } from "@dashboard/pages/fixtures";
|
||||
|
@ -12,21 +15,37 @@ import { PaginatorContextDecorator } from "../../../../.storybook/decorators";
|
|||
import PageListPage, { PageListPageProps } from "./PageListPage";
|
||||
|
||||
const props: PageListPageProps = {
|
||||
...filterPageProps,
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...sortPageProps,
|
||||
...filterPresetsProps,
|
||||
settings: {
|
||||
...pageListProps.default.settings,
|
||||
columns: ["title", "slug", "visible"],
|
||||
},
|
||||
pages: pageList,
|
||||
sort: {
|
||||
...sortPageProps.sort,
|
||||
sort: PageListUrlSortField.title,
|
||||
},
|
||||
actionDialogOpts: {
|
||||
open: () => undefined,
|
||||
close: () => undefined,
|
||||
filterOpts: {
|
||||
pageType: {
|
||||
active: false,
|
||||
value: [],
|
||||
choices: [],
|
||||
displayValues: [],
|
||||
},
|
||||
params: {
|
||||
ids: [],
|
||||
},
|
||||
selectedPageIds: [],
|
||||
loading: false,
|
||||
hasPresetsChanged: () => false,
|
||||
onSelectPageIds: () => undefined,
|
||||
onPagesDelete: () => undefined,
|
||||
onPagesPublish: () => undefined,
|
||||
onPagesUnpublish: () => undefined,
|
||||
onPageCreate: () => undefined,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof PageListPage> = {
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
import { ListFilters } from "@dashboard/components/AppLayout/ListFilters";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import { PageFragment } from "@dashboard/graphql";
|
||||
import { BulkDeleteButton } from "@dashboard/components/BulkDeleteButton";
|
||||
import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect";
|
||||
import { ListPageLayout } from "@dashboard/components/Layouts";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { Pages } from "@dashboard/pages/types";
|
||||
import {
|
||||
PageListUrlDialog,
|
||||
PageListUrlQueryParams,
|
||||
PageListUrlSortField,
|
||||
pageUrl,
|
||||
} from "@dashboard/pages/urls";
|
||||
import { ListActions, PageListProps, SortPage } from "@dashboard/types";
|
||||
import {
|
||||
FilterPagePropsWithPresets,
|
||||
PageListProps,
|
||||
SortPage,
|
||||
} from "@dashboard/types";
|
||||
import { Card } from "@material-ui/core";
|
||||
import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import PageList from "../PageList";
|
||||
import PageListSearchAndFilters from "./PageListSearchAndFilters";
|
||||
import {
|
||||
createFilterStructure,
|
||||
PageListFilterKeys,
|
||||
PageListFilterOpts,
|
||||
} from "../../views/PageList/filters";
|
||||
import { PageListDatagrid } from "../PageListDatagrid/PageListDatagrid";
|
||||
import { pagesListSearchAndFiltersMessages as messages } from "./messages";
|
||||
|
||||
export interface PageListActionDialogOpts {
|
||||
open: (action: PageListUrlDialog, newParams?: PageListUrlQueryParams) => void;
|
||||
|
@ -21,41 +36,125 @@ export interface PageListActionDialogOpts {
|
|||
}
|
||||
export interface PageListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
FilterPagePropsWithPresets<PageListFilterKeys, PageListFilterOpts>,
|
||||
SortPage<PageListUrlSortField> {
|
||||
pages: PageFragment[];
|
||||
params: PageListUrlQueryParams;
|
||||
actionDialogOpts: PageListActionDialogOpts;
|
||||
onAdd: () => void;
|
||||
pages: Pages | undefined;
|
||||
selectedPageIds: string[];
|
||||
loading: boolean;
|
||||
onSelectPageIds: (rows: number[], clearSelection: () => void) => void;
|
||||
onPagesDelete: () => void;
|
||||
onPagesPublish: () => void;
|
||||
onPagesUnpublish: () => void;
|
||||
onPageCreate: () => void;
|
||||
}
|
||||
|
||||
const PageListPage: React.FC<PageListPageProps> = ({
|
||||
params,
|
||||
actionDialogOpts,
|
||||
onAdd,
|
||||
selectedFilterPreset,
|
||||
filterOpts,
|
||||
initialSearch,
|
||||
onFilterPresetsAll,
|
||||
onFilterChange,
|
||||
onFilterPresetDelete,
|
||||
onFilterPresetUpdate,
|
||||
onSearchChange,
|
||||
onFilterPresetChange,
|
||||
onFilterPresetPresetSave,
|
||||
filterPresets,
|
||||
selectedPageIds,
|
||||
hasPresetsChanged,
|
||||
onPagesDelete,
|
||||
onPagesPublish,
|
||||
onPagesUnpublish,
|
||||
onPageCreate,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
||||
const structure = createFilterStructure(intl, filterOpts);
|
||||
const [isFilterPresetOpen, setFilterPresetOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.content)}>
|
||||
<Button onClick={onAdd} variant="primary" data-test-id="create-page">
|
||||
<ListPageLayout>
|
||||
<TopNav
|
||||
title={intl.formatMessage(sectionNames.content)}
|
||||
isAlignToRight={false}
|
||||
withoutBorder
|
||||
>
|
||||
<Box
|
||||
__flex={1}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Box display="flex">
|
||||
<Box marginX={5} display="flex" alignItems="center">
|
||||
<ChevronRightIcon />
|
||||
</Box>
|
||||
<FilterPresetsSelect
|
||||
presetsChanged={hasPresetsChanged()}
|
||||
onSelect={onFilterPresetChange}
|
||||
onRemove={onFilterPresetDelete}
|
||||
onUpdate={onFilterPresetUpdate}
|
||||
savedPresets={filterPresets}
|
||||
activePreset={selectedFilterPreset}
|
||||
onSelectAll={onFilterPresetsAll}
|
||||
onSave={onFilterPresetPresetSave}
|
||||
isOpen={isFilterPresetOpen}
|
||||
onOpenChange={setFilterPresetOpen}
|
||||
selectAllLabel={intl.formatMessage({
|
||||
id: "hJrzlT",
|
||||
defaultMessage: "All content",
|
||||
description: "tab name",
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
<Button
|
||||
onClick={onPageCreate}
|
||||
variant="primary"
|
||||
data-test-id="create-page"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="DOVEZF"
|
||||
defaultMessage="Create content"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</TopNav>
|
||||
<Card>
|
||||
<PageListSearchAndFilters
|
||||
params={params}
|
||||
actionDialogOpts={actionDialogOpts}
|
||||
<ListFilters
|
||||
filterStructure={structure}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={intl.formatMessage(messages.searchPlaceholder)}
|
||||
onFilterChange={onFilterChange}
|
||||
onSearchChange={onSearchChange}
|
||||
actions={
|
||||
selectedPageIds.length > 0 && (
|
||||
<Box display="flex" gap={4}>
|
||||
<Button variant="secondary" onClick={onPagesUnpublish}>
|
||||
<FormattedMessage {...messages.unpublish} />
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onPagesPublish}>
|
||||
<FormattedMessage {...messages.publish} />
|
||||
</Button>
|
||||
<BulkDeleteButton onClick={onPagesDelete}>
|
||||
<FormattedMessage {...messages.delete} />
|
||||
</BulkDeleteButton>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<PageListDatagrid
|
||||
{...listProps}
|
||||
hasRowHover={!isFilterPresetOpen}
|
||||
rowAnchor={pageUrl}
|
||||
onRowClick={id => navigate(pageUrl(id))}
|
||||
/>
|
||||
<PageList {...listProps} />
|
||||
</Card>
|
||||
</>
|
||||
</ListPageLayout>
|
||||
);
|
||||
};
|
||||
PageListPage.displayName = "PageListPage";
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
// @ts-strict-ignore
|
||||
import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
|
||||
import FilterBar from "@dashboard/components/FilterBar";
|
||||
import SaveFilterTabDialog, {
|
||||
SaveFilterTabDialogFormData,
|
||||
} from "@dashboard/components/SaveFilterTabDialog";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config";
|
||||
import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils";
|
||||
import useBulkActions from "@dashboard/hooks/useBulkActions";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { pageListUrl, PageListUrlQueryParams } from "@dashboard/pages/urls";
|
||||
import usePageTypeSearch from "@dashboard/searches/usePageTypeSearch";
|
||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import {
|
||||
createFilterStructure,
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterOpts,
|
||||
getFilterQueryParam,
|
||||
getFiltersCurrentTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab,
|
||||
} from "./filters";
|
||||
import { pagesListSearchAndFiltersMessages as messages } from "./messages";
|
||||
import { PageListActionDialogOpts } from "./PageListPage";
|
||||
|
||||
interface PageListSearchAndFiltersProps {
|
||||
params: PageListUrlQueryParams;
|
||||
actionDialogOpts: PageListActionDialogOpts;
|
||||
}
|
||||
|
||||
const PageListSearchAndFilters: React.FC<PageListSearchAndFiltersProps> = ({
|
||||
params,
|
||||
actionDialogOpts,
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const intl = useIntl();
|
||||
|
||||
const defaultSearchVariables = {
|
||||
variables: {
|
||||
...DEFAULT_INITIAL_SEARCH_DATA,
|
||||
first: 5,
|
||||
},
|
||||
};
|
||||
|
||||
const { reset } = useBulkActions(params.ids);
|
||||
|
||||
const {
|
||||
loadMore: fetchMorePageTypes,
|
||||
search: searchPageTypes,
|
||||
result: searchPageTypesResult,
|
||||
} = usePageTypeSearch(defaultSearchVariables);
|
||||
|
||||
const filterOpts = getFilterOpts({
|
||||
params,
|
||||
pageTypes: mapEdgesToItems(searchPageTypesResult?.data?.search),
|
||||
pageTypesProps: {
|
||||
...getSearchFetchMoreProps(searchPageTypesResult, fetchMorePageTypes),
|
||||
onSearchChange: searchPageTypes,
|
||||
},
|
||||
});
|
||||
|
||||
const [changeFilters, resetFilters, handleSearchChange] =
|
||||
createFilterHandlers({
|
||||
createUrl: pageListUrl,
|
||||
getFilterQueryParam,
|
||||
navigate,
|
||||
params,
|
||||
cleanupFn: reset,
|
||||
});
|
||||
|
||||
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||
|
||||
const { open: openModal, close: closeModal } = actionDialogOpts;
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
navigate(
|
||||
pageListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const tabs = getFilterTabs();
|
||||
const currentTab = getFiltersCurrentTab(params, tabs);
|
||||
|
||||
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
const handleTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
reset();
|
||||
navigate(pageListUrl());
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterBar
|
||||
filterStructure={filterStructure}
|
||||
initialSearch={""}
|
||||
onAll={resetFilters}
|
||||
onFilterChange={changeFilters}
|
||||
onSearchChange={handleSearchChange}
|
||||
searchPlaceholder={intl.formatMessage(messages.searchPlaceholder)}
|
||||
allTabLabel={"All Content"}
|
||||
tabs={tabs.map(({ name }) => name)}
|
||||
currentTab={currentTab}
|
||||
onTabDelete={handleTabDelete}
|
||||
onTabChange={handleTabChange}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleTabDelete}
|
||||
tabName={tabs[currentTab - 1]?.name ?? "..."}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageListSearchAndFilters;
|
|
@ -6,4 +6,19 @@ export const pagesListSearchAndFiltersMessages = defineMessages({
|
|||
defaultMessage: "Search Content",
|
||||
description: "search content placeholder",
|
||||
},
|
||||
publish: {
|
||||
id: "j/Oo0B",
|
||||
defaultMessage: "Publish",
|
||||
description: "bulk actions button label",
|
||||
},
|
||||
unpublish: {
|
||||
id: "ftOPoy",
|
||||
defaultMessage: "Unpublish",
|
||||
description: "bulk actions button label",
|
||||
},
|
||||
delete: {
|
||||
id: "zfBsje",
|
||||
defaultMessage: "Delete content",
|
||||
description: "bulk actions button label",
|
||||
},
|
||||
});
|
||||
|
|
5
src/pages/types.ts
Normal file
5
src/pages/types.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { PageListQuery } from "@dashboard/graphql";
|
||||
import { RelayToFlat } from "@dashboard/types";
|
||||
|
||||
export type Pages = RelayToFlat<NonNullable<PageListQuery["pages"]>>;
|
||||
export type Page = Pages[number];
|
|
@ -1,13 +1,18 @@
|
|||
// @ts-strict-ignore
|
||||
import ActionDialog from "@dashboard/components/ActionDialog";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config";
|
||||
import {
|
||||
usePageBulkPublishMutation,
|
||||
usePageBulkRemoveMutation,
|
||||
usePageListQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import useBulkActions from "@dashboard/hooks/useBulkActions";
|
||||
import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils";
|
||||
import {
|
||||
getPresetNameToDelete,
|
||||
useFilterPresets,
|
||||
} from "@dashboard/hooks/useFilterPresets";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
|
@ -16,17 +21,18 @@ import usePaginator, {
|
|||
createPaginationState,
|
||||
PaginatorContext,
|
||||
} from "@dashboard/hooks/usePaginator";
|
||||
import { maybe } from "@dashboard/misc";
|
||||
import { useRowSelection } from "@dashboard/hooks/useRowSelection";
|
||||
import PageTypePickerDialog from "@dashboard/pages/components/PageTypePickerDialog";
|
||||
import usePageTypeSearch from "@dashboard/searches/usePageTypeSearch";
|
||||
import { ListViews } from "@dashboard/types";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
||||
import { getSortParams } from "@dashboard/utils/sort";
|
||||
import { DialogContentText } from "@material-ui/core";
|
||||
import { DeleteIcon, IconButton } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import React, { useCallback } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import PageListPage from "../../components/PageListPage/PageListPage";
|
||||
|
@ -36,6 +42,7 @@ import {
|
|||
PageListUrlDialog,
|
||||
PageListUrlQueryParams,
|
||||
} from "../../urls";
|
||||
import { getFilterOpts, getFilterQueryParam, storageUtils } from "./filters";
|
||||
import { getFilterVariables, getSortQueryVariables } from "./sort";
|
||||
|
||||
interface PageListProps {
|
||||
|
@ -45,16 +52,46 @@ interface PageListProps {
|
|||
export const PageList: React.FC<PageListProps> = ({ params }) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||
params.ids,
|
||||
);
|
||||
const intl = useIntl();
|
||||
const { updateListSettings, settings } = useListSettings(
|
||||
ListViews.PAGES_LIST,
|
||||
);
|
||||
|
||||
usePaginationReset(pageListUrl, params, settings.rowNumber);
|
||||
|
||||
const intl = useIntl();
|
||||
const {
|
||||
clearRowSelection,
|
||||
selectedRowIds,
|
||||
setClearDatagridRowSelectionCallback,
|
||||
setSelectedRowIds,
|
||||
} = useRowSelection(params);
|
||||
|
||||
const [changeFilters, resetFilters, handleSearchChange] =
|
||||
createFilterHandlers({
|
||||
cleanupFn: clearRowSelection,
|
||||
createUrl: pageListUrl,
|
||||
getFilterQueryParam,
|
||||
navigate,
|
||||
params,
|
||||
keepActiveTab: true,
|
||||
});
|
||||
|
||||
const {
|
||||
selectedPreset,
|
||||
presets,
|
||||
hasPresetsChanged,
|
||||
onPresetChange,
|
||||
onPresetDelete,
|
||||
onPresetSave,
|
||||
onPresetUpdate,
|
||||
setPresetIdToDelete,
|
||||
presetIdToDelete,
|
||||
} = useFilterPresets({
|
||||
params,
|
||||
reset: clearRowSelection,
|
||||
getUrl: pageListUrl,
|
||||
storageUtils,
|
||||
});
|
||||
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
const queryVariables = React.useMemo(
|
||||
|
@ -70,8 +107,10 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
variables: queryVariables,
|
||||
});
|
||||
|
||||
const pages = mapEdgesToItems(data?.pages);
|
||||
|
||||
const paginationValues = usePaginator({
|
||||
pageInfo: maybe(() => data.pages.pageInfo),
|
||||
pageInfo: data?.pages?.pageInfo,
|
||||
paginationState,
|
||||
queryString: params,
|
||||
});
|
||||
|
@ -83,7 +122,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
|
||||
const [bulkPageRemove, bulkPageRemoveOpts] = usePageBulkRemoveMutation({
|
||||
onCompleted: data => {
|
||||
if (data.pageBulkDelete.errors.length === 0) {
|
||||
if (data.pageBulkDelete?.errors.length === 0) {
|
||||
closeModal();
|
||||
notify({
|
||||
status: "success",
|
||||
|
@ -93,7 +132,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
description: "notification",
|
||||
}),
|
||||
});
|
||||
reset();
|
||||
clearRowSelection();
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
|
@ -101,7 +140,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
|
||||
const [bulkPagePublish, bulkPagePublishOpts] = usePageBulkPublishMutation({
|
||||
onCompleted: data => {
|
||||
if (data.pageBulkPublish.errors.length === 0) {
|
||||
if (data.pageBulkPublish?.errors.length === 0) {
|
||||
closeModal();
|
||||
notify({
|
||||
status: "success",
|
||||
|
@ -111,7 +150,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
description: "notification",
|
||||
}),
|
||||
});
|
||||
reset();
|
||||
clearRowSelection();
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
|
@ -133,66 +172,72 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
onFetchMore: loadMoreDialogPageTypes,
|
||||
};
|
||||
|
||||
const filterOpts = getFilterOpts({
|
||||
params,
|
||||
pageTypes: mapEdgesToItems(searchDialogPageTypesOpts?.data?.search),
|
||||
pageTypesProps: {
|
||||
...getSearchFetchMoreProps(
|
||||
searchDialogPageTypesOpts,
|
||||
loadMoreDialogPageTypes,
|
||||
),
|
||||
onSearchChange: searchDialogPageTypes,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSetSelectedPageIds = useCallback(
|
||||
(rows: number[], clearSelection: () => void) => {
|
||||
if (!pages) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rowsIds = rows.map(row => pages[row].id);
|
||||
const haveSaveValues = isEqual(rowsIds, selectedRowIds);
|
||||
|
||||
if (!haveSaveValues) {
|
||||
setSelectedRowIds(rowsIds);
|
||||
}
|
||||
|
||||
setClearDatagridRowSelectionCallback(clearSelection);
|
||||
},
|
||||
[
|
||||
pages,
|
||||
selectedRowIds,
|
||||
setClearDatagridRowSelectionCallback,
|
||||
setSelectedRowIds,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<PaginatorContext.Provider value={paginationValues}>
|
||||
<PageListPage
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
settings={settings}
|
||||
pages={mapEdgesToItems(data?.pages)}
|
||||
pages={pages}
|
||||
onUpdateListSettings={updateListSettings}
|
||||
onAdd={() => openModal("create-page")}
|
||||
onPageCreate={() => openModal("create-page")}
|
||||
onSort={handleSort}
|
||||
actionDialogOpts={{
|
||||
open: openModal,
|
||||
close: closeModal,
|
||||
}}
|
||||
params={params}
|
||||
toolbar={
|
||||
<>
|
||||
<Button
|
||||
onClick={() =>
|
||||
openModal("unpublish", {
|
||||
ids: listElements,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="F8gsds"
|
||||
defaultMessage="Unpublish"
|
||||
description="unpublish page, button"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
openModal("publish", {
|
||||
ids: listElements,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="yEmwxD"
|
||||
defaultMessage="Publish"
|
||||
description="publish page, button"
|
||||
/>
|
||||
</Button>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
openModal("remove", {
|
||||
ids: listElements,
|
||||
})
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
isChecked={isSelected}
|
||||
selected={listElements.length}
|
||||
sort={getSortParams(params)}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
selectedPageIds={selectedRowIds}
|
||||
onPagesDelete={() => openModal("remove", { ids: selectedRowIds })}
|
||||
onPagesPublish={() => openModal("publish", { ids: selectedRowIds })}
|
||||
onPagesUnpublish={() => openModal("unpublish", { ids: selectedRowIds })}
|
||||
onSelectPageIds={handleSetSelectedPageIds}
|
||||
filterOpts={filterOpts}
|
||||
onFilterChange={changeFilters}
|
||||
initialSearch={params?.query ?? ""}
|
||||
onSearchChange={handleSearchChange}
|
||||
onFilterPresetChange={onPresetChange}
|
||||
onFilterPresetDelete={(id: number) => {
|
||||
setPresetIdToDelete(id);
|
||||
openModal("delete-search");
|
||||
}}
|
||||
onFilterPresetUpdate={onPresetUpdate}
|
||||
onFilterPresetPresetSave={() => openModal("save-search")}
|
||||
selectedFilterPreset={selectedPreset}
|
||||
filterPresets={presets.map(preset => preset.name)}
|
||||
hasPresetsChanged={hasPresetsChanged}
|
||||
onFilterPresetsAll={resetFilters}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "publish"}
|
||||
|
@ -201,7 +246,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
onConfirm={() =>
|
||||
bulkPagePublish({
|
||||
variables: {
|
||||
ids: params.ids,
|
||||
ids: selectedRowIds,
|
||||
isPublished: true,
|
||||
},
|
||||
})
|
||||
|
@ -218,10 +263,8 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
defaultMessage="{counter,plural,one{Are you sure you want to publish this page?} other{Are you sure you want to publish {displayQuantity} pages?}}"
|
||||
description="dialog content"
|
||||
values={{
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: (
|
||||
<strong>{maybe(() => params.ids.length)}</strong>
|
||||
),
|
||||
counter: selectedRowIds.length,
|
||||
displayQuantity: <strong>{selectedRowIds.length}</strong>,
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
|
@ -233,7 +276,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
onConfirm={() =>
|
||||
bulkPagePublish({
|
||||
variables: {
|
||||
ids: params.ids,
|
||||
ids: selectedRowIds,
|
||||
isPublished: false,
|
||||
},
|
||||
})
|
||||
|
@ -249,8 +292,8 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
defaultMessage="{counter,plural,one{Are you sure you want to unpublish this page?} other{Are you sure you want to unpublish {displayQuantity} pages?}}"
|
||||
description="dialog content"
|
||||
values={{
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>,
|
||||
counter: selectedRowIds.length,
|
||||
displayQuantity: <strong>{selectedRowIds.length}</strong>,
|
||||
}}
|
||||
/>
|
||||
</ActionDialog>
|
||||
|
@ -261,7 +304,7 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
onConfirm={() =>
|
||||
bulkPageRemove({
|
||||
variables: {
|
||||
ids: params.ids,
|
||||
ids: selectedRowIds,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -277,8 +320,8 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
defaultMessage="{counter,plural,one{Are you sure you want to delete this page?} other{Are you sure you want to delete {displayQuantity} pages?}}"
|
||||
description="dialog content"
|
||||
values={{
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>,
|
||||
counter: selectedRowIds.length,
|
||||
displayQuantity: <strong>{selectedRowIds.length}</strong>,
|
||||
}}
|
||||
/>
|
||||
</ActionDialog>
|
||||
|
@ -299,6 +342,19 @@ export const PageList: React.FC<PageListProps> = ({ params }) => {
|
|||
)
|
||||
}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={onPresetSave}
|
||||
/>
|
||||
<DeleteFilterTabDialog
|
||||
open={params.action === "delete-search"}
|
||||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={onPresetDelete}
|
||||
tabName={getPresetNameToDelete(presets, presetIdToDelete)}
|
||||
/>
|
||||
</PaginatorContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -113,8 +113,7 @@ export type PageListUrlQueryParams = Pagination &
|
|||
ActiveTab &
|
||||
Search;
|
||||
|
||||
export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
|
||||
createFilterTabUtils<PageListUrlFilters>(PAGES_FILTERS_KEY);
|
||||
export const storageUtils = createFilterTabUtils<string>(PAGES_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
|
||||
createFilterUtils<PageListUrlQueryParams, PageListUrlFilters>(
|
Loading…
Reference in a new issue