@vendure/dashboard 3.3.6-master-202507020234 → 3.3.6-master-202507020959
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/package.json +4 -4
- package/src/lib/framework/component-registry/component-registry.tsx +31 -47
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +29 -95
- package/src/lib/framework/extension-api/display-component-extensions.tsx +69 -0
- package/src/lib/framework/extension-api/extension-api-types.ts +18 -160
- package/src/lib/framework/extension-api/input-component-extensions.tsx +69 -0
- package/src/lib/framework/extension-api/logic/alerts.ts +10 -0
- package/src/lib/framework/extension-api/logic/data-table.ts +60 -0
- package/src/lib/framework/extension-api/logic/detail-forms.ts +48 -0
- package/src/lib/framework/extension-api/logic/form-components.ts +13 -0
- package/src/lib/framework/extension-api/logic/index.ts +8 -0
- package/src/lib/framework/extension-api/logic/layout.ts +22 -0
- package/src/lib/framework/extension-api/logic/navigation.ts +37 -0
- package/src/lib/framework/extension-api/logic/widgets.ts +10 -0
- package/src/lib/framework/extension-api/types/alerts.ts +54 -0
- package/src/lib/framework/extension-api/types/data-table.ts +64 -0
- package/src/lib/framework/extension-api/types/detail-forms.ts +81 -0
- package/src/lib/framework/extension-api/types/form-components.ts +32 -0
- package/src/lib/framework/extension-api/types/index.ts +8 -0
- package/src/lib/framework/extension-api/types/layout.ts +78 -0
- package/src/lib/framework/extension-api/types/navigation.ts +19 -0
- package/src/lib/framework/extension-api/types/widgets.ts +94 -0
- package/src/lib/framework/page/detail-page.tsx +48 -3
- package/src/lib/framework/registry/registry-types.ts +3 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vendure/dashboard",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.3.6-master-
|
|
4
|
+
"version": "3.3.6-master-202507020959",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"@types/react-dom": "^19.0.4",
|
|
87
87
|
"@types/react-grid-layout": "^1.3.5",
|
|
88
88
|
"@uidotdev/usehooks": "^2.4.1",
|
|
89
|
-
"@vendure/common": "^3.3.6-master-
|
|
90
|
-
"@vendure/core": "^3.3.6-master-
|
|
89
|
+
"@vendure/common": "^3.3.6-master-202507020959",
|
|
90
|
+
"@vendure/core": "^3.3.6-master-202507020959",
|
|
91
91
|
"@vitejs/plugin-react": "^4.3.4",
|
|
92
92
|
"awesome-graphql-client": "^2.1.0",
|
|
93
93
|
"class-variance-authority": "^0.7.1",
|
|
@@ -130,5 +130,5 @@
|
|
|
130
130
|
"lightningcss-linux-arm64-musl": "^1.29.3",
|
|
131
131
|
"lightningcss-linux-x64-musl": "^1.29.1"
|
|
132
132
|
},
|
|
133
|
-
"gitHead": "
|
|
133
|
+
"gitHead": "5ee07181c993fe6edee6c000fe4cd90ffcba852f"
|
|
134
134
|
}
|
|
@@ -1,70 +1,54 @@
|
|
|
1
|
-
import { BooleanDisplayBadge, BooleanDisplayCheckbox } from '@/components/data-display/boolean.js';
|
|
2
|
-
import { DateTime } from '@/components/data-display/date-time.js';
|
|
3
|
-
import { Money } from '@/components/data-display/money.js';
|
|
4
|
-
import { DateTimeInput } from '@/components/data-input/datetime-input.js';
|
|
5
|
-
import { FacetValueInput } from '@/components/data-input/facet-value-input.js';
|
|
6
|
-
import { MoneyInput } from '@/components/data-input/money-input.js';
|
|
7
|
-
import { VendureImage } from '@/components/shared/vendure-image.js';
|
|
8
|
-
import { Checkbox } from '@/components/ui/checkbox.js';
|
|
9
|
-
import { Input } from '@/components/ui/input.js';
|
|
10
1
|
import * as React from 'react';
|
|
2
|
+
import { addDisplayComponent, getDisplayComponent } from '../extension-api/display-component-extensions.js';
|
|
3
|
+
import { addInputComponent, getInputComponent } from '../extension-api/input-component-extensions.js';
|
|
11
4
|
|
|
12
5
|
export interface ComponentRegistryEntry<Props extends Record<string, any>> {
|
|
13
6
|
component: React.ComponentType<Props>;
|
|
14
7
|
}
|
|
15
8
|
|
|
16
9
|
// Basic component types
|
|
17
|
-
export type DataDisplayComponent = React.ComponentType<{ value: any; [key: string]: any }>;
|
|
18
|
-
export type DataInputComponent = React.ComponentType<{ value: any; onChange: (value: any) => void; [key: string]: any }>;
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
dataInput: Record<string, DataInputComponent>;
|
|
11
|
+
export interface DataDisplayComponentProps {
|
|
12
|
+
value: any;
|
|
13
|
+
[key: string]: any;
|
|
24
14
|
}
|
|
25
15
|
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
dataInput: {
|
|
35
|
-
'vendure:moneyInput': MoneyInput,
|
|
36
|
-
'vendure:textInput': (props) => <Input {...props} onChange={e => props.onChange(e.target.value)} />,
|
|
37
|
-
'vendure:numberInput': (props) => <Input {...props} onChange={e => props.onChange(e.target.value)} type="number" />,
|
|
38
|
-
'vendure:dateTimeInput': DateTimeInput,
|
|
39
|
-
'vendure:checkboxInput': (props) => <Checkbox {...props} checked={props.value === 'true' || props.value === true} onCheckedChange={value => props.onChange(value)} />,
|
|
40
|
-
'vendure:facetValueInput': FacetValueInput,
|
|
41
|
-
}
|
|
42
|
-
};
|
|
16
|
+
export interface DataInputComponentProps {
|
|
17
|
+
value: any;
|
|
18
|
+
onChange: (value: any) => void;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type DataDisplayComponent = React.ComponentType<DataDisplayComponentProps>;
|
|
23
|
+
export type DataInputComponent = React.ComponentType<DataInputComponentProps>;
|
|
43
24
|
|
|
44
|
-
//
|
|
25
|
+
// Component registry hook that uses the global registry
|
|
45
26
|
export function useComponentRegistry() {
|
|
46
27
|
return {
|
|
47
28
|
getDisplayComponent: (id: string): DataDisplayComponent | undefined => {
|
|
48
|
-
|
|
49
|
-
return COMPONENT_REGISTRY.dataDisplay[id];
|
|
29
|
+
return getDisplayComponent(id);
|
|
50
30
|
},
|
|
51
31
|
getInputComponent: (id: string): DataInputComponent | undefined => {
|
|
52
|
-
|
|
53
|
-
return COMPONENT_REGISTRY.dataInput[id];
|
|
32
|
+
return getInputComponent(id);
|
|
54
33
|
},
|
|
55
34
|
};
|
|
56
35
|
}
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
// Legacy registration functions - these now delegate to the global registry
|
|
38
|
+
export function registerInputComponent(
|
|
39
|
+
pageId: string,
|
|
40
|
+
blockId: string,
|
|
41
|
+
field: string,
|
|
42
|
+
component: DataInputComponent,
|
|
43
|
+
) {
|
|
44
|
+
addInputComponent({ pageId, blockId, field, component });
|
|
63
45
|
}
|
|
64
46
|
|
|
65
|
-
export function registerDisplayComponent(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
47
|
+
export function registerDisplayComponent(
|
|
48
|
+
pageId: string,
|
|
49
|
+
blockId: string,
|
|
50
|
+
field: string,
|
|
51
|
+
component: DataDisplayComponent,
|
|
52
|
+
) {
|
|
53
|
+
addDisplayComponent({ pageId, blockId, field, component });
|
|
70
54
|
}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import { addBulkAction, addListQueryDocument } from '@/framework/data-table/data-table-extensions.js';
|
|
2
|
-
import { parse } from 'graphql';
|
|
3
|
-
|
|
4
|
-
import { registerDashboardWidget } from '../dashboard-widget/widget-extensions.js';
|
|
5
|
-
import {
|
|
6
|
-
addCustomFormComponent,
|
|
7
|
-
addDetailQueryDocument,
|
|
8
|
-
} from '../form-engine/custom-form-component-extensions.js';
|
|
9
|
-
import {
|
|
10
|
-
registerDashboardActionBarItem,
|
|
11
|
-
registerDashboardPageBlock,
|
|
12
|
-
} from '../layout-engine/layout-extensions.js';
|
|
13
|
-
import { addNavMenuItem, addNavMenuSection, NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
14
|
-
import { registerRoute } from '../page/page-api.js';
|
|
15
1
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
16
2
|
|
|
17
3
|
import { DashboardExtension } from './extension-api-types.js';
|
|
4
|
+
import {
|
|
5
|
+
registerAlertExtensions,
|
|
6
|
+
registerDataTableExtensions,
|
|
7
|
+
registerDetailFormExtensions,
|
|
8
|
+
registerFormComponentExtensions,
|
|
9
|
+
registerLayoutExtensions,
|
|
10
|
+
registerNavigationExtensions,
|
|
11
|
+
registerWidgetExtensions,
|
|
12
|
+
} from './logic/index.js';
|
|
18
13
|
|
|
19
14
|
globalRegistry.register('extensionSourceChangeCallbacks', new Set<() => void>());
|
|
20
15
|
globalRegistry.register('registerDashboardExtensionCallbacks', new Set<() => void>());
|
|
@@ -41,89 +36,28 @@ export function executeDashboardExtensionCallbacks() {
|
|
|
41
36
|
*/
|
|
42
37
|
export function defineDashboardExtension(extension: DashboardExtension) {
|
|
43
38
|
globalRegistry.get('registerDashboardExtensionCallbacks').add(() => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
addNavMenuSection({
|
|
47
|
-
...section,
|
|
48
|
-
placement: 'top',
|
|
49
|
-
order: section.order ?? 999,
|
|
50
|
-
items: [],
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (extension.routes) {
|
|
55
|
-
for (const route of extension.routes) {
|
|
56
|
-
if (route.navMenuItem) {
|
|
57
|
-
// Add the nav menu item
|
|
58
|
-
const item: NavMenuItem = {
|
|
59
|
-
url: route.navMenuItem.url ?? route.path,
|
|
60
|
-
id: route.navMenuItem.id ?? route.path,
|
|
61
|
-
title: route.navMenuItem.title ?? route.path,
|
|
62
|
-
};
|
|
63
|
-
addNavMenuItem(item, route.navMenuItem.sectionId);
|
|
64
|
-
}
|
|
65
|
-
if (route.path) {
|
|
66
|
-
// Configure a list page
|
|
67
|
-
registerRoute(route);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (extension.actionBarItems) {
|
|
72
|
-
for (const item of extension.actionBarItems) {
|
|
73
|
-
registerDashboardActionBarItem(item);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (extension.pageBlocks) {
|
|
77
|
-
for (const block of extension.pageBlocks) {
|
|
78
|
-
registerDashboardPageBlock(block);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (extension.widgets) {
|
|
82
|
-
for (const widget of extension.widgets) {
|
|
83
|
-
registerDashboardWidget(widget);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (extension.customFormComponents) {
|
|
87
|
-
for (const component of extension.customFormComponents) {
|
|
88
|
-
addCustomFormComponent(component);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (extension.dataTables) {
|
|
92
|
-
for (const dataTable of extension.dataTables) {
|
|
93
|
-
if (dataTable.bulkActions?.length) {
|
|
94
|
-
for (const action of dataTable.bulkActions) {
|
|
95
|
-
addBulkAction(dataTable.pageId, dataTable.blockId, action);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (dataTable.extendListDocument) {
|
|
99
|
-
const document =
|
|
100
|
-
typeof dataTable.extendListDocument === 'function'
|
|
101
|
-
? dataTable.extendListDocument()
|
|
102
|
-
: dataTable.extendListDocument;
|
|
39
|
+
// Register navigation extensions (nav sections and routes)
|
|
40
|
+
registerNavigationExtensions(extension.navSections, extension.routes);
|
|
103
41
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
dataTable.blockId,
|
|
107
|
-
typeof document === 'string' ? parse(document) : document,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (extension.detailForms) {
|
|
113
|
-
for (const detailForm of extension.detailForms) {
|
|
114
|
-
if (detailForm.extendDetailDocument) {
|
|
115
|
-
const document =
|
|
116
|
-
typeof detailForm.extendDetailDocument === 'function'
|
|
117
|
-
? detailForm.extendDetailDocument()
|
|
118
|
-
: detailForm.extendDetailDocument;
|
|
42
|
+
// Register layout extensions (action bar items and page blocks)
|
|
43
|
+
registerLayoutExtensions(extension.actionBarItems, extension.pageBlocks);
|
|
119
44
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
45
|
+
// Register widget extensions
|
|
46
|
+
registerWidgetExtensions(extension.widgets);
|
|
47
|
+
|
|
48
|
+
// Register form component extensions (custom form components, input components, and display components)
|
|
49
|
+
registerFormComponentExtensions(extension.customFormComponents);
|
|
50
|
+
|
|
51
|
+
// Register data table extensions
|
|
52
|
+
registerDataTableExtensions(extension.dataTables);
|
|
53
|
+
|
|
54
|
+
// Register detail form extensions
|
|
55
|
+
registerDetailFormExtensions(extension.detailForms);
|
|
56
|
+
|
|
57
|
+
// Register alert extensions
|
|
58
|
+
registerAlertExtensions(extension.alerts);
|
|
59
|
+
|
|
60
|
+
// Execute extension source change callbacks
|
|
127
61
|
const callbacks = globalRegistry.get('extensionSourceChangeCallbacks');
|
|
128
62
|
if (callbacks.size) {
|
|
129
63
|
for (const callback of callbacks) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BooleanDisplayBadge, BooleanDisplayCheckbox } from '@/components/data-display/boolean.js';
|
|
2
|
+
import { DateTime } from '@/components/data-display/date-time.js';
|
|
3
|
+
import { Money } from '@/components/data-display/money.js';
|
|
4
|
+
import { VendureImage } from '@/components/shared/vendure-image.js';
|
|
5
|
+
import { DataDisplayComponent } from '../component-registry/component-registry.js';
|
|
6
|
+
import { globalRegistry } from '../registry/global-registry.js';
|
|
7
|
+
|
|
8
|
+
globalRegistry.register('displayComponents', new Map<string, DataDisplayComponent>());
|
|
9
|
+
|
|
10
|
+
// Create component function for asset display
|
|
11
|
+
const AssetDisplay: DataDisplayComponent = ({ value }) => <VendureImage asset={value} preset="tiny" />;
|
|
12
|
+
|
|
13
|
+
// Register built-in display components
|
|
14
|
+
const displayComponents = globalRegistry.get('displayComponents');
|
|
15
|
+
displayComponents.set('vendure:booleanCheckbox', BooleanDisplayCheckbox);
|
|
16
|
+
displayComponents.set('vendure:booleanBadge', BooleanDisplayBadge);
|
|
17
|
+
displayComponents.set('vendure:dateTime', DateTime);
|
|
18
|
+
displayComponents.set('vendure:asset', AssetDisplay);
|
|
19
|
+
displayComponents.set('vendure:money', Money);
|
|
20
|
+
|
|
21
|
+
export function getDisplayComponent(id: string): DataDisplayComponent | undefined {
|
|
22
|
+
return globalRegistry.get('displayComponents').get(id);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @description
|
|
27
|
+
* Gets a display component using the targeting properties.
|
|
28
|
+
* Uses the same key pattern as registration: pageId_blockId_fieldName
|
|
29
|
+
*/
|
|
30
|
+
export function getTargetedDisplayComponent(
|
|
31
|
+
pageId: string,
|
|
32
|
+
blockId: string,
|
|
33
|
+
field: string,
|
|
34
|
+
): DataDisplayComponent | undefined {
|
|
35
|
+
const key = generateDisplayComponentKey(pageId, blockId, field);
|
|
36
|
+
return globalRegistry.get('displayComponents').get(key);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description
|
|
41
|
+
* Generates a display component key based on the targeting properties.
|
|
42
|
+
* Follows the existing pattern: pageId_blockId_fieldName
|
|
43
|
+
*/
|
|
44
|
+
export function generateDisplayComponentKey(pageId: string, blockId: string, field: string): string {
|
|
45
|
+
return `${pageId}_${blockId}_${field}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function addDisplayComponent({
|
|
49
|
+
pageId,
|
|
50
|
+
blockId,
|
|
51
|
+
field,
|
|
52
|
+
component,
|
|
53
|
+
}: {
|
|
54
|
+
pageId: string;
|
|
55
|
+
blockId: string;
|
|
56
|
+
field: string;
|
|
57
|
+
component: React.ComponentType<{ value: any; [key: string]: any }>;
|
|
58
|
+
}) {
|
|
59
|
+
const displayComponents = globalRegistry.get('displayComponents');
|
|
60
|
+
|
|
61
|
+
// Generate the key using the helper function
|
|
62
|
+
const key = generateDisplayComponentKey(pageId, blockId, field);
|
|
63
|
+
|
|
64
|
+
if (displayComponents.has(key)) {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.warn(`Display component with key "${key}" is already registered and will be overwritten.`);
|
|
67
|
+
}
|
|
68
|
+
displayComponents.set(key, component);
|
|
69
|
+
}
|
|
@@ -1,161 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { DocumentNode } from 'graphql';
|
|
4
|
-
import { LucideIcon } from 'lucide-react';
|
|
5
|
-
import type React from 'react';
|
|
1
|
+
// Import all domain-specific types
|
|
2
|
+
export * from './types/index.js';
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
export interface DashboardCustomFormComponent {
|
|
21
|
-
id: string;
|
|
22
|
-
component: React.FunctionComponent<CustomFormComponentInputProps>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface DashboardRouteDefinition {
|
|
26
|
-
component: (route: AnyRoute) => React.ReactNode;
|
|
27
|
-
path: string;
|
|
28
|
-
navMenuItem?: Partial<NavMenuItem> & { sectionId: string };
|
|
29
|
-
loader?: RouteOptions['loader'];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface ActionBarButtonState {
|
|
33
|
-
disabled: boolean;
|
|
34
|
-
visible: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface DashboardNavSectionDefinition {
|
|
38
|
-
id: string;
|
|
39
|
-
title: string;
|
|
40
|
-
icon?: LucideIcon;
|
|
41
|
-
order?: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @description
|
|
46
|
-
* **Status: Developer Preview**
|
|
47
|
-
*
|
|
48
|
-
* Allows you to define custom action bar items for any page in the dashboard.
|
|
49
|
-
*
|
|
50
|
-
* @docsCategory extensions
|
|
51
|
-
* @since 3.3.0
|
|
52
|
-
*/
|
|
53
|
-
export interface DashboardActionBarItem {
|
|
54
|
-
/**
|
|
55
|
-
* @description
|
|
56
|
-
* The ID of the page where the action bar item should be displayed.
|
|
57
|
-
*/
|
|
58
|
-
pageId: string;
|
|
59
|
-
/**
|
|
60
|
-
* @description
|
|
61
|
-
* A React component that will be rendered in the action bar.
|
|
62
|
-
*/
|
|
63
|
-
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
64
|
-
/**
|
|
65
|
-
* @description
|
|
66
|
-
* Any permissions that are required to display this action bar item.
|
|
67
|
-
*/
|
|
68
|
-
requiresPermission?: string | string[];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface DashboardActionBarDropdownMenuItem {
|
|
72
|
-
locationId: string;
|
|
73
|
-
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
74
|
-
requiresPermission?: string | string[];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | 'replace' };
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @description
|
|
81
|
-
* **Status: Developer Preview**
|
|
82
|
-
*
|
|
83
|
-
* The location of a page block in the dashboard. The location can be found by turning on
|
|
84
|
-
* "developer mode" in the dashboard user menu (bottom left corner) and then
|
|
85
|
-
* clicking the `< />` icon when hovering over a page block.
|
|
86
|
-
*
|
|
87
|
-
* @docsCategory extensions
|
|
88
|
-
* @since 3.3.0
|
|
89
|
-
*/
|
|
90
|
-
export type PageBlockLocation = {
|
|
91
|
-
pageId: string;
|
|
92
|
-
position: PageBlockPosition;
|
|
93
|
-
column: 'main' | 'side';
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @description
|
|
98
|
-
* **Status: Developer Preview**
|
|
99
|
-
*
|
|
100
|
-
* This allows you to insert a custom component into a specific location
|
|
101
|
-
* on any page in the dashboard.
|
|
102
|
-
*
|
|
103
|
-
* @docsCategory extensions
|
|
104
|
-
* @since 3.3.0
|
|
105
|
-
*/
|
|
106
|
-
export interface DashboardPageBlockDefinition {
|
|
107
|
-
id: string;
|
|
108
|
-
title?: React.ReactNode;
|
|
109
|
-
location: PageBlockLocation;
|
|
110
|
-
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
111
|
-
requiresPermission?: string | string[];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @description
|
|
116
|
-
* **Status: Developer Preview**
|
|
117
|
-
*
|
|
118
|
-
* This allows you to customize aspects of existing data tables in the dashboard.
|
|
119
|
-
*
|
|
120
|
-
* @docsCategory extensions
|
|
121
|
-
* @since 3.4.0
|
|
122
|
-
*/
|
|
123
|
-
export interface DashboardDataTableExtensionDefinition {
|
|
124
|
-
/**
|
|
125
|
-
* @description
|
|
126
|
-
* The ID of the page where the data table is located, e.g. `'product-list'`, `'order-list'`.
|
|
127
|
-
*/
|
|
128
|
-
pageId: string;
|
|
129
|
-
/**
|
|
130
|
-
* @description
|
|
131
|
-
* The ID of the data table block. Defaults to `'list-table'`, which is the default blockId
|
|
132
|
-
* for the standard list pages. However, some other pages may use a different blockId,
|
|
133
|
-
* such as `'product-variants-table'` on the `'product-detail'` page.
|
|
134
|
-
*/
|
|
135
|
-
blockId?: string;
|
|
136
|
-
/**
|
|
137
|
-
* @description
|
|
138
|
-
* An array of additional bulk actions that will be available on the data table.
|
|
139
|
-
*/
|
|
140
|
-
bulkActions?: BulkAction[];
|
|
141
|
-
/**
|
|
142
|
-
* @description
|
|
143
|
-
* Allows you to extend the list document for the data table.
|
|
144
|
-
*/
|
|
145
|
-
extendListDocument?: string | DocumentNode | (() => DocumentNode | string);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export interface DashboardDetailFormExtensionDefinition {
|
|
149
|
-
/**
|
|
150
|
-
* @description
|
|
151
|
-
* The ID of the page where the detail form is located, e.g. `'product-detail'`, `'order-detail'`.
|
|
152
|
-
*/
|
|
153
|
-
pageId: string;
|
|
154
|
-
/**
|
|
155
|
-
* @description
|
|
156
|
-
*/
|
|
157
|
-
extendDetailDocument?: string | DocumentNode | (() => DocumentNode | string);
|
|
158
|
-
}
|
|
4
|
+
// Import types for the main interface
|
|
5
|
+
import {
|
|
6
|
+
DashboardActionBarItem,
|
|
7
|
+
DashboardAlertDefinition,
|
|
8
|
+
DashboardCustomFormComponents,
|
|
9
|
+
DashboardDataTableExtensionDefinition,
|
|
10
|
+
DashboardDetailFormExtensionDefinition,
|
|
11
|
+
DashboardNavSectionDefinition,
|
|
12
|
+
DashboardPageBlockDefinition,
|
|
13
|
+
DashboardRouteDefinition,
|
|
14
|
+
DashboardWidgetDefinition,
|
|
15
|
+
} from './types/index.js';
|
|
159
16
|
|
|
160
17
|
/**
|
|
161
18
|
* @description
|
|
@@ -189,7 +46,7 @@ export interface DashboardExtension {
|
|
|
189
46
|
actionBarItems?: DashboardActionBarItem[];
|
|
190
47
|
/**
|
|
191
48
|
* @description
|
|
192
|
-
*
|
|
49
|
+
* Allows you to define custom alerts that can be displayed in the dashboard.
|
|
193
50
|
*/
|
|
194
51
|
alerts?: DashboardAlertDefinition[];
|
|
195
52
|
/**
|
|
@@ -200,9 +57,10 @@ export interface DashboardExtension {
|
|
|
200
57
|
widgets?: DashboardWidgetDefinition[];
|
|
201
58
|
/**
|
|
202
59
|
* @description
|
|
203
|
-
*
|
|
60
|
+
* Unified registration for custom form components including custom field components,
|
|
61
|
+
* input components, and display components.
|
|
204
62
|
*/
|
|
205
|
-
customFormComponents?:
|
|
63
|
+
customFormComponents?: DashboardCustomFormComponents;
|
|
206
64
|
/**
|
|
207
65
|
* @description
|
|
208
66
|
* Allows you to customize aspects of existing data tables in the dashboard.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { DateTimeInput } from '@/components/data-input/datetime-input.js';
|
|
2
|
+
import { FacetValueInput } from '@/components/data-input/facet-value-input.js';
|
|
3
|
+
import { MoneyInput } from '@/components/data-input/money-input.js';
|
|
4
|
+
import { Checkbox } from '@/components/ui/checkbox.js';
|
|
5
|
+
import { Input } from '@/components/ui/input.js';
|
|
6
|
+
import { DataInputComponent } from '../component-registry/component-registry.js';
|
|
7
|
+
import { globalRegistry } from '../registry/global-registry.js';
|
|
8
|
+
|
|
9
|
+
globalRegistry.register('inputComponents', new Map<string, DataInputComponent>());
|
|
10
|
+
|
|
11
|
+
// Create component functions for built-in components
|
|
12
|
+
const TextInput: DataInputComponent = props => (
|
|
13
|
+
<Input {...props} onChange={e => props.onChange(e.target.value)} />
|
|
14
|
+
);
|
|
15
|
+
const NumberInput: DataInputComponent = props => (
|
|
16
|
+
<Input {...props} onChange={e => props.onChange(e.target.valueAsNumber)} type="number" />
|
|
17
|
+
);
|
|
18
|
+
const CheckboxInput: DataInputComponent = props => (
|
|
19
|
+
<Checkbox
|
|
20
|
+
{...props}
|
|
21
|
+
checked={props.value === 'true' || props.value === true}
|
|
22
|
+
onCheckedChange={value => props.onChange(value)}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Register built-in input components
|
|
27
|
+
const inputComponents = globalRegistry.get('inputComponents');
|
|
28
|
+
inputComponents.set('vendure:moneyInput', MoneyInput);
|
|
29
|
+
inputComponents.set('vendure:textInput', TextInput);
|
|
30
|
+
inputComponents.set('vendure:numberInput', NumberInput);
|
|
31
|
+
inputComponents.set('vendure:dateTimeInput', DateTimeInput);
|
|
32
|
+
inputComponents.set('vendure:checkboxInput', CheckboxInput);
|
|
33
|
+
inputComponents.set('vendure:facetValueInput', FacetValueInput);
|
|
34
|
+
|
|
35
|
+
export function getInputComponent(id: string): DataInputComponent | undefined {
|
|
36
|
+
return globalRegistry.get('inputComponents').get(id);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description
|
|
41
|
+
* Generates a component key based on the targeting properties.
|
|
42
|
+
* Follows the existing pattern: pageId_blockId_fieldName
|
|
43
|
+
*/
|
|
44
|
+
export function generateInputComponentKey(pageId: string, blockId: string, field: string): string {
|
|
45
|
+
return `${pageId}_${blockId}_${field}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function addInputComponent({
|
|
49
|
+
pageId,
|
|
50
|
+
blockId,
|
|
51
|
+
field,
|
|
52
|
+
component,
|
|
53
|
+
}: {
|
|
54
|
+
pageId: string;
|
|
55
|
+
blockId: string;
|
|
56
|
+
field: string;
|
|
57
|
+
component: React.ComponentType<{ value: any; onChange: (value: any) => void; [key: string]: any }>;
|
|
58
|
+
}) {
|
|
59
|
+
const inputComponents = globalRegistry.get('inputComponents');
|
|
60
|
+
|
|
61
|
+
// Generate the key using the helper function
|
|
62
|
+
const key = generateInputComponentKey(pageId, blockId, field);
|
|
63
|
+
|
|
64
|
+
if (inputComponents.has(key)) {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.warn(`Input component with key "${key}" is already registered and will be overwritten.`);
|
|
67
|
+
}
|
|
68
|
+
inputComponents.set(key, component);
|
|
69
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { globalRegistry } from '../../registry/global-registry.js';
|
|
2
|
+
import { DashboardAlertDefinition } from '../types/alerts.js';
|
|
3
|
+
|
|
4
|
+
export function registerAlertExtensions(alerts?: DashboardAlertDefinition[]) {
|
|
5
|
+
if (alerts) {
|
|
6
|
+
for (const alert of alerts) {
|
|
7
|
+
globalRegistry.get('dashboardAlertRegistry').set(alert.id, alert);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { parse } from 'graphql';
|
|
2
|
+
|
|
3
|
+
import { addBulkAction, addListQueryDocument } from '../../data-table/data-table-extensions.js';
|
|
4
|
+
import { addDisplayComponent } from '../display-component-extensions.js';
|
|
5
|
+
import { DashboardDataTableExtensionDefinition } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description
|
|
9
|
+
* Generates a data table display component key based on the pageId and column name.
|
|
10
|
+
* Uses the pattern: pageId_columnName
|
|
11
|
+
*/
|
|
12
|
+
export function generateDataTableDisplayComponentKey(pageId: string, column: string): string {
|
|
13
|
+
return `${pageId}_${column}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @description
|
|
18
|
+
* Adds a display component for a specific column in a data table.
|
|
19
|
+
*/
|
|
20
|
+
export function addDataTableDisplayComponent(
|
|
21
|
+
pageId: string,
|
|
22
|
+
column: string,
|
|
23
|
+
component: React.ComponentType<{ value: any; [key: string]: any }>,
|
|
24
|
+
) {
|
|
25
|
+
const key = generateDataTableDisplayComponentKey(pageId, column);
|
|
26
|
+
addDisplayComponent({ pageId, blockId: 'list-table', field: column, component });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function registerDataTableExtensions(dataTables?: DashboardDataTableExtensionDefinition[]) {
|
|
30
|
+
if (dataTables) {
|
|
31
|
+
for (const dataTable of dataTables) {
|
|
32
|
+
if (dataTable.bulkActions?.length) {
|
|
33
|
+
for (const action of dataTable.bulkActions) {
|
|
34
|
+
addBulkAction(dataTable.pageId, dataTable.blockId, action);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (dataTable.extendListDocument) {
|
|
38
|
+
const document =
|
|
39
|
+
typeof dataTable.extendListDocument === 'function'
|
|
40
|
+
? dataTable.extendListDocument()
|
|
41
|
+
: dataTable.extendListDocument;
|
|
42
|
+
|
|
43
|
+
addListQueryDocument(
|
|
44
|
+
dataTable.pageId,
|
|
45
|
+
dataTable.blockId,
|
|
46
|
+
typeof document === 'string' ? parse(document) : document,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (dataTable.displayComponents?.length) {
|
|
50
|
+
for (const displayComponent of dataTable.displayComponents) {
|
|
51
|
+
addDataTableDisplayComponent(
|
|
52
|
+
dataTable.pageId,
|
|
53
|
+
displayComponent.column,
|
|
54
|
+
displayComponent.component,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|