@vendure/dashboard 3.3.6-master-202507011151 → 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/app/common/delete-bulk-action.tsx +147 -0
- package/src/app/common/duplicate-bulk-action.tsx +1 -1
- package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_administrators/administrators.tsx +7 -0
- package/src/app/routes/_authenticated/_administrators/components/administrator-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_assets/assets.graphql.ts +11 -0
- package/src/app/routes/_authenticated/_assets/assets.tsx +10 -2
- package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +45 -0
- package/src/app/routes/_authenticated/_channels/channels.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_channels/channels.tsx +7 -0
- package/src/app/routes/_authenticated/_channels/components/channel-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +39 -110
- package/src/app/routes/_authenticated/_countries/components/country-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_countries/countries.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_countries/countries.tsx +7 -0
- package/src/app/routes/_authenticated/_customer-groups/components/customer-group-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +7 -0
- package/src/app/routes/_authenticated/_customers/components/customer-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_customers/customers.graphql.ts +9 -1
- package/src/app/routes/_authenticated/_customers/customers.tsx +7 -0
- package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +104 -0
- package/src/app/routes/_authenticated/_facets/facets.graphql.ts +30 -0
- package/src/app/routes/_authenticated/_facets/facets.tsx +24 -0
- package/src/app/routes/_authenticated/_payment-methods/components/payment-method-bulk-actions.tsx +58 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +27 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +30 -8
- package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +4 -1
- package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +36 -110
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +36 -105
- package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +82 -0
- package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +25 -0
- package/src/app/routes/_authenticated/_promotions/promotions.tsx +24 -0
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +1 -1
- package/src/app/routes/_authenticated/_roles/components/role-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_roles/roles.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_roles/roles.tsx +7 -0
- package/src/app/routes/_authenticated/_sellers/components/seller-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_sellers/sellers.tsx +7 -0
- package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +1 -1
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-bulk-actions.tsx +61 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +27 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +19 -0
- package/src/app/routes/_authenticated/_stock-locations/components/stock-location-bulk-actions.tsx +58 -0
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +25 -0
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +19 -0
- package/src/app/routes/_authenticated/_tax-categories/components/tax-category-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +7 -0
- package/src/app/routes/_authenticated/_tax-rates/components/tax-rate-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +7 -0
- package/src/app/routes/_authenticated/_zones/components/zone-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_zones/zones.graphql.ts +9 -0
- package/src/app/routes/_authenticated/_zones/zones.tsx +7 -0
- package/src/lib/components/shared/asset/asset-bulk-actions.tsx +90 -0
- package/src/lib/components/shared/asset/asset-gallery.tsx +12 -7
- package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +70 -0
- package/src/{app/routes/_authenticated/_products/components → lib/components/shared}/assign-to-channel-dialog.tsx +48 -30
- package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +89 -0
- 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/src/app/routes/_authenticated/_collections/components/assign-collections-to-channel-dialog.tsx +0 -110
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { addDetailQueryDocument } from '@/framework/form-engine/custom-form-component-extensions.js';
|
|
2
|
+
import { parse } from 'graphql';
|
|
3
|
+
|
|
4
|
+
import { addDisplayComponent } from '../display-component-extensions.js';
|
|
5
|
+
import { addInputComponent } from '../input-component-extensions.js';
|
|
6
|
+
import { DashboardDetailFormExtensionDefinition } from '../types/detail-forms.js';
|
|
7
|
+
|
|
8
|
+
export function registerDetailFormExtensions(detailForms?: DashboardDetailFormExtensionDefinition[]) {
|
|
9
|
+
if (detailForms) {
|
|
10
|
+
for (const detailForm of detailForms) {
|
|
11
|
+
if (detailForm.extendDetailDocument) {
|
|
12
|
+
const document =
|
|
13
|
+
typeof detailForm.extendDetailDocument === 'function'
|
|
14
|
+
? detailForm.extendDetailDocument()
|
|
15
|
+
: detailForm.extendDetailDocument;
|
|
16
|
+
|
|
17
|
+
addDetailQueryDocument(
|
|
18
|
+
detailForm.pageId,
|
|
19
|
+
typeof document === 'string' ? parse(document) : document,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Register input components for this detail form
|
|
24
|
+
if (detailForm.inputs) {
|
|
25
|
+
for (const inputComponent of detailForm.inputs) {
|
|
26
|
+
addInputComponent({
|
|
27
|
+
pageId: detailForm.pageId,
|
|
28
|
+
blockId: inputComponent.blockId,
|
|
29
|
+
field: inputComponent.field,
|
|
30
|
+
component: inputComponent.component,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Register display components for this detail form
|
|
36
|
+
if (detailForm.displays) {
|
|
37
|
+
for (const displayComponent of detailForm.displays) {
|
|
38
|
+
addDisplayComponent({
|
|
39
|
+
pageId: detailForm.pageId,
|
|
40
|
+
blockId: displayComponent.blockId,
|
|
41
|
+
field: displayComponent.field,
|
|
42
|
+
component: displayComponent.component,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { addCustomFormComponent } from '../../form-engine/custom-form-component-extensions.js';
|
|
2
|
+
import { DashboardCustomFormComponents } from '../types/form-components.js';
|
|
3
|
+
|
|
4
|
+
export function registerFormComponentExtensions(customFormComponents?: DashboardCustomFormComponents) {
|
|
5
|
+
if (customFormComponents) {
|
|
6
|
+
// Handle custom field components
|
|
7
|
+
if (customFormComponents.customFields) {
|
|
8
|
+
for (const component of customFormComponents.customFields) {
|
|
9
|
+
addCustomFormComponent(component);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Re-export all domain-specific logic functions
|
|
2
|
+
export * from './alerts.js';
|
|
3
|
+
export * from './data-table.js';
|
|
4
|
+
export * from './detail-forms.js';
|
|
5
|
+
export * from './form-components.js';
|
|
6
|
+
export * from './layout.js';
|
|
7
|
+
export * from './navigation.js';
|
|
8
|
+
export * from './widgets.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerDashboardActionBarItem,
|
|
3
|
+
registerDashboardPageBlock,
|
|
4
|
+
} from '../../layout-engine/layout-extensions.js';
|
|
5
|
+
import { DashboardActionBarItem, DashboardPageBlockDefinition } from '../types/layout.js';
|
|
6
|
+
|
|
7
|
+
export function registerLayoutExtensions(
|
|
8
|
+
actionBarItems?: DashboardActionBarItem[],
|
|
9
|
+
pageBlocks?: DashboardPageBlockDefinition[],
|
|
10
|
+
) {
|
|
11
|
+
if (actionBarItems) {
|
|
12
|
+
for (const item of actionBarItems) {
|
|
13
|
+
registerDashboardActionBarItem(item);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (pageBlocks) {
|
|
18
|
+
for (const block of pageBlocks) {
|
|
19
|
+
registerDashboardPageBlock(block);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { addNavMenuItem, addNavMenuSection, NavMenuItem } from '../../nav-menu/nav-menu-extensions.js';
|
|
2
|
+
import { registerRoute } from '../../page/page-api.js';
|
|
3
|
+
import { DashboardNavSectionDefinition, DashboardRouteDefinition } from '../types/navigation.js';
|
|
4
|
+
|
|
5
|
+
export function registerNavigationExtensions(
|
|
6
|
+
navSections?: DashboardNavSectionDefinition[],
|
|
7
|
+
routes?: DashboardRouteDefinition[],
|
|
8
|
+
) {
|
|
9
|
+
if (navSections) {
|
|
10
|
+
for (const section of navSections) {
|
|
11
|
+
addNavMenuSection({
|
|
12
|
+
...section,
|
|
13
|
+
placement: 'top',
|
|
14
|
+
order: section.order ?? 999,
|
|
15
|
+
items: [],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (routes) {
|
|
21
|
+
for (const route of routes) {
|
|
22
|
+
if (route.navMenuItem) {
|
|
23
|
+
// Add the nav menu item
|
|
24
|
+
const item: NavMenuItem = {
|
|
25
|
+
url: route.navMenuItem.url ?? route.path,
|
|
26
|
+
id: route.navMenuItem.id ?? route.path,
|
|
27
|
+
title: route.navMenuItem.title ?? route.path,
|
|
28
|
+
};
|
|
29
|
+
addNavMenuItem(item, route.navMenuItem.sectionId);
|
|
30
|
+
}
|
|
31
|
+
if (route.path) {
|
|
32
|
+
// Configure a list page
|
|
33
|
+
registerRoute(route);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { registerDashboardWidget } from '../../dashboard-widget/widget-extensions.js';
|
|
2
|
+
import { DashboardWidgetDefinition } from '../types/index.js';
|
|
3
|
+
|
|
4
|
+
export function registerWidgetExtensions(widgets?: DashboardWidgetDefinition[]) {
|
|
5
|
+
if (widgets) {
|
|
6
|
+
for (const widget of widgets) {
|
|
7
|
+
registerDashboardWidget(widget);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description
|
|
3
|
+
* **Status: Developer Preview**
|
|
4
|
+
*
|
|
5
|
+
* Allows you to define custom alerts that can be displayed in the dashboard.
|
|
6
|
+
*
|
|
7
|
+
* @docsCategory extensions
|
|
8
|
+
* @since 3.3.0
|
|
9
|
+
*/
|
|
10
|
+
export interface DashboardAlertDefinition<TResponse = any> {
|
|
11
|
+
/**
|
|
12
|
+
* @description
|
|
13
|
+
* A unique identifier for the alert.
|
|
14
|
+
*/
|
|
15
|
+
id: string;
|
|
16
|
+
/**
|
|
17
|
+
* @description
|
|
18
|
+
* The title of the alert. Can be a string or a function that returns a string based on the response data.
|
|
19
|
+
*/
|
|
20
|
+
title: string | ((data: TResponse) => string);
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* The description of the alert. Can be a string or a function that returns a string based on the response data.
|
|
24
|
+
*/
|
|
25
|
+
description?: string | ((data: TResponse) => string);
|
|
26
|
+
/**
|
|
27
|
+
* @description
|
|
28
|
+
* The severity level of the alert.
|
|
29
|
+
*/
|
|
30
|
+
severity: 'info' | 'warning' | 'error';
|
|
31
|
+
/**
|
|
32
|
+
* @description
|
|
33
|
+
* A function that checks the condition and returns the response data.
|
|
34
|
+
*/
|
|
35
|
+
check: () => Promise<TResponse> | TResponse;
|
|
36
|
+
/**
|
|
37
|
+
* @description
|
|
38
|
+
* The interval in milliseconds to recheck the condition.
|
|
39
|
+
*/
|
|
40
|
+
recheckInterval?: number;
|
|
41
|
+
/**
|
|
42
|
+
* @description
|
|
43
|
+
* A function that determines whether the alert should be shown based on the response data.
|
|
44
|
+
*/
|
|
45
|
+
shouldShow?: (data: TResponse) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* @description
|
|
48
|
+
* Optional actions that can be performed when the alert is shown.
|
|
49
|
+
*/
|
|
50
|
+
actions?: Array<{
|
|
51
|
+
label: string;
|
|
52
|
+
onClick: (data: TResponse) => void;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
|
+
|
|
3
|
+
import { BulkAction } from '../../data-table/data-table-types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @description
|
|
7
|
+
* Allows you to define custom display components for specific columns in data tables.
|
|
8
|
+
* The pageId is already defined in the data table extension, so only the column name is needed.
|
|
9
|
+
*
|
|
10
|
+
* @docsCategory extensions
|
|
11
|
+
* @since 3.4.0
|
|
12
|
+
*/
|
|
13
|
+
export interface DashboardDataTableDisplayComponent {
|
|
14
|
+
/**
|
|
15
|
+
* @description
|
|
16
|
+
* The name of the column where this display component should be used.
|
|
17
|
+
*/
|
|
18
|
+
column: string;
|
|
19
|
+
/**
|
|
20
|
+
* @description
|
|
21
|
+
* The React component that will be rendered as the display.
|
|
22
|
+
* It should accept `value` and other standard display props.
|
|
23
|
+
*/
|
|
24
|
+
component: React.ComponentType<{ value: any; [key: string]: any }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @description
|
|
29
|
+
* **Status: Developer Preview**
|
|
30
|
+
*
|
|
31
|
+
* This allows you to customize aspects of existing data tables in the dashboard.
|
|
32
|
+
*
|
|
33
|
+
* @docsCategory extensions
|
|
34
|
+
* @since 3.4.0
|
|
35
|
+
*/
|
|
36
|
+
export interface DashboardDataTableExtensionDefinition {
|
|
37
|
+
/**
|
|
38
|
+
* @description
|
|
39
|
+
* The ID of the page where the data table is located, e.g. `'product-list'`, `'order-list'`.
|
|
40
|
+
*/
|
|
41
|
+
pageId: string;
|
|
42
|
+
/**
|
|
43
|
+
* @description
|
|
44
|
+
* The ID of the data table block. Defaults to `'list-table'`, which is the default blockId
|
|
45
|
+
* for the standard list pages. However, some other pages may use a different blockId,
|
|
46
|
+
* such as `'product-variants-table'` on the `'product-detail'` page.
|
|
47
|
+
*/
|
|
48
|
+
blockId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* @description
|
|
51
|
+
* An array of additional bulk actions that will be available on the data table.
|
|
52
|
+
*/
|
|
53
|
+
bulkActions?: BulkAction[];
|
|
54
|
+
/**
|
|
55
|
+
* @description
|
|
56
|
+
* Allows you to extend the list document for the data table.
|
|
57
|
+
*/
|
|
58
|
+
extendListDocument?: string | DocumentNode | (() => DocumentNode | string);
|
|
59
|
+
/**
|
|
60
|
+
* @description
|
|
61
|
+
* Custom display components for specific columns in the data table.
|
|
62
|
+
*/
|
|
63
|
+
displayComponents?: DashboardDataTableDisplayComponent[];
|
|
64
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataDisplayComponent,
|
|
3
|
+
DataInputComponent,
|
|
4
|
+
} from '@/framework/component-registry/component-registry.js';
|
|
5
|
+
import { DocumentNode } from 'graphql';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description
|
|
9
|
+
* Allows you to define custom input components for specific fields in detail forms.
|
|
10
|
+
* The pageId is already defined in the detail form extension, so only the blockId and field are needed.
|
|
11
|
+
*
|
|
12
|
+
* @docsCategory extensions
|
|
13
|
+
* @since 3.4.0
|
|
14
|
+
*/
|
|
15
|
+
export interface DashboardDetailFormInputComponent {
|
|
16
|
+
/**
|
|
17
|
+
* @description
|
|
18
|
+
* The ID of the block where this input component should be used.
|
|
19
|
+
*/
|
|
20
|
+
blockId: string;
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* The name of the field where this input component should be used.
|
|
24
|
+
*/
|
|
25
|
+
field: string;
|
|
26
|
+
/**
|
|
27
|
+
* @description
|
|
28
|
+
* The React component that will be rendered as the input.
|
|
29
|
+
* It should accept `value`, `onChange`, and other standard input props.
|
|
30
|
+
*/
|
|
31
|
+
component: DataInputComponent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @description
|
|
36
|
+
* Allows you to define custom display components for specific fields in detail forms.
|
|
37
|
+
* The pageId is already defined in the detail form extension, so only the blockId and field are needed.
|
|
38
|
+
*
|
|
39
|
+
* @docsCategory extensions
|
|
40
|
+
* @since 3.4.0
|
|
41
|
+
*/
|
|
42
|
+
export interface DashboardDetailFormDisplayComponent {
|
|
43
|
+
/**
|
|
44
|
+
* @description
|
|
45
|
+
* The ID of the block where this display component should be used.
|
|
46
|
+
*/
|
|
47
|
+
blockId: string;
|
|
48
|
+
/**
|
|
49
|
+
* @description
|
|
50
|
+
* The name of the field where this display component should be used.
|
|
51
|
+
*/
|
|
52
|
+
field: string;
|
|
53
|
+
/**
|
|
54
|
+
* @description
|
|
55
|
+
* The React component that will be rendered as the display.
|
|
56
|
+
* It should accept `value` and other standard display props.
|
|
57
|
+
*/
|
|
58
|
+
component: DataDisplayComponent;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface DashboardDetailFormExtensionDefinition {
|
|
62
|
+
/**
|
|
63
|
+
* @description
|
|
64
|
+
* The ID of the page where the detail form is located, e.g. `'product-detail'`, `'order-detail'`.
|
|
65
|
+
*/
|
|
66
|
+
pageId: string;
|
|
67
|
+
/**
|
|
68
|
+
* @description
|
|
69
|
+
*/
|
|
70
|
+
extendDetailDocument?: string | DocumentNode | (() => DocumentNode | string);
|
|
71
|
+
/**
|
|
72
|
+
* @description
|
|
73
|
+
* Custom input components for specific fields in the detail form.
|
|
74
|
+
*/
|
|
75
|
+
inputs?: DashboardDetailFormInputComponent[];
|
|
76
|
+
/**
|
|
77
|
+
* @description
|
|
78
|
+
* Custom display components for specific fields in the detail form.
|
|
79
|
+
*/
|
|
80
|
+
displays?: DashboardDetailFormDisplayComponent[];
|
|
81
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { CustomFormComponentInputProps } from '../../form-engine/custom-form-component.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @description
|
|
7
|
+
* Allows you to define custom form components for custom fields in the dashboard.
|
|
8
|
+
*
|
|
9
|
+
* @docsCategory extensions
|
|
10
|
+
* @since 3.4.0
|
|
11
|
+
*/
|
|
12
|
+
export interface DashboardCustomFormComponent {
|
|
13
|
+
id: string;
|
|
14
|
+
component: React.FunctionComponent<CustomFormComponentInputProps>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @description
|
|
19
|
+
* Interface for registering custom field components in the dashboard.
|
|
20
|
+
* For input and display components, use the co-located approach with detailForms.
|
|
21
|
+
*
|
|
22
|
+
* @docsCategory extensions
|
|
23
|
+
* @since 3.4.0
|
|
24
|
+
*/
|
|
25
|
+
export interface DashboardCustomFormComponents {
|
|
26
|
+
/**
|
|
27
|
+
* @description
|
|
28
|
+
* Custom form components for custom fields. These are used when rendering
|
|
29
|
+
* custom fields in forms.
|
|
30
|
+
*/
|
|
31
|
+
customFields?: DashboardCustomFormComponent[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Re-export all domain-specific types
|
|
2
|
+
export * from './alerts.js';
|
|
3
|
+
export * from './data-table.js';
|
|
4
|
+
export * from './detail-forms.js';
|
|
5
|
+
export * from './form-components.js';
|
|
6
|
+
export * from './layout.js';
|
|
7
|
+
export * from './navigation.js';
|
|
8
|
+
export * from './widgets.js';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { PageContextValue } from '../../layout-engine/page-provider.js';
|
|
4
|
+
|
|
5
|
+
export interface ActionBarButtonState {
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
visible: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
* **Status: Developer Preview**
|
|
13
|
+
*
|
|
14
|
+
* Allows you to define custom action bar items for any page in the dashboard.
|
|
15
|
+
*
|
|
16
|
+
* @docsCategory extensions
|
|
17
|
+
* @since 3.3.0
|
|
18
|
+
*/
|
|
19
|
+
export interface DashboardActionBarItem {
|
|
20
|
+
/**
|
|
21
|
+
* @description
|
|
22
|
+
* The ID of the page where the action bar item should be displayed.
|
|
23
|
+
*/
|
|
24
|
+
pageId: string;
|
|
25
|
+
/**
|
|
26
|
+
* @description
|
|
27
|
+
* A React component that will be rendered in the action bar.
|
|
28
|
+
*/
|
|
29
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
30
|
+
/**
|
|
31
|
+
* @description
|
|
32
|
+
* Any permissions that are required to display this action bar item.
|
|
33
|
+
*/
|
|
34
|
+
requiresPermission?: string | string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DashboardActionBarDropdownMenuItem {
|
|
38
|
+
locationId: string;
|
|
39
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
40
|
+
requiresPermission?: string | string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | 'replace' };
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @description
|
|
47
|
+
* **Status: Developer Preview**
|
|
48
|
+
*
|
|
49
|
+
* The location of a page block in the dashboard. The location can be found by turning on
|
|
50
|
+
* "developer mode" in the dashboard user menu (bottom left corner) and then
|
|
51
|
+
* clicking the `< />` icon when hovering over a page block.
|
|
52
|
+
*
|
|
53
|
+
* @docsCategory extensions
|
|
54
|
+
* @since 3.3.0
|
|
55
|
+
*/
|
|
56
|
+
export type PageBlockLocation = {
|
|
57
|
+
pageId: string;
|
|
58
|
+
position: PageBlockPosition;
|
|
59
|
+
column: 'main' | 'side';
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @description
|
|
64
|
+
* **Status: Developer Preview**
|
|
65
|
+
*
|
|
66
|
+
* This allows you to insert a custom component into a specific location
|
|
67
|
+
* on any page in the dashboard.
|
|
68
|
+
*
|
|
69
|
+
* @docsCategory extensions
|
|
70
|
+
* @since 3.3.0
|
|
71
|
+
*/
|
|
72
|
+
export interface DashboardPageBlockDefinition {
|
|
73
|
+
id: string;
|
|
74
|
+
title?: React.ReactNode;
|
|
75
|
+
location: PageBlockLocation;
|
|
76
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
77
|
+
requiresPermission?: string | string[];
|
|
78
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AnyRoute, RouteOptions } from '@tanstack/react-router';
|
|
2
|
+
import { LucideIcon } from 'lucide-react';
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
|
|
5
|
+
import { NavMenuItem } from '../../nav-menu/nav-menu-extensions.js';
|
|
6
|
+
|
|
7
|
+
export interface DashboardRouteDefinition {
|
|
8
|
+
component: (route: AnyRoute) => React.ReactNode;
|
|
9
|
+
path: string;
|
|
10
|
+
navMenuItem?: Partial<NavMenuItem> & { sectionId: string };
|
|
11
|
+
loader?: RouteOptions['loader'];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DashboardNavSectionDefinition {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
icon?: LucideIcon;
|
|
18
|
+
order?: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description
|
|
5
|
+
* **Status: Developer Preview**
|
|
6
|
+
*
|
|
7
|
+
* Base props interface for dashboard widgets.
|
|
8
|
+
*
|
|
9
|
+
* @docsCategory extensions
|
|
10
|
+
* @since 3.3.0
|
|
11
|
+
*/
|
|
12
|
+
export interface DashboardBaseWidgetProps {
|
|
13
|
+
widgetId: string;
|
|
14
|
+
config?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @description
|
|
19
|
+
* **Status: Developer Preview**
|
|
20
|
+
*
|
|
21
|
+
* Represents an instance of a dashboard widget with its layout and configuration.
|
|
22
|
+
*
|
|
23
|
+
* @docsCategory extensions
|
|
24
|
+
* @since 3.3.0
|
|
25
|
+
*/
|
|
26
|
+
export type DashboardWidgetInstance = {
|
|
27
|
+
/**
|
|
28
|
+
* @description
|
|
29
|
+
* A unique identifier for the widget instance.
|
|
30
|
+
*/
|
|
31
|
+
id: string;
|
|
32
|
+
/**
|
|
33
|
+
* @description
|
|
34
|
+
* The ID of the widget definition this instance is based on.
|
|
35
|
+
*/
|
|
36
|
+
widgetId: string;
|
|
37
|
+
/**
|
|
38
|
+
* @description
|
|
39
|
+
* The layout configuration for the widget.
|
|
40
|
+
*/
|
|
41
|
+
layout: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
w: number;
|
|
45
|
+
h: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* @description
|
|
49
|
+
* Optional configuration data for the widget.
|
|
50
|
+
*/
|
|
51
|
+
config?: Record<string, unknown>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @description
|
|
56
|
+
* **Status: Developer Preview**
|
|
57
|
+
*
|
|
58
|
+
* Defines a dashboard widget that can be added to the dashboard.
|
|
59
|
+
*
|
|
60
|
+
* @docsCategory extensions
|
|
61
|
+
* @since 3.3.0
|
|
62
|
+
*/
|
|
63
|
+
export type DashboardWidgetDefinition = {
|
|
64
|
+
/**
|
|
65
|
+
* @description
|
|
66
|
+
* A unique identifier for the widget.
|
|
67
|
+
*/
|
|
68
|
+
id: string;
|
|
69
|
+
/**
|
|
70
|
+
* @description
|
|
71
|
+
* The display name of the widget.
|
|
72
|
+
*/
|
|
73
|
+
name: string;
|
|
74
|
+
/**
|
|
75
|
+
* @description
|
|
76
|
+
* The React component that renders the widget.
|
|
77
|
+
*/
|
|
78
|
+
component: React.ComponentType<DashboardBaseWidgetProps>;
|
|
79
|
+
/**
|
|
80
|
+
* @description
|
|
81
|
+
* The default size and position of the widget.
|
|
82
|
+
*/
|
|
83
|
+
defaultSize: { w: number; h: number; x?: number; y?: number };
|
|
84
|
+
/**
|
|
85
|
+
* @description
|
|
86
|
+
* The minimum size constraints for the widget.
|
|
87
|
+
*/
|
|
88
|
+
minSize?: { w: number; h: number };
|
|
89
|
+
/**
|
|
90
|
+
* @description
|
|
91
|
+
* The maximum size constraints for the widget.
|
|
92
|
+
*/
|
|
93
|
+
maxSize?: { w: number; h: number };
|
|
94
|
+
};
|
|
@@ -11,11 +11,15 @@ import { AnyRoute, useNavigate } from '@tanstack/react-router';
|
|
|
11
11
|
import { ResultOf, VariablesOf } from 'gql.tada';
|
|
12
12
|
import { toast } from 'sonner';
|
|
13
13
|
import {
|
|
14
|
+
FieldInfo,
|
|
14
15
|
getEntityName,
|
|
15
16
|
getOperationVariablesFields,
|
|
16
17
|
} from '../document-introspection/get-document-structure.js';
|
|
17
18
|
|
|
18
19
|
import { TranslatableFormFieldWrapper } from '@/components/shared/translatable-form-field.js';
|
|
20
|
+
import { ControllerRenderProps, FieldPath, FieldValues } from 'react-hook-form';
|
|
21
|
+
import { useComponentRegistry } from '../component-registry/component-registry.js';
|
|
22
|
+
import { generateInputComponentKey } from '../extension-api/input-component-extensions.js';
|
|
19
23
|
import {
|
|
20
24
|
CustomFieldsPageBlock,
|
|
21
25
|
DetailFormGrid,
|
|
@@ -85,10 +89,37 @@ export interface DetailPageProps<
|
|
|
85
89
|
setValuesForUpdate: (entity: ResultOf<T>[EntityField]) => VariablesOf<U>['input'];
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
export interface DetailPageFieldProps<
|
|
93
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
94
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
95
|
+
> {
|
|
96
|
+
fieldInfo: FieldInfo;
|
|
97
|
+
field: ControllerRenderProps<TFieldValues, TName>;
|
|
98
|
+
blockId: string;
|
|
99
|
+
pageId: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
88
102
|
/**
|
|
89
103
|
* Renders form input components based on field type
|
|
90
104
|
*/
|
|
91
|
-
function
|
|
105
|
+
function FieldInputRenderer<
|
|
106
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
107
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
108
|
+
>({ fieldInfo, field, blockId, pageId }: DetailPageFieldProps<TFieldValues, TName>) {
|
|
109
|
+
const componentRegistry = useComponentRegistry();
|
|
110
|
+
const customInputComponentKey = generateInputComponentKey(pageId, blockId, fieldInfo.name);
|
|
111
|
+
|
|
112
|
+
const DisplayComponent = componentRegistry.getDisplayComponent(customInputComponentKey);
|
|
113
|
+
const InputComponent = componentRegistry.getInputComponent(customInputComponentKey);
|
|
114
|
+
|
|
115
|
+
if (DisplayComponent) {
|
|
116
|
+
return <DisplayComponent {...field} />;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (InputComponent) {
|
|
120
|
+
return <InputComponent {...field} />;
|
|
121
|
+
}
|
|
122
|
+
|
|
92
123
|
switch (fieldInfo.type) {
|
|
93
124
|
case 'Int':
|
|
94
125
|
case 'Float':
|
|
@@ -196,7 +227,14 @@ export function DetailPage<
|
|
|
196
227
|
control={form.control}
|
|
197
228
|
name={fieldInfo.name as never}
|
|
198
229
|
label={fieldInfo.name}
|
|
199
|
-
render={({ field }) =>
|
|
230
|
+
render={({ field }) => (
|
|
231
|
+
<FieldInputRenderer
|
|
232
|
+
fieldInfo={fieldInfo}
|
|
233
|
+
field={field}
|
|
234
|
+
blockId="main-form"
|
|
235
|
+
pageId={pageId}
|
|
236
|
+
/>
|
|
237
|
+
)}
|
|
200
238
|
/>
|
|
201
239
|
);
|
|
202
240
|
})}
|
|
@@ -211,7 +249,14 @@ export function DetailPage<
|
|
|
211
249
|
control={form.control}
|
|
212
250
|
name={fieldInfo.name as never}
|
|
213
251
|
label={fieldInfo.name}
|
|
214
|
-
render={({ field }) =>
|
|
252
|
+
render={({ field }) => (
|
|
253
|
+
<FieldInputRenderer
|
|
254
|
+
fieldInfo={fieldInfo}
|
|
255
|
+
field={field}
|
|
256
|
+
blockId="main-form"
|
|
257
|
+
pageId={pageId}
|
|
258
|
+
/>
|
|
259
|
+
)}
|
|
215
260
|
/>
|
|
216
261
|
);
|
|
217
262
|
})}
|
|
@@ -2,6 +2,7 @@ import { DocumentNode } from 'graphql';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
import { DashboardAlertDefinition } from '../alert/types.js';
|
|
5
|
+
import { DataDisplayComponent, DataInputComponent } from '../component-registry/component-registry.js';
|
|
5
6
|
import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
|
|
6
7
|
import { BulkAction } from '../data-table/data-table-types.js';
|
|
7
8
|
import {
|
|
@@ -20,6 +21,8 @@ export interface GlobalRegistryContents {
|
|
|
20
21
|
dashboardWidgetRegistry: Map<string, DashboardWidgetDefinition>;
|
|
21
22
|
dashboardAlertRegistry: Map<string, DashboardAlertDefinition>;
|
|
22
23
|
customFormComponents: Map<string, React.FunctionComponent<CustomFormComponentInputProps>>;
|
|
24
|
+
inputComponents: Map<string, DataInputComponent>;
|
|
25
|
+
displayComponents: Map<string, DataDisplayComponent>;
|
|
23
26
|
bulkActionsRegistry: Map<string, BulkAction[]>;
|
|
24
27
|
listQueryDocumentRegistry: Map<string, DocumentNode[]>;
|
|
25
28
|
detailQueryDocumentRegistry: Map<string, DocumentNode[]>;
|