@vendure/dashboard 3.4.3-master-202509250229 → 3.5.0-minor-202509261210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugin/api/api-extensions.js +11 -14
- package/dist/plugin/api/metrics.resolver.d.ts +2 -2
- package/dist/plugin/api/metrics.resolver.js +2 -2
- package/dist/plugin/config/metrics-strategies.d.ts +9 -9
- package/dist/plugin/config/metrics-strategies.js +6 -6
- package/dist/plugin/constants.d.ts +2 -0
- package/dist/plugin/constants.js +3 -1
- package/dist/plugin/dashboard.plugin.js +13 -0
- package/dist/plugin/service/metrics.service.d.ts +3 -3
- package/dist/plugin/service/metrics.service.js +37 -53
- package/dist/plugin/types.d.ts +9 -12
- package/dist/plugin/types.js +7 -11
- package/dist/vite/utils/compiler.js +2 -0
- package/dist/vite/vite-plugin-vendure-dashboard.js +2 -2
- package/package.json +4 -4
- package/src/app/routes/_authenticated/_collections/collections.tsx +7 -2
- package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +15 -2
- package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +14 -2
- package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +10 -0
- package/src/app/routes/_authenticated/_products/components/product-option-group-badge.tsx +19 -0
- package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +111 -0
- package/src/app/routes/_authenticated/_products/product-option-groups.graphql.ts +103 -0
- package/src/app/routes/_authenticated/_products/products.graphql.ts +13 -1
- package/src/app/routes/_authenticated/_products/products.tsx +27 -3
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +26 -9
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +181 -0
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +208 -0
- package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +4 -1
- package/src/app/routes/_authenticated/index.tsx +41 -24
- package/src/lib/components/data-display/json.tsx +16 -1
- package/src/lib/components/data-input/index.ts +3 -0
- package/src/lib/components/data-input/slug-input.tsx +296 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +13 -6
- package/src/lib/components/data-table/data-table-bulk-action-item.tsx +38 -1
- package/src/lib/components/data-table/data-table-context.tsx +91 -0
- package/src/lib/components/data-table/data-table-filter-badge.tsx +9 -5
- package/src/lib/components/data-table/data-table-view-options.tsx +17 -8
- package/src/lib/components/data-table/data-table.tsx +146 -94
- package/src/lib/components/data-table/global-views-bar.tsx +97 -0
- package/src/lib/components/data-table/global-views-sheet.tsx +11 -0
- package/src/lib/components/data-table/manage-global-views-button.tsx +26 -0
- package/src/lib/components/data-table/my-views-button.tsx +47 -0
- package/src/lib/components/data-table/refresh-button.tsx +12 -3
- package/src/lib/components/data-table/save-view-button.tsx +45 -0
- package/src/lib/components/data-table/save-view-dialog.tsx +113 -0
- package/src/lib/components/data-table/use-generated-columns.tsx +3 -1
- package/src/lib/components/data-table/user-views-sheet.tsx +11 -0
- package/src/lib/components/data-table/views-sheet.tsx +297 -0
- package/src/lib/components/date-range-picker.tsx +184 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +59 -32
- package/src/lib/components/ui/button.tsx +1 -1
- package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +29 -2
- package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +10 -7
- package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +9 -3
- package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +19 -75
- package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +33 -0
- package/src/lib/framework/document-introspection/add-custom-fields.spec.ts +319 -9
- package/src/lib/framework/document-introspection/add-custom-fields.ts +60 -31
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +1 -159
- package/src/lib/framework/document-introspection/include-only-selected-list-fields.spec.ts +1840 -0
- package/src/lib/framework/document-introspection/include-only-selected-list-fields.ts +940 -0
- package/src/lib/framework/document-introspection/testing-utils.ts +161 -0
- package/src/lib/framework/extension-api/display-component-extensions.tsx +2 -0
- package/src/lib/framework/extension-api/types/data-table.ts +62 -4
- package/src/lib/framework/extension-api/types/navigation.ts +16 -0
- package/src/lib/framework/form-engine/utils.ts +34 -0
- package/src/lib/framework/page/list-page.tsx +289 -4
- package/src/lib/framework/page/use-extended-router.tsx +59 -17
- package/src/lib/graphql/api.ts +4 -2
- package/src/lib/graphql/graphql-env.d.ts +13 -10
- package/src/lib/hooks/use-extended-list-query.ts +5 -0
- package/src/lib/hooks/use-saved-views.ts +230 -0
- package/src/lib/index.ts +15 -0
- package/src/lib/types/saved-views.ts +39 -0
- package/src/lib/utils/saved-views-utils.ts +40 -0
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
DropdownMenuItem,
|
|
8
8
|
DropdownMenuTrigger,
|
|
9
9
|
} from '@/vdb/components/ui/dropdown-menu.js';
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/vdb/components/ui/tooltip.js';
|
|
10
11
|
import { Trans } from '@/vdb/lib/trans.js';
|
|
11
12
|
import { camelCaseToTitleCase } from '@/vdb/lib/utils.js';
|
|
12
13
|
import { Column, ColumnDef } from '@tanstack/react-table';
|
|
13
|
-
import {
|
|
14
|
+
import { FilterIcon } from 'lucide-react';
|
|
14
15
|
import { useState } from 'react';
|
|
15
16
|
|
|
16
17
|
export interface AddFilterMenuProps {
|
|
@@ -26,12 +27,18 @@ export function AddFilterMenu({ columns }: Readonly<AddFilterMenuProps>) {
|
|
|
26
27
|
return (
|
|
27
28
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
28
29
|
<DropdownMenu>
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
<
|
|
30
|
+
<Tooltip>
|
|
31
|
+
<TooltipTrigger asChild>
|
|
32
|
+
<DropdownMenuTrigger asChild>
|
|
33
|
+
<Button variant="outline" size="icon">
|
|
34
|
+
<FilterIcon />
|
|
35
|
+
</Button>
|
|
36
|
+
</DropdownMenuTrigger>
|
|
37
|
+
</TooltipTrigger>
|
|
38
|
+
<TooltipContent>
|
|
32
39
|
<Trans>Add filter</Trans>
|
|
33
|
-
</
|
|
34
|
-
</
|
|
40
|
+
</TooltipContent>
|
|
41
|
+
</Tooltip>
|
|
35
42
|
<DropdownMenuContent align="end" className="w-[200px]">
|
|
36
43
|
{filterableColumns.map(column => (
|
|
37
44
|
<DropdownMenuItem
|
|
@@ -16,6 +16,13 @@ import {
|
|
|
16
16
|
} from '../ui/alert-dialog.js';
|
|
17
17
|
import { DropdownMenuItem } from '../ui/dropdown-menu.js';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @description
|
|
21
|
+
*
|
|
22
|
+
* @docsCategory list-views
|
|
23
|
+
* @docsPage bulk-actions
|
|
24
|
+
* @since 3.4.0
|
|
25
|
+
*/
|
|
19
26
|
export interface DataTableBulkActionItemProps {
|
|
20
27
|
label: React.ReactNode;
|
|
21
28
|
icon?: LucideIcon;
|
|
@@ -25,6 +32,36 @@ export interface DataTableBulkActionItemProps {
|
|
|
25
32
|
requiresPermission?: string[];
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @description
|
|
37
|
+
* A component that should be used to implement any bulk actions for list pages & data tables.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* import { DataTableBulkActionItem, Trans } from '\@vendure/dashboard';
|
|
42
|
+
* import { Check } from 'lucide-react';
|
|
43
|
+
*
|
|
44
|
+
* export const MyBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <DataTableBulkActionItem
|
|
48
|
+
* requiresPermission={['ReadMyCustomEntity']}
|
|
49
|
+
* onClick={() => {
|
|
50
|
+
* console.log('Selected items:', selection);
|
|
51
|
+
* }}
|
|
52
|
+
* label={<Trans>Delete</Trans>}
|
|
53
|
+
* confirmationText={<Trans>Are you sure?</Trans>}
|
|
54
|
+
* icon={Check}
|
|
55
|
+
* className="text-destructive"
|
|
56
|
+
* />
|
|
57
|
+
* );
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @docsCategory list-views
|
|
62
|
+
* @docsPage bulk-actions
|
|
63
|
+
* @since 3.4.0
|
|
64
|
+
*/
|
|
28
65
|
export function DataTableBulkActionItem({
|
|
29
66
|
label,
|
|
30
67
|
icon: Icon,
|
|
@@ -32,7 +69,7 @@ export function DataTableBulkActionItem({
|
|
|
32
69
|
className,
|
|
33
70
|
onClick,
|
|
34
71
|
requiresPermission,
|
|
35
|
-
}: DataTableBulkActionItemProps) {
|
|
72
|
+
}: Readonly<DataTableBulkActionItemProps>) {
|
|
36
73
|
const [isOpen, setIsOpen] = useState(false);
|
|
37
74
|
const { hasPermissions } = usePermissions();
|
|
38
75
|
const userHasPermission = hasPermissions(requiresPermission ?? []);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ColumnFiltersState, SortingState, Table } from '@tanstack/react-table';
|
|
4
|
+
import React, { createContext, ReactNode, useContext } from 'react';
|
|
5
|
+
|
|
6
|
+
interface DataTableContextValue {
|
|
7
|
+
columnFilters: ColumnFiltersState;
|
|
8
|
+
setColumnFilters: React.Dispatch<React.SetStateAction<ColumnFiltersState>>;
|
|
9
|
+
searchTerm: string;
|
|
10
|
+
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
|
|
11
|
+
sorting: SortingState;
|
|
12
|
+
setSorting: React.Dispatch<React.SetStateAction<SortingState>>;
|
|
13
|
+
pageId?: string;
|
|
14
|
+
onFilterChange?: (table: Table<any>, filters: ColumnFiltersState) => void;
|
|
15
|
+
onSearchTermChange?: (searchTerm: string) => void;
|
|
16
|
+
onRefresh?: () => void;
|
|
17
|
+
isLoading?: boolean;
|
|
18
|
+
table?: Table<any>;
|
|
19
|
+
handleApplyView: (filters: ColumnFiltersState, searchTerm?: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DataTableContext = createContext<DataTableContextValue | undefined>(undefined);
|
|
23
|
+
|
|
24
|
+
export interface DataTableProviderProps {
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
columnFilters: ColumnFiltersState;
|
|
27
|
+
setColumnFilters: React.Dispatch<React.SetStateAction<ColumnFiltersState>>;
|
|
28
|
+
searchTerm: string;
|
|
29
|
+
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
|
|
30
|
+
sorting: SortingState;
|
|
31
|
+
setSorting: React.Dispatch<React.SetStateAction<SortingState>>;
|
|
32
|
+
pageId?: string;
|
|
33
|
+
onFilterChange?: (table: Table<any>, filters: ColumnFiltersState) => void;
|
|
34
|
+
onSearchTermChange?: (searchTerm: string) => void;
|
|
35
|
+
onRefresh?: () => void;
|
|
36
|
+
isLoading?: boolean;
|
|
37
|
+
table?: Table<any>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function DataTableProvider({
|
|
41
|
+
children,
|
|
42
|
+
columnFilters,
|
|
43
|
+
setColumnFilters,
|
|
44
|
+
searchTerm,
|
|
45
|
+
setSearchTerm,
|
|
46
|
+
sorting,
|
|
47
|
+
setSorting,
|
|
48
|
+
pageId,
|
|
49
|
+
onFilterChange,
|
|
50
|
+
onSearchTermChange,
|
|
51
|
+
onRefresh,
|
|
52
|
+
isLoading,
|
|
53
|
+
table,
|
|
54
|
+
}: DataTableProviderProps) {
|
|
55
|
+
const handleApplyView = (filters: ColumnFiltersState, viewSearchTerm?: string) => {
|
|
56
|
+
setColumnFilters(filters);
|
|
57
|
+
if (viewSearchTerm !== undefined && onSearchTermChange) {
|
|
58
|
+
setSearchTerm(viewSearchTerm);
|
|
59
|
+
onSearchTermChange(viewSearchTerm);
|
|
60
|
+
}
|
|
61
|
+
if (onFilterChange && table) {
|
|
62
|
+
onFilterChange(table, filters);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const value: DataTableContextValue = {
|
|
67
|
+
columnFilters,
|
|
68
|
+
setColumnFilters,
|
|
69
|
+
searchTerm,
|
|
70
|
+
setSearchTerm,
|
|
71
|
+
sorting,
|
|
72
|
+
setSorting,
|
|
73
|
+
pageId,
|
|
74
|
+
onFilterChange,
|
|
75
|
+
onSearchTermChange,
|
|
76
|
+
onRefresh,
|
|
77
|
+
isLoading,
|
|
78
|
+
table,
|
|
79
|
+
handleApplyView,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return <DataTableContext.Provider value={value}>{children}</DataTableContext.Provider>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function useDataTableContext() {
|
|
86
|
+
const context = useContext(DataTableContext);
|
|
87
|
+
if (context === undefined) {
|
|
88
|
+
throw new Error('useDataTableContext must be used within a DataTableProvider');
|
|
89
|
+
}
|
|
90
|
+
return context;
|
|
91
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
|
|
2
|
-
import {
|
|
2
|
+
import { Filter, XIcon } from 'lucide-react';
|
|
3
3
|
import { Badge } from '../ui/badge.js';
|
|
4
4
|
import { HumanReadableOperator, Operator } from './human-readable-operator.js';
|
|
5
5
|
import { ColumnDataType } from './types.js';
|
|
@@ -17,16 +17,20 @@ export function DataTableFilterBadge({
|
|
|
17
17
|
}) {
|
|
18
18
|
const [operator, value] = Object.entries(filter.value as Record<string, unknown>)[0];
|
|
19
19
|
return (
|
|
20
|
-
<Badge
|
|
20
|
+
<Badge
|
|
21
|
+
key={filter.id}
|
|
22
|
+
className="flex gap-1 items-center font-mono cursor-pointer "
|
|
23
|
+
variant="outline"
|
|
24
|
+
onClick={() => onRemove(filter)}
|
|
25
|
+
>
|
|
21
26
|
<Filter size="12" className="opacity-50" />
|
|
22
27
|
<div>{filter.id}</div>
|
|
23
28
|
<div className="text-muted-foreground">
|
|
24
29
|
<HumanReadableOperator operator={operator as Operator} mode="short" />
|
|
25
30
|
</div>
|
|
26
31
|
<FilterValue value={value} dataType={dataType} currencyCode={currencyCode} />
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
</button>
|
|
32
|
+
|
|
33
|
+
<XIcon className="h-4" />
|
|
30
34
|
</Badge>
|
|
31
35
|
);
|
|
32
36
|
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
DropdownMenuTrigger,
|
|
18
18
|
} from '@/vdb/components/ui/dropdown-menu.js';
|
|
19
19
|
import { ScrollArea } from '@/vdb/components/ui/scroll-area.js';
|
|
20
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/vdb/components/ui/tooltip.js';
|
|
20
21
|
import { usePage } from '@/vdb/hooks/use-page.js';
|
|
21
22
|
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
|
|
22
23
|
import { Trans } from '@/vdb/lib/trans.js';
|
|
@@ -78,12 +79,18 @@ export function DataTableViewOptions<TData>({ table }: DataTableViewOptionsProps
|
|
|
78
79
|
return (
|
|
79
80
|
<div className="flex items-center gap-2">
|
|
80
81
|
<DropdownMenu modal={false}>
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
<Tooltip>
|
|
83
|
+
<TooltipTrigger asChild>
|
|
84
|
+
<DropdownMenuTrigger asChild>
|
|
85
|
+
<Button variant="outline" size="sm" className="ml-auto hidden h-8 lg:flex">
|
|
86
|
+
<Settings2 />
|
|
87
|
+
</Button>
|
|
88
|
+
</DropdownMenuTrigger>
|
|
89
|
+
</TooltipTrigger>
|
|
90
|
+
<TooltipContent>
|
|
91
|
+
<Trans>Column settings</Trans>
|
|
92
|
+
</TooltipContent>
|
|
93
|
+
</Tooltip>
|
|
87
94
|
<DropdownMenuContent align="end" className="overflow-auto">
|
|
88
95
|
<ScrollArea className="max-h-[60vh]" type="always">
|
|
89
96
|
<DndContext
|
|
@@ -100,7 +107,7 @@ export function DataTableViewOptions<TData>({ table }: DataTableViewOptionsProps
|
|
|
100
107
|
<DropdownMenuCheckboxItem
|
|
101
108
|
className="capitalize"
|
|
102
109
|
checked={column.getIsVisible()}
|
|
103
|
-
onCheckedChange={value => column.toggleVisibility(
|
|
110
|
+
onCheckedChange={value => column.toggleVisibility(value)}
|
|
104
111
|
onSelect={e => e.preventDefault()}
|
|
105
112
|
>
|
|
106
113
|
{column.id}
|
|
@@ -110,7 +117,9 @@ export function DataTableViewOptions<TData>({ table }: DataTableViewOptionsProps
|
|
|
110
117
|
</SortableContext>
|
|
111
118
|
</DndContext>
|
|
112
119
|
<DropdownMenuSeparator />
|
|
113
|
-
<DropdownMenuItem onClick={handleReset}>
|
|
120
|
+
<DropdownMenuItem onClick={handleReset}>
|
|
121
|
+
<Trans>Reset</Trans>
|
|
122
|
+
</DropdownMenuItem>
|
|
114
123
|
</ScrollArea>
|
|
115
124
|
</DropdownMenuContent>
|
|
116
125
|
</DropdownMenu>
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import { DataTablePagination } from '@/vdb/components/data-table/data-table-pagination.js';
|
|
4
4
|
import { DataTableViewOptions } from '@/vdb/components/data-table/data-table-view-options.js';
|
|
5
|
+
import { GlobalViewsBar } from '@/vdb/components/data-table/global-views-bar.js';
|
|
6
|
+
import { MyViewsButton } from '@/vdb/components/data-table/my-views-button.js';
|
|
5
7
|
import { RefreshButton } from '@/vdb/components/data-table/refresh-button.js';
|
|
8
|
+
import { SaveViewButton } from '@/vdb/components/data-table/save-view-button.js';
|
|
9
|
+
import { Button } from '@/vdb/components/ui/button.js';
|
|
6
10
|
import { Input } from '@/vdb/components/ui/input.js';
|
|
7
11
|
import { Skeleton } from '@/vdb/components/ui/skeleton.js';
|
|
8
12
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/vdb/components/ui/table.js';
|
|
9
13
|
import { BulkAction } from '@/vdb/framework/extension-api/types/index.js';
|
|
10
14
|
import { useChannel } from '@/vdb/hooks/use-channel.js';
|
|
15
|
+
import { usePage } from '@/vdb/hooks/use-page.js';
|
|
16
|
+
import { useSavedViews } from '@/vdb/hooks/use-saved-views.js';
|
|
17
|
+
import { Trans, useLingui } from '@/vdb/lib/trans.js';
|
|
11
18
|
import {
|
|
12
19
|
ColumnDef,
|
|
13
20
|
ColumnFilter,
|
|
@@ -25,6 +32,7 @@ import { RowSelectionState, TableOptions } from '@tanstack/table-core';
|
|
|
25
32
|
import React, { Suspense, useEffect } from 'react';
|
|
26
33
|
import { AddFilterMenu } from './add-filter-menu.js';
|
|
27
34
|
import { DataTableBulkActions } from './data-table-bulk-actions.js';
|
|
35
|
+
import { DataTableProvider } from './data-table-context.js';
|
|
28
36
|
import { DataTableFacetedFilter, DataTableFacetedFilterOption } from './data-table-faceted-filter.js';
|
|
29
37
|
import { DataTableFilterBadge } from './data-table-filter-badge.js';
|
|
30
38
|
|
|
@@ -106,7 +114,12 @@ export function DataTable<TData>({
|
|
|
106
114
|
}: Readonly<DataTableProps<TData>>) {
|
|
107
115
|
const [sorting, setSorting] = React.useState<SortingState>(sortingInitialState || []);
|
|
108
116
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(filtersInitialState || []);
|
|
117
|
+
const [searchTerm, setSearchTerm] = React.useState<string>('');
|
|
109
118
|
const { activeChannel } = useChannel();
|
|
119
|
+
const { pageId } = usePage();
|
|
120
|
+
const savedViewsResult = useSavedViews();
|
|
121
|
+
const globalViews = pageId && onFilterChange ? savedViewsResult.globalViews : [];
|
|
122
|
+
const { i18n } = useLingui();
|
|
110
123
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
111
124
|
pageIndex: (page ?? 1) - 1,
|
|
112
125
|
pageSize: itemsPerPage ?? 10,
|
|
@@ -175,19 +188,37 @@ export function DataTable<TData>({
|
|
|
175
188
|
}, [columnVisibility]);
|
|
176
189
|
|
|
177
190
|
const visibleColumnCount = Object.values(columnVisibility).filter(Boolean).length;
|
|
191
|
+
|
|
192
|
+
const handleSearchChange = (value: string) => {
|
|
193
|
+
setSearchTerm(value);
|
|
194
|
+
onSearchTermChange?.(value);
|
|
195
|
+
};
|
|
196
|
+
|
|
178
197
|
return (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
<DataTableProvider
|
|
199
|
+
columnFilters={columnFilters}
|
|
200
|
+
setColumnFilters={setColumnFilters}
|
|
201
|
+
searchTerm={searchTerm}
|
|
202
|
+
setSearchTerm={setSearchTerm}
|
|
203
|
+
sorting={sorting}
|
|
204
|
+
setSorting={setSorting}
|
|
205
|
+
pageId={pageId}
|
|
206
|
+
onFilterChange={onFilterChange}
|
|
207
|
+
onSearchTermChange={onSearchTermChange}
|
|
208
|
+
onRefresh={onRefresh}
|
|
209
|
+
isLoading={isLoading}
|
|
210
|
+
table={table}
|
|
211
|
+
>
|
|
212
|
+
<div className="space-y-2">
|
|
213
|
+
<div className="flex items-center justify-between gap-2">
|
|
214
|
+
<div className="flex items-center gap-2">
|
|
183
215
|
{onSearchTermChange && (
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
</div>
|
|
216
|
+
<Input
|
|
217
|
+
placeholder={i18n.t('Filter...')}
|
|
218
|
+
value={searchTerm}
|
|
219
|
+
onChange={event => handleSearchChange(event.target.value)}
|
|
220
|
+
className="w-64"
|
|
221
|
+
/>
|
|
191
222
|
)}
|
|
192
223
|
<Suspense>
|
|
193
224
|
{Object.entries(facetedFilters ?? {}).map(([key, filter]) => (
|
|
@@ -201,99 +232,120 @@ export function DataTable<TData>({
|
|
|
201
232
|
))}
|
|
202
233
|
</Suspense>
|
|
203
234
|
{onFilterChange && <AddFilterMenu columns={table.getAllColumns()} />}
|
|
235
|
+
{pageId && onFilterChange && <MyViewsButton />}
|
|
204
236
|
</div>
|
|
205
|
-
<div className="flex gap-
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const column = table.getColumn(f.id);
|
|
210
|
-
const currency = activeChannel?.defaultCurrencyCode ?? 'USD';
|
|
211
|
-
return (
|
|
212
|
-
<DataTableFilterBadge
|
|
213
|
-
key={f.id}
|
|
214
|
-
filter={f}
|
|
215
|
-
currencyCode={currency}
|
|
216
|
-
dataType={
|
|
217
|
-
(column?.columnDef.meta as any)?.fieldInfo?.type ?? 'String'
|
|
218
|
-
}
|
|
219
|
-
onRemove={() =>
|
|
220
|
-
setColumnFilters(old => old.filter(x => x.id !== f.id))
|
|
221
|
-
}
|
|
222
|
-
/>
|
|
223
|
-
);
|
|
224
|
-
})}
|
|
237
|
+
<div className="flex items-center gap-2">
|
|
238
|
+
{pageId && onFilterChange && <SaveViewButton />}
|
|
239
|
+
{!disableViewOptions && <DataTableViewOptions table={table} />}
|
|
240
|
+
{onRefresh && <RefreshButton onRefresh={onRefresh} isLoading={isLoading ?? false} />}
|
|
225
241
|
</div>
|
|
226
242
|
</div>
|
|
227
|
-
<div className="flex items-center justify-start gap-2">
|
|
228
|
-
{!disableViewOptions && <DataTableViewOptions table={table} />}
|
|
229
|
-
{onRefresh && <RefreshButton onRefresh={onRefresh} isLoading={isLoading ?? false} />}
|
|
230
|
-
</div>
|
|
231
|
-
</div>
|
|
232
243
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
<
|
|
238
|
-
|
|
244
|
+
{(pageId && onFilterChange && globalViews.length > 0) ||
|
|
245
|
+
columnFilters.filter(f => !facetedFilters?.[f.id]).length > 0 ? (
|
|
246
|
+
<div className="flex items-center justify-between bg-muted/40 rounded border border-border p-2">
|
|
247
|
+
<div className="flex items-center">
|
|
248
|
+
{pageId && onFilterChange && <GlobalViewsBar />}
|
|
249
|
+
</div>
|
|
250
|
+
<div className="flex gap-1 items-center">
|
|
251
|
+
{columnFilters
|
|
252
|
+
.filter(f => !facetedFilters?.[f.id])
|
|
253
|
+
.map(f => {
|
|
254
|
+
const column = table.getColumn(f.id);
|
|
255
|
+
const currency = activeChannel?.defaultCurrencyCode ?? 'USD';
|
|
239
256
|
return (
|
|
240
|
-
<
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
257
|
+
<DataTableFilterBadge
|
|
258
|
+
key={f.id}
|
|
259
|
+
filter={f}
|
|
260
|
+
currencyCode={currency}
|
|
261
|
+
dataType={
|
|
262
|
+
(column?.columnDef.meta as any)?.fieldInfo?.type ?? 'String'
|
|
263
|
+
}
|
|
264
|
+
onRemove={() =>
|
|
265
|
+
setColumnFilters(old => old.filter(x => x.id !== f.id))
|
|
266
|
+
}
|
|
267
|
+
/>
|
|
248
268
|
);
|
|
249
269
|
})}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
<TableRow
|
|
257
|
-
key={`skeleton-${index}`}
|
|
258
|
-
className="animate-in fade-in duration-100"
|
|
270
|
+
{columnFilters.filter(f => !facetedFilters?.[f.id]).length > 0 && (
|
|
271
|
+
<Button
|
|
272
|
+
variant="ghost"
|
|
273
|
+
size="sm"
|
|
274
|
+
onClick={() => setColumnFilters([])}
|
|
275
|
+
className="text-xs opacity-60 hover:opacity-100"
|
|
259
276
|
>
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
<Trans>Clear all</Trans>
|
|
278
|
+
</Button>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
) : null}
|
|
283
|
+
|
|
284
|
+
<div className="rounded-md border my-2 relative shadow-sm">
|
|
285
|
+
<Table>
|
|
286
|
+
<TableHeader className="bg-muted/50">
|
|
287
|
+
{table.getHeaderGroups().map(headerGroup => (
|
|
288
|
+
<TableRow key={headerGroup.id}>
|
|
289
|
+
{headerGroup.headers.map(header => {
|
|
290
|
+
return (
|
|
291
|
+
<TableHead key={header.id}>
|
|
292
|
+
{header.isPlaceholder
|
|
293
|
+
? null
|
|
294
|
+
: flexRender(
|
|
295
|
+
header.column.columnDef.header,
|
|
296
|
+
header.getContext(),
|
|
297
|
+
)}
|
|
298
|
+
</TableHead>
|
|
299
|
+
);
|
|
300
|
+
})}
|
|
268
301
|
</TableRow>
|
|
269
|
-
))
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
302
|
+
))}
|
|
303
|
+
</TableHeader>
|
|
304
|
+
<TableBody>
|
|
305
|
+
{isLoading && !data?.length ? (
|
|
306
|
+
Array.from({ length: pagination.pageSize }).map((_, index) => (
|
|
307
|
+
<TableRow
|
|
308
|
+
key={`skeleton-${index}`}
|
|
309
|
+
className="animate-in fade-in duration-100"
|
|
310
|
+
>
|
|
311
|
+
{Array.from({ length: visibleColumnCount }).map((_, cellIndex) => (
|
|
312
|
+
<TableCell
|
|
313
|
+
key={`skeleton-cell-${index}-${cellIndex}`}
|
|
314
|
+
className="h-12"
|
|
315
|
+
>
|
|
316
|
+
<Skeleton className="h-4 my-2 w-full" />
|
|
317
|
+
</TableCell>
|
|
318
|
+
))}
|
|
319
|
+
</TableRow>
|
|
320
|
+
))
|
|
321
|
+
) : table.getRowModel().rows?.length ? (
|
|
322
|
+
table.getRowModel().rows.map(row => (
|
|
323
|
+
<TableRow
|
|
324
|
+
key={row.id}
|
|
325
|
+
data-state={row.getIsSelected() && 'selected'}
|
|
326
|
+
className="animate-in fade-in duration-100"
|
|
327
|
+
>
|
|
328
|
+
{row.getVisibleCells().map(cell => (
|
|
329
|
+
<TableCell key={cell.id} className="h-12">
|
|
330
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
331
|
+
</TableCell>
|
|
332
|
+
))}
|
|
333
|
+
</TableRow>
|
|
334
|
+
))
|
|
335
|
+
) : (
|
|
336
|
+
<TableRow className="animate-in fade-in duration-100">
|
|
337
|
+
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
338
|
+
<Trans>No results</Trans>
|
|
339
|
+
</TableCell>
|
|
282
340
|
</TableRow>
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
)}
|
|
291
|
-
{children}
|
|
292
|
-
</TableBody>
|
|
293
|
-
</Table>
|
|
294
|
-
<DataTableBulkActions bulkActions={bulkActions ?? []} table={table} />
|
|
341
|
+
)}
|
|
342
|
+
{children}
|
|
343
|
+
</TableBody>
|
|
344
|
+
</Table>
|
|
345
|
+
<DataTableBulkActions bulkActions={bulkActions ?? []} table={table} />
|
|
346
|
+
</div>
|
|
347
|
+
{onPageChange && totalItems != null && <DataTablePagination table={table} />}
|
|
295
348
|
</div>
|
|
296
|
-
|
|
297
|
-
</>
|
|
349
|
+
</DataTableProvider>
|
|
298
350
|
);
|
|
299
351
|
}
|