@vendure/dashboard 3.2.4 → 3.3.0
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/utils/ast-utils.spec.js +2 -2
- package/dist/plugin/utils/schema-generator.js +1 -1
- package/dist/plugin/utils/ui-config.js +2 -3
- package/dist/plugin/vite-plugin-config.js +4 -6
- package/package.json +13 -9
- package/src/app/app-providers.tsx +1 -1
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +0 -1
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +8 -2
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -0
- package/src/app/routes/_authenticated/_system/job-queue.tsx +7 -8
- package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +241 -0
- package/src/app/styles.css +15 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +1 -1
- package/src/lib/components/data-table/data-table.tsx +32 -26
- package/src/lib/components/data-table/refresh-button.tsx +25 -0
- package/src/lib/components/layout/nav-user.tsx +16 -11
- package/src/lib/components/layout/prerelease-popup.tsx +1 -5
- package/src/lib/components/shared/alerts.tsx +19 -1
- package/src/lib/components/shared/error-page.tsx +2 -2
- package/src/lib/components/shared/navigation-confirmation.tsx +20 -10
- package/src/lib/components/shared/paginated-list-data-table.tsx +1 -0
- package/src/lib/framework/alert/alert-extensions.tsx +31 -0
- package/src/lib/framework/alert/alert-item.tsx +47 -0
- package/src/lib/framework/alert/alerts-indicator.tsx +23 -0
- package/src/lib/framework/alert/types.ts +13 -0
- package/src/lib/framework/dashboard-widget/base-widget.tsx +1 -0
- package/src/lib/framework/defaults.ts +34 -0
- package/src/lib/framework/document-introspection/get-document-structure.ts +1 -2
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +15 -5
- package/src/lib/framework/extension-api/extension-api-types.ts +81 -12
- package/src/lib/framework/layout-engine/layout-extensions.ts +3 -3
- package/src/lib/framework/layout-engine/page-layout.tsx +192 -35
- package/src/lib/framework/layout-engine/page-provider.tsx +10 -0
- package/src/lib/framework/page/detail-page.tsx +62 -9
- package/src/lib/framework/page/list-page.tsx +19 -0
- package/src/lib/framework/page/page-api.ts +1 -1
- package/src/lib/framework/page/use-detail-page.ts +81 -0
- package/src/lib/framework/registry/registry-types.ts +6 -2
- package/src/lib/graphql/graphql-env.d.ts +25 -9
- package/src/lib/hooks/use-auth.tsx +13 -1
- package/src/lib/hooks/use-channel.ts +13 -0
- package/src/lib/hooks/use-local-format.ts +28 -1
- package/src/lib/hooks/use-page.tsx +2 -3
- package/src/lib/hooks/use-permissions.ts +13 -0
- package/src/lib/index.ts +3 -4
- package/src/lib/providers/auth.tsx +11 -1
- package/src/lib/providers/channel-provider.tsx +8 -1
- package/vite/utils/ast-utils.spec.ts +2 -2
- package/vite/utils/schema-generator.ts +4 -5
- package/vite/utils/ui-config.ts +7 -3
- package/vite/vite-plugin-admin-api-schema.ts +0 -10
- package/vite/vite-plugin-config.ts +1 -0
- package/src/lib/components/ui/avatar.tsx +0 -38
|
@@ -11,17 +11,13 @@ export function PrereleasePopup() {
|
|
|
11
11
|
description: (
|
|
12
12
|
<div className="space-y-2">
|
|
13
13
|
<p>
|
|
14
|
-
This is
|
|
14
|
+
This is a <span className="font-bold">beta</span> version of our new Vendure
|
|
15
15
|
Dashboard!
|
|
16
16
|
</p>
|
|
17
17
|
<p>
|
|
18
18
|
This release allows you to explore the new interface and functionality, but it's not
|
|
19
19
|
yet ready for production use.
|
|
20
20
|
</p>
|
|
21
|
-
<p>
|
|
22
|
-
If you find missing or broken functionality, you don't need to report it on GitHub at
|
|
23
|
-
this point - we're already working on it!
|
|
24
|
-
</p>
|
|
25
21
|
</div>
|
|
26
22
|
),
|
|
27
23
|
duration: 1000 * 60,
|
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
import { BellIcon } from 'lucide-react';
|
|
2
2
|
import { Button } from '../ui/button.js';
|
|
3
3
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog.js';
|
|
4
|
+
import { useAlerts } from '../../framework/alert/alert-extensions.js';
|
|
5
|
+
import { AlertItem } from '../../framework/alert/alert-item.js';
|
|
6
|
+
import { ScrollArea } from '../ui/scroll-area.js';
|
|
7
|
+
import { AlertsIndicator } from '../../framework/alert/alerts-indicator.js';
|
|
4
8
|
|
|
5
9
|
export function Alerts() {
|
|
10
|
+
const { alerts } = useAlerts();
|
|
11
|
+
|
|
12
|
+
if (alerts.length === 0) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
return (
|
|
7
17
|
<Dialog>
|
|
8
18
|
<DialogTrigger asChild>
|
|
9
|
-
<Button size="icon" variant="ghost">
|
|
19
|
+
<Button size="icon" variant="ghost" className="relative">
|
|
10
20
|
<BellIcon />
|
|
21
|
+
<AlertsIndicator />
|
|
11
22
|
</Button>
|
|
12
23
|
</DialogTrigger>
|
|
13
24
|
<DialogContent>
|
|
14
25
|
<DialogHeader>
|
|
15
26
|
<DialogTitle>Alerts</DialogTitle>
|
|
16
27
|
</DialogHeader>
|
|
28
|
+
<ScrollArea className="max-h-[500px]">
|
|
29
|
+
<div className="flex flex-col divide-y divide-border">
|
|
30
|
+
{alerts.map(alert => (
|
|
31
|
+
<AlertItem className="py-2" key={alert.id} alert={alert} />
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
</ScrollArea>
|
|
17
35
|
</DialogContent>
|
|
18
36
|
</Dialog>
|
|
19
37
|
);
|
|
@@ -13,12 +13,12 @@ export interface ErrorPageProps {
|
|
|
13
13
|
*/
|
|
14
14
|
export function ErrorPage({ message }: ErrorPageProps) {
|
|
15
15
|
return (
|
|
16
|
-
<Page>
|
|
16
|
+
<Page pageId='error-page'>
|
|
17
17
|
<PageTitle>
|
|
18
18
|
<Trans>Error</Trans>
|
|
19
19
|
</PageTitle>
|
|
20
20
|
<PageLayout>
|
|
21
|
-
<PageBlock column="main">
|
|
21
|
+
<PageBlock column="main" blockId='error-message'>
|
|
22
22
|
<Alert variant="destructive">
|
|
23
23
|
<AlertCircle className="h-4 w-4" />
|
|
24
24
|
<AlertTitle>Error</AlertTitle>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Trans } from
|
|
2
|
-
import { useBlocker } from
|
|
3
|
-
import { UseFormReturn } from
|
|
1
|
+
import { Trans } from '@/lib/trans.js';
|
|
2
|
+
import { useBlocker } from '@tanstack/react-router';
|
|
3
|
+
import { UseFormReturn } from 'react-hook-form';
|
|
4
4
|
|
|
5
|
-
import { Button } from
|
|
6
|
-
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from
|
|
5
|
+
import { Button } from '../ui/button.js';
|
|
6
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog.js';
|
|
7
7
|
|
|
8
8
|
export interface NavigationConfirmationProps {
|
|
9
9
|
form: UseFormReturn<any>;
|
|
@@ -12,19 +12,29 @@ export interface NavigationConfirmationProps {
|
|
|
12
12
|
/**
|
|
13
13
|
* Navigation confirmation dialog that blocks navigation when the form is dirty.
|
|
14
14
|
*/
|
|
15
|
-
export function NavigationConfirmation(props: NavigationConfirmationProps) {
|
|
15
|
+
export function NavigationConfirmation(props: Readonly<NavigationConfirmationProps>) {
|
|
16
16
|
const { proceed, reset, status } = useBlocker({
|
|
17
|
-
shouldBlockFn: () =>
|
|
17
|
+
shouldBlockFn: (args) => {
|
|
18
|
+
// When a new entity is being created, we don't want to block navigation
|
|
19
|
+
// to the newly-created entity page.
|
|
20
|
+
const isNavigatingToNewlyCreatedEntity = args.current.fullPath === args.next.fullPath
|
|
21
|
+
&& args.current.params.id === 'new' && args.next.params.id !== 'new'
|
|
22
|
+
if (isNavigatingToNewlyCreatedEntity) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return props.form.formState.isDirty;
|
|
26
|
+
},
|
|
18
27
|
withResolver: true,
|
|
19
28
|
enableBeforeUnload: true,
|
|
20
|
-
})
|
|
29
|
+
});
|
|
21
30
|
return (
|
|
22
31
|
<Dialog open={status === 'blocked'} onOpenChange={reset}>
|
|
23
32
|
<DialogContent className="sm:max-w-[425px]">
|
|
24
33
|
<DialogHeader>
|
|
25
34
|
<DialogTitle><Trans>Confirm navigation</Trans></DialogTitle>
|
|
26
35
|
<DialogDescription>
|
|
27
|
-
<Trans>Are you sure you want to navigate away from this page? Any unsaved changes will be
|
|
36
|
+
<Trans>Are you sure you want to navigate away from this page? Any unsaved changes will be
|
|
37
|
+
lost.</Trans>
|
|
28
38
|
</DialogDescription>
|
|
29
39
|
</DialogHeader>
|
|
30
40
|
<DialogFooter>
|
|
@@ -35,5 +45,5 @@ export function NavigationConfirmation(props: NavigationConfirmationProps) {
|
|
|
35
45
|
</DialogFooter>
|
|
36
46
|
</DialogContent>
|
|
37
47
|
</Dialog>
|
|
38
|
-
)
|
|
48
|
+
);
|
|
39
49
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { globalRegistry } from '../registry/global-registry.js';
|
|
4
|
+
import { DashboardAlertDefinition } from './types.js';
|
|
5
|
+
|
|
6
|
+
globalRegistry.register('dashboardAlertRegistry', new Map<string, DashboardAlertDefinition>());
|
|
7
|
+
|
|
8
|
+
export function registerAlert<TResponse>(alert: DashboardAlertDefinition<TResponse>) {
|
|
9
|
+
globalRegistry.set('dashboardAlertRegistry', map => {
|
|
10
|
+
map.set(alert.id, alert);
|
|
11
|
+
return map;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getAlertRegistry() {
|
|
16
|
+
return globalRegistry.get('dashboardAlertRegistry');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getAlert(id: string) {
|
|
20
|
+
return getAlertRegistry().get(id);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useAlerts() {
|
|
24
|
+
const [alerts, setAlerts] = useState<DashboardAlertDefinition[]>([]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
setAlerts(Array.from(getAlertRegistry().values()));
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
return { alerts };
|
|
31
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Button } from '@/components/ui/button.js';
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { ComponentProps } from 'react';
|
|
4
|
+
import { DashboardAlertDefinition } from './types.js';
|
|
5
|
+
import { cn } from '@/lib/utils.js';
|
|
6
|
+
interface AlertItemProps extends ComponentProps<'div'> {
|
|
7
|
+
alert: DashboardAlertDefinition;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function AlertItem({ alert, className, ...props }: Readonly<AlertItemProps>) {
|
|
11
|
+
const { data } = useQuery({
|
|
12
|
+
queryKey: ['alert', alert.id],
|
|
13
|
+
queryFn: () => alert.check(),
|
|
14
|
+
refetchInterval: alert.recheckInterval,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const isAlertActive = alert.shouldShow?.(data);
|
|
18
|
+
|
|
19
|
+
if (!isAlertActive) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className={cn('flex items-center justify-between gap-1', className)} {...props}>
|
|
25
|
+
<div className="flex flex-col">
|
|
26
|
+
<span className="font-semibold">
|
|
27
|
+
{typeof alert.title === 'string' ? alert.title : alert.title(data)}
|
|
28
|
+
</span>
|
|
29
|
+
<span className="text-sm text-muted-foreground">
|
|
30
|
+
{typeof alert.description === 'string' ? alert.description : alert.description?.(data)}
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex items-center gap-1">
|
|
34
|
+
{alert.actions?.map(action => (
|
|
35
|
+
<Button
|
|
36
|
+
key={action.label}
|
|
37
|
+
variant="secondary"
|
|
38
|
+
size="sm"
|
|
39
|
+
onClick={() => action.onClick(data)}
|
|
40
|
+
>
|
|
41
|
+
{action.label}
|
|
42
|
+
</Button>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useQueries } from '@tanstack/react-query';
|
|
2
|
+
import { useAlerts } from './alert-extensions.js';
|
|
3
|
+
|
|
4
|
+
export function AlertsIndicator() {
|
|
5
|
+
const { alerts } = useAlerts();
|
|
6
|
+
|
|
7
|
+
const alertsCount = useQueries({
|
|
8
|
+
queries: alerts.map(alert => ({
|
|
9
|
+
queryKey: ['alert', alert.id],
|
|
10
|
+
queryFn: () => alert.check(),
|
|
11
|
+
})),
|
|
12
|
+
combine: results => {
|
|
13
|
+
return results.filter((result, idx) => result.data && alerts[idx].shouldShow?.(result.data))
|
|
14
|
+
.length;
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="absolute -right-1 -top-1 rounded-full bg-red-500 text-xs w-4 h-4 flex items-center justify-center">
|
|
20
|
+
{alertsCount}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DashboardAlertDefinition<TResponse = any> {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string | ((data: TResponse) => string);
|
|
4
|
+
description?: string | ((data: TResponse) => string);
|
|
5
|
+
severity: 'info' | 'warning' | 'error';
|
|
6
|
+
check: () => Promise<TResponse> | TResponse;
|
|
7
|
+
recheckInterval?: number;
|
|
8
|
+
shouldShow?: (data: TResponse) => boolean;
|
|
9
|
+
actions?: Array<{
|
|
10
|
+
label: string;
|
|
11
|
+
onClick: (data: TResponse) => void;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
@@ -2,6 +2,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
|
|
2
2
|
import { cn } from '@/lib/utils.js';
|
|
3
3
|
import { Trans } from '@/lib/trans.js';
|
|
4
4
|
import { PropsWithChildren, useRef, useEffect, useState, createContext, useContext } from 'react';
|
|
5
|
+
import type React from 'react';
|
|
5
6
|
|
|
6
7
|
type WidgetDimensions = {
|
|
7
8
|
width: number;
|
|
@@ -122,6 +122,11 @@ export function registerDefaults() {
|
|
|
122
122
|
title: 'Healthchecks',
|
|
123
123
|
url: '/healthchecks',
|
|
124
124
|
},
|
|
125
|
+
{
|
|
126
|
+
id: 'scheduled-tasks',
|
|
127
|
+
title: 'Scheduled Tasks',
|
|
128
|
+
url: '/scheduled-tasks',
|
|
129
|
+
},
|
|
125
130
|
],
|
|
126
131
|
},
|
|
127
132
|
{
|
|
@@ -216,4 +221,33 @@ export function registerDefaults() {
|
|
|
216
221
|
component: OrdersSummaryWidget,
|
|
217
222
|
defaultSize: { w: 6, h: 3, x: 6, y: 0 },
|
|
218
223
|
});
|
|
224
|
+
|
|
225
|
+
// registerAlert<boolean>({
|
|
226
|
+
// id: 'test-alert',
|
|
227
|
+
// title: data => `Test Alert ${String(data)}`,
|
|
228
|
+
// description: 'This is a test alert',
|
|
229
|
+
// severity: 'info',
|
|
230
|
+
// check: () => Promise.resolve(true),
|
|
231
|
+
// actions: [
|
|
232
|
+
// {
|
|
233
|
+
// label: 'Test Action',
|
|
234
|
+
// onClick: () => console.log('Test Action'),
|
|
235
|
+
// },
|
|
236
|
+
// ],
|
|
237
|
+
// });
|
|
238
|
+
|
|
239
|
+
// registerAlert<boolean>({
|
|
240
|
+
// id: 'test-alert-2',
|
|
241
|
+
// title: 'Test Alert 2',
|
|
242
|
+
// description: 'This is a test alert 2',
|
|
243
|
+
// severity: 'info',
|
|
244
|
+
// check: () => Promise.resolve(true),
|
|
245
|
+
// shouldShow: data => data === true,
|
|
246
|
+
// actions: [
|
|
247
|
+
// {
|
|
248
|
+
// label: 'Test Action',
|
|
249
|
+
// onClick: () => console.log('Test Action'),
|
|
250
|
+
// },
|
|
251
|
+
// ],
|
|
252
|
+
// });
|
|
219
253
|
}
|
|
@@ -2,11 +2,10 @@ import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
|
2
2
|
import { VariablesOf } from 'gql.tada';
|
|
3
3
|
import {
|
|
4
4
|
DocumentNode,
|
|
5
|
-
OperationDefinitionNode,
|
|
6
5
|
FieldNode,
|
|
7
6
|
FragmentDefinitionNode,
|
|
8
7
|
FragmentSpreadNode,
|
|
9
|
-
|
|
8
|
+
OperationDefinitionNode,
|
|
10
9
|
} from 'graphql';
|
|
11
10
|
import { DefinitionNode, NamedTypeNode, SelectionSetNode, TypeNode } from 'graphql/language/ast.js';
|
|
12
11
|
import { schemaInfo } from 'virtual:admin-api-schema';
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { registerDashboardWidget } from '
|
|
2
|
-
import { DashboardExtension } from '@/framework/extension-api/extension-api-types.js';
|
|
3
|
-
import { addNavMenuItem, NavMenuItem } from '@/framework/nav-menu/nav-menu-extensions.js';
|
|
4
|
-
import { registerRoute } from '@/framework/page/page-api.js';
|
|
5
|
-
|
|
1
|
+
import { registerDashboardWidget } from '../dashboard-widget/widget-extensions.js';
|
|
6
2
|
import {
|
|
7
3
|
registerDashboardActionBarItem,
|
|
8
4
|
registerDashboardPageBlock,
|
|
9
5
|
} from '../layout-engine/layout-extensions.js';
|
|
6
|
+
import { addNavMenuItem, NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
7
|
+
import { registerRoute } from '../page/page-api.js';
|
|
10
8
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
11
9
|
|
|
10
|
+
import { DashboardExtension } from './extension-api-types.js';
|
|
11
|
+
|
|
12
12
|
globalRegistry.register('extensionSourceChangeCallbacks', new Set<() => void>());
|
|
13
13
|
globalRegistry.register('registerDashboardExtensionCallbacks', new Set<() => void>());
|
|
14
14
|
|
|
@@ -22,6 +22,16 @@ export function executeDashboardExtensionCallbacks() {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @description
|
|
27
|
+
* **Status: Developer Preview**
|
|
28
|
+
*
|
|
29
|
+
* The main entry point for extensions to the React-based dashboard.
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* @docsCategory extensions
|
|
33
|
+
* @since 3.3.0
|
|
34
|
+
*/
|
|
25
35
|
export function defineDashboardExtension(extension: DashboardExtension) {
|
|
26
36
|
globalRegistry.get('registerDashboardExtensionCallbacks').add(() => {
|
|
27
37
|
if (extension.routes) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PageContextValue } from '@/framework/layout-engine/page-provider.js';
|
|
2
2
|
import { AnyRoute, RouteOptions } from '@tanstack/react-router';
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import type React from 'react';
|
|
4
4
|
|
|
5
|
+
import { DashboardAlertDefinition } from '../alert/types.js';
|
|
5
6
|
import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
|
|
6
|
-
import {
|
|
7
|
+
import { NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
7
8
|
|
|
8
9
|
export interface DashboardRouteDefinition {
|
|
9
10
|
component: (route: AnyRoute) => React.ReactNode;
|
|
@@ -17,42 +18,110 @@ export interface ActionBarButtonState {
|
|
|
17
18
|
visible: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* **Status: Developer Preview**
|
|
24
|
+
*
|
|
25
|
+
* Allows you to define custom action bar items for any page in the dashboard.
|
|
26
|
+
*
|
|
27
|
+
* @docsCategory extensions
|
|
28
|
+
* @since 3.3.0
|
|
29
|
+
*/
|
|
20
30
|
export interface DashboardActionBarItem {
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @description
|
|
33
|
+
* The ID of the page where the action bar item should be displayed.
|
|
34
|
+
*/
|
|
35
|
+
pageId: string;
|
|
36
|
+
/**
|
|
37
|
+
* @description
|
|
38
|
+
* A React component that will be rendered in the action bar.
|
|
39
|
+
*/
|
|
40
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
41
|
+
/**
|
|
42
|
+
* @description
|
|
43
|
+
* Any permissions that are required to display this action bar item.
|
|
44
|
+
*/
|
|
23
45
|
requiresPermission?: string | string[];
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
export interface DashboardActionBarDropdownMenuItem {
|
|
27
49
|
locationId: string;
|
|
28
|
-
component: React.FunctionComponent<{ context:
|
|
50
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
29
51
|
requiresPermission?: string | string[];
|
|
30
52
|
}
|
|
31
53
|
|
|
32
54
|
export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | 'replace' };
|
|
33
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @description
|
|
58
|
+
* **Status: Developer Preview**
|
|
59
|
+
*
|
|
60
|
+
* The location of a page block in the dashboard. The location can be found by turning on
|
|
61
|
+
* "developer mode" in the dashboard user menu (bottom left corner) and then
|
|
62
|
+
* clicking the `< />` icon when hovering over a page block.
|
|
63
|
+
*
|
|
64
|
+
* @docsCategory extensions
|
|
65
|
+
* @since 3.3.0
|
|
66
|
+
*/
|
|
34
67
|
export type PageBlockLocation = {
|
|
35
68
|
pageId: string;
|
|
36
69
|
position: PageBlockPosition;
|
|
37
70
|
column: 'main' | 'side';
|
|
38
71
|
};
|
|
39
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @description
|
|
75
|
+
* **Status: Developer Preview**
|
|
76
|
+
*
|
|
77
|
+
* This allows you to insert a custom component into a specific location
|
|
78
|
+
* on any page in the dashboard.
|
|
79
|
+
*
|
|
80
|
+
* @docsCategory extensions
|
|
81
|
+
* @since 3.3.0
|
|
82
|
+
*/
|
|
40
83
|
export interface DashboardPageBlockDefinition {
|
|
41
84
|
id: string;
|
|
42
85
|
title?: React.ReactNode;
|
|
43
86
|
location: PageBlockLocation;
|
|
44
|
-
component: React.FunctionComponent<{ context:
|
|
87
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
45
88
|
requiresPermission?: string | string[];
|
|
46
89
|
}
|
|
47
90
|
|
|
48
91
|
/**
|
|
49
92
|
* @description
|
|
50
|
-
*
|
|
93
|
+
* **Status: Developer Preview**
|
|
94
|
+
*
|
|
51
95
|
* This is used to define the routes, widgets, etc. that will be displayed in the dashboard.
|
|
96
|
+
*
|
|
97
|
+
* @docsCategory extensions
|
|
98
|
+
* @since 3.3.0
|
|
52
99
|
*/
|
|
53
100
|
export interface DashboardExtension {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
/**
|
|
102
|
+
* @description
|
|
103
|
+
* Allows you to define custom routes such as list or detail views.
|
|
104
|
+
*/
|
|
105
|
+
routes?: DashboardRouteDefinition[];
|
|
106
|
+
/**
|
|
107
|
+
* @description
|
|
108
|
+
* Allows you to define custom page blocks for any page in the dashboard.
|
|
109
|
+
*/
|
|
110
|
+
pageBlocks?: DashboardPageBlockDefinition[];
|
|
111
|
+
/**
|
|
112
|
+
* @description
|
|
113
|
+
* Allows you to define custom action bar items for any page in the dashboard.
|
|
114
|
+
*/
|
|
115
|
+
actionBarItems?: DashboardActionBarItem[];
|
|
116
|
+
/**
|
|
117
|
+
* @description
|
|
118
|
+
* Not yet implemented
|
|
119
|
+
*/
|
|
120
|
+
alerts?: DashboardAlertDefinition[];
|
|
121
|
+
/**
|
|
122
|
+
* @description
|
|
123
|
+
* Allows you to define custom routes for the dashboard, which will render the
|
|
124
|
+
* given components and optionally also add a nav menu item.
|
|
125
|
+
*/
|
|
126
|
+
widgets?: DashboardWidgetDefinition[];
|
|
58
127
|
}
|
|
@@ -9,13 +9,13 @@ globalRegistry.register('dashboardPageBlockRegistry', new Map<string, DashboardP
|
|
|
9
9
|
|
|
10
10
|
export function registerDashboardActionBarItem(item: DashboardActionBarItem) {
|
|
11
11
|
globalRegistry.set('dashboardActionBarItemRegistry', map => {
|
|
12
|
-
map.set(item.
|
|
12
|
+
map.set(item.pageId, [...(map.get(item.pageId) ?? []), item]);
|
|
13
13
|
return map;
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function getDashboardActionBarItems(
|
|
18
|
-
return globalRegistry.get('dashboardActionBarItemRegistry').get(
|
|
17
|
+
export function getDashboardActionBarItems(pageId: string) {
|
|
18
|
+
return globalRegistry.get('dashboardActionBarItemRegistry').get(pageId) ?? [];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function registerDashboardPageBlock(block: DashboardPageBlockDefinition) {
|