@vendure/dashboard 3.3.5-master-202506250727 → 3.3.5-master-202506251305
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/tests/barrel-exports.spec.js +1 -1
- package/dist/plugin/vite-plugin-config.js +1 -0
- package/dist/plugin/vite-plugin-dashboard-metadata.d.ts +1 -3
- package/dist/plugin/vite-plugin-dashboard-metadata.js +1 -8
- package/dist/plugin/vite-plugin-tailwind-source.d.ts +7 -0
- package/dist/plugin/vite-plugin-tailwind-source.js +49 -0
- package/dist/plugin/vite-plugin-vendure-dashboard.js +3 -1
- package/package.json +4 -4
- package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +98 -0
- package/src/app/routes/_authenticated/_products/components/assign-to-channel-dialog.tsx +126 -0
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +268 -0
- package/src/app/routes/_authenticated/_products/products.graphql.ts +64 -0
- package/src/app/routes/_authenticated/_products/products.tsx +31 -2
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +3 -1
- package/src/app/styles.css +3 -0
- package/src/lib/components/data-table/data-table-bulk-action-item.tsx +101 -0
- package/src/lib/components/data-table/data-table-bulk-actions.tsx +89 -0
- package/src/lib/components/data-table/data-table-filter-badge.tsx +16 -8
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +4 -4
- package/src/lib/components/data-table/data-table-pagination.tsx +2 -2
- package/src/lib/components/data-table/data-table.tsx +50 -31
- package/src/lib/components/data-table/human-readable-operator.tsx +3 -3
- package/src/lib/components/shared/assigned-facet-values.tsx +1 -5
- package/src/lib/components/shared/paginated-list-data-table.tsx +47 -11
- package/src/lib/framework/data-table/data-table-extensions.ts +21 -0
- package/src/lib/framework/data-table/data-table-types.ts +25 -0
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +11 -0
- package/src/lib/framework/extension-api/extension-api-types.ts +35 -0
- package/src/lib/framework/form-engine/use-generated-form.tsx +2 -5
- package/src/lib/framework/layout-engine/page-block-provider.tsx +6 -0
- package/src/lib/framework/layout-engine/page-layout.tsx +43 -33
- package/src/lib/framework/page/list-page.tsx +6 -8
- package/src/lib/framework/registry/registry-types.ts +4 -2
- package/src/lib/hooks/use-page-block.tsx +10 -0
- package/src/lib/index.ts +8 -1
- package/vite/tests/barrel-exports.spec.ts +13 -9
- package/vite/vite-plugin-config.ts +1 -0
- package/vite/vite-plugin-dashboard-metadata.ts +1 -9
- package/vite/vite-plugin-tailwind-source.ts +65 -0
- package/vite/vite-plugin-vendure-dashboard.ts +5 -3
- /package/src/lib/components/data-table/{data-table-types.ts → types.ts} +0 -0
|
@@ -7,15 +7,9 @@ import {
|
|
|
7
7
|
} from '@/framework/document-introspection/get-document-structure.js';
|
|
8
8
|
import { useListQueryFields } from '@/framework/document-introspection/hooks.js';
|
|
9
9
|
import { api } from '@/graphql/api.js';
|
|
10
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
10
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
11
11
|
import { useDebounce } from '@uidotdev/usehooks';
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
DropdownMenu,
|
|
15
|
-
DropdownMenuContent,
|
|
16
|
-
DropdownMenuItem,
|
|
17
|
-
DropdownMenuTrigger,
|
|
18
|
-
} from '@/components/ui/dropdown-menu.js';
|
|
19
13
|
import {
|
|
20
14
|
AlertDialog,
|
|
21
15
|
AlertDialogAction,
|
|
@@ -27,11 +21,17 @@ import {
|
|
|
27
21
|
AlertDialogTitle,
|
|
28
22
|
AlertDialogTrigger,
|
|
29
23
|
} from '@/components/ui/alert-dialog.js';
|
|
24
|
+
import {
|
|
25
|
+
DropdownMenu,
|
|
26
|
+
DropdownMenuContent,
|
|
27
|
+
DropdownMenuItem,
|
|
28
|
+
DropdownMenuTrigger,
|
|
29
|
+
} from '@/components/ui/dropdown-menu.js';
|
|
30
30
|
import { DisplayComponent } from '@/framework/component-registry/dynamic-component.js';
|
|
31
|
+
import { BulkAction } from '@/framework/data-table/data-table-types.js';
|
|
31
32
|
import { ResultOf } from '@/graphql/graphql.js';
|
|
32
33
|
import { Trans, useLingui } from '@/lib/trans.js';
|
|
33
34
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
34
|
-
import { useQuery } from '@tanstack/react-query';
|
|
35
35
|
import {
|
|
36
36
|
ColumnFiltersState,
|
|
37
37
|
ColumnSort,
|
|
@@ -44,6 +44,7 @@ import { EllipsisIcon, TrashIcon } from 'lucide-react';
|
|
|
44
44
|
import React, { useMemo } from 'react';
|
|
45
45
|
import { toast } from 'sonner';
|
|
46
46
|
import { Button } from '../ui/button.js';
|
|
47
|
+
import { Checkbox } from '../ui/checkbox.js';
|
|
47
48
|
|
|
48
49
|
// Type that identifies a paginated list structure (has items array and totalItems)
|
|
49
50
|
type IsPaginatedList<T> = T extends { items: any[]; totalItems: number } ? true : false;
|
|
@@ -227,6 +228,7 @@ export interface PaginatedListDataTableProps<
|
|
|
227
228
|
onColumnVisibilityChange?: (table: Table<any>, columnVisibility: VisibilityState) => void;
|
|
228
229
|
facetedFilters?: FacetedFilterConfig<T>;
|
|
229
230
|
rowActions?: RowAction<PaginatedListItemFields<T>>[];
|
|
231
|
+
bulkActions?: BulkAction[];
|
|
230
232
|
disableViewOptions?: boolean;
|
|
231
233
|
transformData?: (data: PaginatedListItemFields<T>[]) => PaginatedListItemFields<T>[];
|
|
232
234
|
setTableOptions?: (table: TableOptions<any>) => TableOptions<any>;
|
|
@@ -265,6 +267,7 @@ export function PaginatedListDataTable<
|
|
|
265
267
|
onColumnVisibilityChange,
|
|
266
268
|
facetedFilters,
|
|
267
269
|
rowActions,
|
|
270
|
+
bulkActions,
|
|
268
271
|
disableViewOptions,
|
|
269
272
|
setTableOptions,
|
|
270
273
|
transformData,
|
|
@@ -309,6 +312,7 @@ export function PaginatedListDataTable<
|
|
|
309
312
|
function refetchPaginatedList() {
|
|
310
313
|
queryClient.invalidateQueries({ queryKey });
|
|
311
314
|
}
|
|
315
|
+
|
|
312
316
|
registerRefresher?.(refetchPaginatedList);
|
|
313
317
|
|
|
314
318
|
const { data } = useQuery({
|
|
@@ -427,7 +431,10 @@ export function PaginatedListDataTable<
|
|
|
427
431
|
// existing order
|
|
428
432
|
const orderedColumns = finalColumns
|
|
429
433
|
.filter(column => column.id && defaultColumnOrder.includes(column.id as any))
|
|
430
|
-
.sort(
|
|
434
|
+
.sort(
|
|
435
|
+
(a, b) =>
|
|
436
|
+
defaultColumnOrder.indexOf(a.id as any) - defaultColumnOrder.indexOf(b.id as any),
|
|
437
|
+
);
|
|
431
438
|
const remainingColumns = finalColumns.filter(
|
|
432
439
|
column => !column.id || !defaultColumnOrder.includes(column.id as any),
|
|
433
440
|
);
|
|
@@ -441,6 +448,31 @@ export function PaginatedListDataTable<
|
|
|
441
448
|
}
|
|
442
449
|
}
|
|
443
450
|
|
|
451
|
+
// Add the row selection column
|
|
452
|
+
finalColumns.unshift({
|
|
453
|
+
id: 'selection',
|
|
454
|
+
accessorKey: 'selection',
|
|
455
|
+
header: ({ table }) => (
|
|
456
|
+
<Checkbox
|
|
457
|
+
className="mx-1"
|
|
458
|
+
checked={table.getIsAllRowsSelected()}
|
|
459
|
+
onCheckedChange={checked =>
|
|
460
|
+
table.toggleAllRowsSelected(checked === 'indeterminate' ? undefined : checked)
|
|
461
|
+
}
|
|
462
|
+
/>
|
|
463
|
+
),
|
|
464
|
+
enableColumnFilter: false,
|
|
465
|
+
cell: ({ row }) => {
|
|
466
|
+
return (
|
|
467
|
+
<Checkbox
|
|
468
|
+
className="mx-1"
|
|
469
|
+
checked={row.getIsSelected()}
|
|
470
|
+
onCheckedChange={row.getToggleSelectedHandler()}
|
|
471
|
+
/>
|
|
472
|
+
);
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
444
476
|
return { columns: finalColumns, customFieldColumnNames };
|
|
445
477
|
}, [fields, customizeColumns, rowActions]);
|
|
446
478
|
|
|
@@ -465,6 +497,7 @@ export function PaginatedListDataTable<
|
|
|
465
497
|
defaultColumnVisibility={columnVisibility}
|
|
466
498
|
facetedFilters={facetedFilters}
|
|
467
499
|
disableViewOptions={disableViewOptions}
|
|
500
|
+
bulkActions={bulkActions}
|
|
468
501
|
setTableOptions={setTableOptions}
|
|
469
502
|
onRefresh={refetchPaginatedList}
|
|
470
503
|
/>
|
|
@@ -536,7 +569,7 @@ function DeleteMutationRowAction({
|
|
|
536
569
|
return (
|
|
537
570
|
<AlertDialog>
|
|
538
571
|
<AlertDialogTrigger asChild>
|
|
539
|
-
<DropdownMenuItem onSelect={
|
|
572
|
+
<DropdownMenuItem onSelect={e => e.preventDefault()}>
|
|
540
573
|
<div className="flex items-center gap-2 text-destructive">
|
|
541
574
|
<TrashIcon className="w-4 h-4 text-destructive" />
|
|
542
575
|
<Trans>Delete</Trans>
|
|
@@ -549,7 +582,9 @@ function DeleteMutationRowAction({
|
|
|
549
582
|
<Trans>Confirm deletion</Trans>
|
|
550
583
|
</AlertDialogTitle>
|
|
551
584
|
<AlertDialogDescription>
|
|
552
|
-
<Trans>
|
|
585
|
+
<Trans>
|
|
586
|
+
Are you sure you want to delete this item? This action cannot be undone.
|
|
587
|
+
</Trans>
|
|
553
588
|
</AlertDialogDescription>
|
|
554
589
|
</AlertDialogHeader>
|
|
555
590
|
<AlertDialogFooter>
|
|
@@ -567,6 +602,7 @@ function DeleteMutationRowAction({
|
|
|
567
602
|
</AlertDialog>
|
|
568
603
|
);
|
|
569
604
|
}
|
|
605
|
+
|
|
570
606
|
/**
|
|
571
607
|
* Returns the default column visibility configuration.
|
|
572
608
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BulkAction } from '@/framework/data-table/data-table-types.js';
|
|
2
|
+
|
|
3
|
+
import { globalRegistry } from '../registry/global-registry.js';
|
|
4
|
+
|
|
5
|
+
globalRegistry.register('bulkActionsRegistry', new Map<string, BulkAction[]>());
|
|
6
|
+
|
|
7
|
+
export function getBulkActions(pageId: string, blockId = 'list-table'): BulkAction[] {
|
|
8
|
+
const key = createKey(pageId, blockId);
|
|
9
|
+
return globalRegistry.get('bulkActionsRegistry').get(key) || [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function addBulkAction(pageId: string, blockId: string | undefined, action: BulkAction) {
|
|
13
|
+
const bulkActionsRegistry = globalRegistry.get('bulkActionsRegistry');
|
|
14
|
+
const key = createKey(pageId, blockId);
|
|
15
|
+
const existingActions = bulkActionsRegistry.get(key) || [];
|
|
16
|
+
bulkActionsRegistry.set(key, [...existingActions, action]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createKey(pageId: string, blockId: string | undefined): string {
|
|
20
|
+
return `${pageId}__${blockId ?? 'list-table'}`;
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Table } from '@tanstack/react-table';
|
|
2
|
+
|
|
3
|
+
export type BulkActionContext<Item extends { id: string } & Record<string, any>> = {
|
|
4
|
+
selection: Item[];
|
|
5
|
+
table: Table<Item>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type BulkActionComponent<Item extends { id: string } & Record<string, any>> = React.FunctionComponent<
|
|
9
|
+
BulkActionContext<Item>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @description
|
|
14
|
+
* **Status: Developer Preview**
|
|
15
|
+
*
|
|
16
|
+
* A bulk action is a component that will be rendered in the bulk actions dropdown.
|
|
17
|
+
*
|
|
18
|
+
* @docsCategory components
|
|
19
|
+
* @docsPage DataTableBulkActions
|
|
20
|
+
* @since 3.4.0
|
|
21
|
+
*/
|
|
22
|
+
export type BulkAction = {
|
|
23
|
+
order?: number;
|
|
24
|
+
component: BulkActionComponent<any>;
|
|
25
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { addBulkAction } from '@/framework/data-table/data-table-extensions.js';
|
|
2
|
+
|
|
1
3
|
import { registerDashboardWidget } from '../dashboard-widget/widget-extensions.js';
|
|
2
4
|
import { addCustomFormComponent } from '../form-engine/custom-form-component-extensions.js';
|
|
3
5
|
import {
|
|
@@ -82,6 +84,15 @@ export function defineDashboardExtension(extension: DashboardExtension) {
|
|
|
82
84
|
addCustomFormComponent(component);
|
|
83
85
|
}
|
|
84
86
|
}
|
|
87
|
+
if (extension.dataTables) {
|
|
88
|
+
for (const dataTable of extension.dataTables) {
|
|
89
|
+
if (dataTable.bulkActions?.length) {
|
|
90
|
+
for (const action of dataTable.bulkActions) {
|
|
91
|
+
addBulkAction(dataTable.pageId, dataTable.blockId, action);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
85
96
|
const callbacks = globalRegistry.get('extensionSourceChangeCallbacks');
|
|
86
97
|
if (callbacks.size) {
|
|
87
98
|
for (const callback of callbacks) {
|
|
@@ -5,6 +5,7 @@ import type React from 'react';
|
|
|
5
5
|
|
|
6
6
|
import { DashboardAlertDefinition } from '../alert/types.js';
|
|
7
7
|
import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
|
|
8
|
+
import { BulkAction } from '../data-table/data-table-types.js';
|
|
8
9
|
import { CustomFormComponentInputProps } from '../form-engine/custom-form-component.js';
|
|
9
10
|
import { NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
10
11
|
|
|
@@ -109,6 +110,35 @@ export interface DashboardPageBlockDefinition {
|
|
|
109
110
|
requiresPermission?: string | string[];
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @description
|
|
115
|
+
* **Status: Developer Preview**
|
|
116
|
+
*
|
|
117
|
+
* This allows you to customize aspects of existing data tables in the dashboard.
|
|
118
|
+
*
|
|
119
|
+
* @docsCategory extensions
|
|
120
|
+
* @since 3.4.0
|
|
121
|
+
*/
|
|
122
|
+
export interface DashboardDataTableDefinition {
|
|
123
|
+
/**
|
|
124
|
+
* @description
|
|
125
|
+
* The ID of the page where the data table is located, e.g. `'product-list'`, `'order-list'`.
|
|
126
|
+
*/
|
|
127
|
+
pageId: string;
|
|
128
|
+
/**
|
|
129
|
+
* @description
|
|
130
|
+
* The ID of the data table block. Defaults to `'list-table'`, which is the default blockId
|
|
131
|
+
* for the standard list pages. However, some other pages may use a different blockId,
|
|
132
|
+
* such as `'product-variants-table'` on the `'product-detail'` page.
|
|
133
|
+
*/
|
|
134
|
+
blockId?: string;
|
|
135
|
+
/**
|
|
136
|
+
* @description
|
|
137
|
+
* An array of additional bulk actions that will be available on the data table.
|
|
138
|
+
*/
|
|
139
|
+
bulkActions?: BulkAction[];
|
|
140
|
+
}
|
|
141
|
+
|
|
112
142
|
/**
|
|
113
143
|
* @description
|
|
114
144
|
* **Status: Developer Preview**
|
|
@@ -155,4 +185,9 @@ export interface DashboardExtension {
|
|
|
155
185
|
* Allows you to define custom form components for custom fields in the dashboard.
|
|
156
186
|
*/
|
|
157
187
|
customFormComponents?: DashboardCustomFormComponent[];
|
|
188
|
+
/**
|
|
189
|
+
* @description
|
|
190
|
+
* Allows you to customize aspects of existing data tables in the dashboard.
|
|
191
|
+
*/
|
|
192
|
+
dataTables?: DashboardDataTableDefinition[];
|
|
158
193
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { getOperationVariablesFields } from '@/framework/document-introspection/get-document-structure.js';
|
|
2
|
-
import {
|
|
3
|
-
createFormSchemaFromFields,
|
|
4
|
-
getDefaultValuesFromFields,
|
|
5
|
-
} from '@/framework/form-engine/form-schema-tools.js';
|
|
2
|
+
import { createFormSchemaFromFields, getDefaultValuesFromFields } from '@/framework/form-engine/form-schema-tools.js';
|
|
6
3
|
import { useChannel } from '@/hooks/use-channel.js';
|
|
7
4
|
import { useServerConfig } from '@/hooks/use-server-config.js';
|
|
8
5
|
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
@@ -57,7 +54,7 @@ export function useGeneratedForm<
|
|
|
57
54
|
},
|
|
58
55
|
mode: 'onChange',
|
|
59
56
|
defaultValues,
|
|
60
|
-
values: processedEntity ? processedEntity : defaultValues,
|
|
57
|
+
values: processedEntity ? setValues(processedEntity) : defaultValues,
|
|
61
58
|
});
|
|
62
59
|
let submitHandler = (event: FormEvent) => {
|
|
63
60
|
event.preventDefault();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PageBlockProps } from '@/framework/layout-engine/page-layout.js';
|
|
2
|
+
import { createContext } from 'react';
|
|
3
|
+
|
|
4
|
+
export type PageBlockContextValue = Pick<PageBlockProps, 'blockId' | 'column' | 'title' | 'description'>;
|
|
5
|
+
|
|
6
|
+
export const PageBlockContext = createContext<PageBlockContextValue | undefined>(undefined);
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { CustomFieldsForm } from '@/components/shared/custom-fields-form.js';
|
|
2
|
+
import { NavigationConfirmation } from '@/components/shared/navigation-confirmation.js';
|
|
2
3
|
import { PermissionGuard } from '@/components/shared/permission-guard.js';
|
|
3
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
|
|
4
5
|
import { Form } from '@/components/ui/form.js';
|
|
5
6
|
import { useCustomFieldConfig } from '@/hooks/use-custom-field-config.js';
|
|
6
7
|
import { usePage } from '@/hooks/use-page.js';
|
|
7
8
|
import { cn } from '@/lib/utils.js';
|
|
8
|
-
import { NavigationConfirmation } from '@/components/shared/navigation-confirmation.js';
|
|
9
9
|
import { useMediaQuery } from '@uidotdev/usehooks';
|
|
10
10
|
import React, { ComponentProps } from 'react';
|
|
11
11
|
import { Control, UseFormReturn } from 'react-hook-form';
|
|
12
12
|
|
|
13
13
|
import { DashboardActionBarItem } from '../extension-api/extension-api-types.js';
|
|
14
14
|
|
|
15
|
+
import { PageBlockContext } from '@/framework/layout-engine/page-block-provider.js';
|
|
16
|
+
import { PageContext, PageContextValue } from '@/framework/layout-engine/page-provider.js';
|
|
15
17
|
import { getDashboardActionBarItems, getDashboardPageBlocks } from './layout-extensions.js';
|
|
16
18
|
import { LocationWrapper } from './location-wrapper.js';
|
|
17
|
-
import { PageContext, PageContextValue } from '@/framework/layout-engine/page-provider.js';
|
|
18
19
|
|
|
19
20
|
export interface PageProps extends ComponentProps<'div'> {
|
|
20
21
|
pageId?: string;
|
|
@@ -45,9 +46,7 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
|
|
|
45
46
|
const childArray = React.Children.toArray(children);
|
|
46
47
|
|
|
47
48
|
const pageTitle = childArray.find(child => React.isValidElement(child) && child.type === PageTitle);
|
|
48
|
-
const pageActionBar = childArray.find(
|
|
49
|
-
child => isOfType(child, PageActionBar),
|
|
50
|
-
);
|
|
49
|
+
const pageActionBar = childArray.find(child => isOfType(child, PageActionBar));
|
|
51
50
|
|
|
52
51
|
const pageContent = childArray.filter(
|
|
53
52
|
child => !isOfType(child, PageTitle) && !isOfType(child, PageActionBar),
|
|
@@ -73,7 +72,13 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
|
|
|
73
72
|
);
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
function PageContent({
|
|
75
|
+
function PageContent({
|
|
76
|
+
pageHeader,
|
|
77
|
+
pageContent,
|
|
78
|
+
form,
|
|
79
|
+
submitHandler,
|
|
80
|
+
...props
|
|
81
|
+
}: {
|
|
77
82
|
pageHeader: React.ReactNode;
|
|
78
83
|
pageContent: React.ReactNode;
|
|
79
84
|
form?: UseFormReturn<any>;
|
|
@@ -94,9 +99,14 @@ function PageContent({ pageHeader, pageContent, form, submitHandler, ...props }:
|
|
|
94
99
|
);
|
|
95
100
|
}
|
|
96
101
|
|
|
97
|
-
export function PageContentWithOptionalForm({
|
|
102
|
+
export function PageContentWithOptionalForm({
|
|
103
|
+
form,
|
|
104
|
+
pageHeader,
|
|
105
|
+
pageContent,
|
|
106
|
+
submitHandler,
|
|
107
|
+
}: {
|
|
98
108
|
form?: UseFormReturn<any>;
|
|
99
|
-
pageHeader: React.ReactNode
|
|
109
|
+
pageHeader: React.ReactNode;
|
|
100
110
|
pageContent: React.ReactNode;
|
|
101
111
|
submitHandler?: any;
|
|
102
112
|
}) {
|
|
@@ -261,12 +271,8 @@ export function PageTitle({ children }: { children: React.ReactNode }) {
|
|
|
261
271
|
export function PageActionBar({ children }: { children: React.ReactNode }) {
|
|
262
272
|
let childArray = React.Children.toArray(children);
|
|
263
273
|
|
|
264
|
-
const leftContent = childArray.filter(
|
|
265
|
-
|
|
266
|
-
);
|
|
267
|
-
const rightContent = childArray.filter(
|
|
268
|
-
child => isOfType(child, PageActionBarRight),
|
|
269
|
-
);
|
|
274
|
+
const leftContent = childArray.filter(child => isOfType(child, PageActionBarLeft));
|
|
275
|
+
const rightContent = childArray.filter(child => isOfType(child, PageActionBarRight));
|
|
270
276
|
|
|
271
277
|
return (
|
|
272
278
|
<div className={cn('flex gap-2', leftContent.length > 0 ? 'justify-between' : 'justify-end')}>
|
|
@@ -348,18 +354,20 @@ export type PageBlockProps = {
|
|
|
348
354
|
* @docsWeight 0
|
|
349
355
|
* @since 3.3.0
|
|
350
356
|
*/
|
|
351
|
-
export function PageBlock({ children, title, description, className, blockId }: PageBlockProps) {
|
|
357
|
+
export function PageBlock({ children, title, description, className, blockId, column }: PageBlockProps) {
|
|
352
358
|
return (
|
|
353
359
|
<LocationWrapper blockId={blockId}>
|
|
354
|
-
<
|
|
355
|
-
{
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
360
|
+
<PageBlockContext.Provider value={{ blockId, title, description, column }}>
|
|
361
|
+
<Card className={cn('w-full', className)}>
|
|
362
|
+
{title || description ? (
|
|
363
|
+
<CardHeader>
|
|
364
|
+
{title && <CardTitle>{title}</CardTitle>}
|
|
365
|
+
{description && <CardDescription>{description}</CardDescription>}
|
|
366
|
+
</CardHeader>
|
|
367
|
+
) : null}
|
|
368
|
+
<CardContent className={cn(!title ? 'pt-6' : '')}>{children}</CardContent>
|
|
369
|
+
</Card>
|
|
370
|
+
</PageBlockContext.Provider>
|
|
363
371
|
</LocationWrapper>
|
|
364
372
|
);
|
|
365
373
|
}
|
|
@@ -376,13 +384,15 @@ export function PageBlock({ children, title, description, className, blockId }:
|
|
|
376
384
|
* @since 3.3.0
|
|
377
385
|
*/
|
|
378
386
|
export function FullWidthPageBlock({
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
387
|
+
children,
|
|
388
|
+
className,
|
|
389
|
+
blockId,
|
|
390
|
+
}: Pick<PageBlockProps, 'children' | 'className' | 'blockId'>) {
|
|
383
391
|
return (
|
|
384
392
|
<LocationWrapper blockId={blockId}>
|
|
385
|
-
<
|
|
393
|
+
<PageBlockContext.Provider value={{ blockId, column: 'main' }}>
|
|
394
|
+
<div className={cn('w-full', className)}>{children}</div>
|
|
395
|
+
</PageBlockContext.Provider>
|
|
386
396
|
</LocationWrapper>
|
|
387
397
|
);
|
|
388
398
|
}
|
|
@@ -398,10 +408,10 @@ export function FullWidthPageBlock({
|
|
|
398
408
|
* @since 3.3.0
|
|
399
409
|
*/
|
|
400
410
|
export function CustomFieldsPageBlock({
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
411
|
+
column,
|
|
412
|
+
entityType,
|
|
413
|
+
control,
|
|
414
|
+
}: {
|
|
405
415
|
column: 'main' | 'side';
|
|
406
416
|
entityType: string;
|
|
407
417
|
control: Control<any, any>;
|
|
@@ -3,12 +3,13 @@ import {
|
|
|
3
3
|
CustomFieldKeysOfItem,
|
|
4
4
|
CustomizeColumnConfig,
|
|
5
5
|
FacetedFilterConfig,
|
|
6
|
+
ListQueryFields,
|
|
6
7
|
ListQueryOptionsShape,
|
|
7
8
|
ListQueryShape,
|
|
8
|
-
ListQueryFields,
|
|
9
9
|
PaginatedListDataTable,
|
|
10
10
|
RowAction,
|
|
11
11
|
} from '@/components/shared/paginated-list-data-table.js';
|
|
12
|
+
import { BulkAction } from '@/framework/data-table/data-table-types.js';
|
|
12
13
|
import { useUserSettings } from '@/hooks/use-user-settings.js';
|
|
13
14
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
14
15
|
import { AnyRoute, AnyRouter, useNavigate } from '@tanstack/react-router';
|
|
@@ -16,13 +17,7 @@ import { ColumnFiltersState, SortingState, Table } from '@tanstack/react-table';
|
|
|
16
17
|
import { TableOptions } from '@tanstack/table-core';
|
|
17
18
|
|
|
18
19
|
import { addCustomFields } from '../document-introspection/add-custom-fields.js';
|
|
19
|
-
import {
|
|
20
|
-
FullWidthPageBlock,
|
|
21
|
-
Page,
|
|
22
|
-
PageActionBar,
|
|
23
|
-
PageLayout,
|
|
24
|
-
PageTitle,
|
|
25
|
-
} from '../layout-engine/page-layout.js';
|
|
20
|
+
import { FullWidthPageBlock, Page, PageActionBar, PageLayout, PageTitle } from '../layout-engine/page-layout.js';
|
|
26
21
|
|
|
27
22
|
/**
|
|
28
23
|
* @description
|
|
@@ -57,6 +52,7 @@ export interface ListPageProps<
|
|
|
57
52
|
rowActions?: RowAction<ListQueryFields<T>>[];
|
|
58
53
|
transformData?: (data: any[]) => any[];
|
|
59
54
|
setTableOptions?: (table: TableOptions<any>) => TableOptions<any>;
|
|
55
|
+
bulkActions?: BulkAction[];
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
/**
|
|
@@ -93,6 +89,7 @@ export function ListPage<
|
|
|
93
89
|
rowActions,
|
|
94
90
|
transformData,
|
|
95
91
|
setTableOptions,
|
|
92
|
+
bulkActions,
|
|
96
93
|
}: ListPageProps<T, U, V, AC>) {
|
|
97
94
|
const route = typeof routeOrFn === 'function' ? routeOrFn() : routeOrFn;
|
|
98
95
|
const routeSearch = route.useSearch();
|
|
@@ -191,6 +188,7 @@ export function ListPage<
|
|
|
191
188
|
}}
|
|
192
189
|
facetedFilters={facetedFilters}
|
|
193
190
|
rowActions={rowActions}
|
|
191
|
+
bulkActions={bulkActions}
|
|
194
192
|
setTableOptions={setTableOptions}
|
|
195
193
|
transformData={transformData}
|
|
196
194
|
/>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
1
3
|
import { DashboardAlertDefinition } from '../alert/types.js';
|
|
2
4
|
import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
|
|
5
|
+
import { BulkAction } from '../data-table/data-table-types.js';
|
|
3
6
|
import {
|
|
4
7
|
DashboardActionBarItem,
|
|
5
8
|
DashboardPageBlockDefinition,
|
|
@@ -16,6 +19,5 @@ export interface GlobalRegistryContents {
|
|
|
16
19
|
dashboardWidgetRegistry: Map<string, DashboardWidgetDefinition>;
|
|
17
20
|
dashboardAlertRegistry: Map<string, DashboardAlertDefinition>;
|
|
18
21
|
customFormComponents: Map<string, React.FunctionComponent<CustomFormComponentInputProps>>;
|
|
22
|
+
bulkActionsRegistry: Map<string, BulkAction[]>;
|
|
19
23
|
}
|
|
20
|
-
|
|
21
|
-
export type GlobalRegistryKey = keyof GlobalRegistryContents;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PageBlockContext } from '@/framework/layout-engine/page-block-provider.js';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
export function usePageBlock() {
|
|
5
|
+
const pageBlock = useContext(PageBlockContext);
|
|
6
|
+
if (!pageBlock) {
|
|
7
|
+
throw new Error('PageBlockProvider not found');
|
|
8
|
+
}
|
|
9
|
+
return pageBlock;
|
|
10
|
+
}
|
package/src/lib/index.ts
CHANGED
|
@@ -11,12 +11,13 @@ export * from './components/data-input/facet-value-input.js';
|
|
|
11
11
|
export * from './components/data-input/money-input.js';
|
|
12
12
|
export * from './components/data-input/richt-text-input.js';
|
|
13
13
|
export * from './components/data-table/add-filter-menu.js';
|
|
14
|
+
export * from './components/data-table/data-table-bulk-action-item.js';
|
|
15
|
+
export * from './components/data-table/data-table-bulk-actions.js';
|
|
14
16
|
export * from './components/data-table/data-table-column-header.js';
|
|
15
17
|
export * from './components/data-table/data-table-faceted-filter.js';
|
|
16
18
|
export * from './components/data-table/data-table-filter-badge.js';
|
|
17
19
|
export * from './components/data-table/data-table-filter-dialog.js';
|
|
18
20
|
export * from './components/data-table/data-table-pagination.js';
|
|
19
|
-
export * from './components/data-table/data-table-types.js';
|
|
20
21
|
export * from './components/data-table/data-table-view-options.js';
|
|
21
22
|
export * from './components/data-table/data-table.js';
|
|
22
23
|
export * from './components/data-table/filters/data-table-boolean-filter.js';
|
|
@@ -26,6 +27,7 @@ export * from './components/data-table/filters/data-table-number-filter.js';
|
|
|
26
27
|
export * from './components/data-table/filters/data-table-string-filter.js';
|
|
27
28
|
export * from './components/data-table/human-readable-operator.js';
|
|
28
29
|
export * from './components/data-table/refresh-button.js';
|
|
30
|
+
export * from './components/data-table/types.js';
|
|
29
31
|
export * from './components/layout/app-layout.js';
|
|
30
32
|
export * from './components/layout/app-sidebar.js';
|
|
31
33
|
export * from './components/layout/channel-switcher.js';
|
|
@@ -137,6 +139,8 @@ export * from './framework/dashboard-widget/orders-summary/index.js';
|
|
|
137
139
|
export * from './framework/dashboard-widget/orders-summary/order-summary-widget.graphql.js';
|
|
138
140
|
export * from './framework/dashboard-widget/types.js';
|
|
139
141
|
export * from './framework/dashboard-widget/widget-extensions.js';
|
|
142
|
+
export * from './framework/data-table/data-table-extensions.js';
|
|
143
|
+
export * from './framework/data-table/data-table-types.js';
|
|
140
144
|
export * from './framework/defaults.js';
|
|
141
145
|
export * from './framework/document-introspection/add-custom-fields.js';
|
|
142
146
|
export * from './framework/document-introspection/get-document-structure.js';
|
|
@@ -150,6 +154,7 @@ export * from './framework/form-engine/form-schema-tools.js';
|
|
|
150
154
|
export * from './framework/form-engine/use-generated-form.js';
|
|
151
155
|
export * from './framework/layout-engine/layout-extensions.js';
|
|
152
156
|
export * from './framework/layout-engine/location-wrapper.js';
|
|
157
|
+
export * from './framework/layout-engine/page-block-provider.js';
|
|
153
158
|
export * from './framework/layout-engine/page-layout.js';
|
|
154
159
|
export * from './framework/layout-engine/page-provider.js';
|
|
155
160
|
export * from './framework/nav-menu/nav-menu-extensions.js';
|
|
@@ -164,12 +169,14 @@ export * from './framework/registry/global-registry.js';
|
|
|
164
169
|
export * from './framework/registry/registry-types.js';
|
|
165
170
|
export * from './graphql/api.js';
|
|
166
171
|
export * from './graphql/fragments.js';
|
|
172
|
+
export * from './graphql/graphql.js';
|
|
167
173
|
export * from './hooks/use-auth.js';
|
|
168
174
|
export * from './hooks/use-channel.js';
|
|
169
175
|
export * from './hooks/use-custom-field-config.js';
|
|
170
176
|
export * from './hooks/use-grouped-permissions.js';
|
|
171
177
|
export * from './hooks/use-local-format.js';
|
|
172
178
|
export * from './hooks/use-mobile.js';
|
|
179
|
+
export * from './hooks/use-page-block.js';
|
|
173
180
|
export * from './hooks/use-page.js';
|
|
174
181
|
export * from './hooks/use-permissions.js';
|
|
175
182
|
export * from './hooks/use-server-config.js';
|
|
@@ -4,14 +4,18 @@ import { describe, expect, it } from 'vitest';
|
|
|
4
4
|
import { loadVendureConfig } from '../utils/config-loader.js';
|
|
5
5
|
|
|
6
6
|
describe('detecting plugins in barrel exports', () => {
|
|
7
|
-
it(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
it(
|
|
8
|
+
'should detect plugins in barrel exports',
|
|
9
|
+
async () => {
|
|
10
|
+
const result = await loadVendureConfig({
|
|
11
|
+
tempDir: join(__dirname, './__temp'),
|
|
12
|
+
vendureConfigPath: join(__dirname, 'barrel-exports', 'vendure-config.ts'),
|
|
13
|
+
});
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
expect(result.pluginInfo).toHaveLength(1);
|
|
16
|
+
expect(result.pluginInfo[0].name).toBe('MyPlugin');
|
|
17
|
+
expect(result.pluginInfo[0].dashboardEntryPath).toBe('./dashboard/index.tsx');
|
|
18
|
+
},
|
|
19
|
+
{ timeout: 10_000 },
|
|
20
|
+
);
|
|
17
21
|
});
|
|
@@ -63,6 +63,7 @@ export function viteConfigPlugin({ packageRoot }: { packageRoot: string }): Plug
|
|
|
63
63
|
...(config.optimizeDeps?.include || []),
|
|
64
64
|
'@/components > recharts',
|
|
65
65
|
'@/components > react-dropzone',
|
|
66
|
+
'@vendure/common/lib/generated-types',
|
|
66
67
|
],
|
|
67
68
|
};
|
|
68
69
|
return config;
|
|
@@ -12,7 +12,7 @@ const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
|
12
12
|
* generates an import statement for each one, wrapped up in a `runDashboardExtensions()`
|
|
13
13
|
* function which can then be imported and executed in the Dashboard app.
|
|
14
14
|
*/
|
|
15
|
-
export function dashboardMetadataPlugin(
|
|
15
|
+
export function dashboardMetadataPlugin(): Plugin {
|
|
16
16
|
let configLoaderApi: ConfigLoaderApi;
|
|
17
17
|
let loadVendureConfigResult: LoadVendureConfigResult;
|
|
18
18
|
return {
|
|
@@ -52,11 +52,3 @@ export function dashboardMetadataPlugin(options: { rootDir: string }): Plugin {
|
|
|
52
52
|
},
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Converts an import path to a normalized path relative to the rootDir.
|
|
58
|
-
*/
|
|
59
|
-
function normalizeImportPath(rootDir: string, importPath: string): string {
|
|
60
|
-
const relativePath = path.relative(rootDir, importPath).replace(/\\/g, '/');
|
|
61
|
-
return relativePath.replace(/\.tsx?$/, '.js');
|
|
62
|
-
}
|