@object-ui/plugin-dashboard 0.5.0 → 3.0.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.
@@ -1,4 +1,4 @@
1
- import { Layout as RGLLayout } from 'react-grid-layout';
1
+ import { LayoutItem as RGLLayout } from 'react-grid-layout';
2
2
  import { DashboardSchema } from '../../types/src';
3
3
  import * as React from 'react';
4
4
  export interface DashboardGridLayoutProps {
@@ -6,6 +6,8 @@ export interface DashboardGridLayoutProps {
6
6
  className?: string;
7
7
  onLayoutChange?: (layout: RGLLayout[]) => void;
8
8
  persistLayoutKey?: string;
9
+ /** Callback invoked when dashboard refresh is triggered (manual or auto) */
10
+ onRefresh?: () => void;
9
11
  }
10
12
  export declare const DashboardGridLayout: React.FC<DashboardGridLayoutProps>;
11
13
  //# sourceMappingURL=DashboardGridLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardGridLayout.d.ts","sourceRoot":"","sources":["../../src/DashboardGridLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAA6B,MAAM,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,kBAAkB,CAAC;AAY/E,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAwLlE,CAAC"}
1
+ {"version":3,"file":"DashboardGridLayout.d.ts","sourceRoot":"","sources":["../../src/DashboardGridLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAA2C,KAAK,UAAU,IAAI,SAAS,EAAuC,MAAM,mBAAmB,CAAC;AAC/I,OAAO,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,kBAAkB,CAAC;AA0B/E,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA8NlE,CAAC"}
@@ -1,7 +1,10 @@
1
1
  import { DashboardSchema } from '../../types/src';
2
- export declare const DashboardRenderer: import('react').ForwardRefExoticComponent<Omit<{
3
- [key: string]: any;
2
+ export interface DashboardRendererProps {
4
3
  schema: DashboardSchema;
5
4
  className?: string;
6
- }, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
5
+ /** Callback invoked when dashboard refresh is triggered (manual or auto) */
6
+ onRefresh?: () => void;
7
+ [key: string]: any;
8
+ }
9
+ export declare const DashboardRenderer: import('react').ForwardRefExoticComponent<Omit<DashboardRendererProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
7
10
  //# sourceMappingURL=DashboardRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardRenderer.d.ts","sourceRoot":"","sources":["../../src/DashboardRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,kBAAkB,CAAC;AAc/E,eAAO,MAAM,iBAAiB;;YAAwC,eAAe;gBAAc,MAAM;0DAkHxG,CAAC"}
1
+ {"version":3,"file":"DashboardRenderer.d.ts","sourceRoot":"","sources":["../../src/DashboardRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,kBAAkB,CAAC;AAe/E,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,gIAuK7B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { StoryObj } from '@storybook/react';
2
+ declare const meta: {
3
+ title: string;
4
+ component: import('react').ForwardRefExoticComponent<Omit<{
5
+ schema: import('../../core/src').SchemaNode;
6
+ } & Record<string, any>, "ref"> & import('react').RefAttributes<any>>;
7
+ parameters: {
8
+ layout: string;
9
+ };
10
+ tags: string[];
11
+ argTypes: {
12
+ schema: {
13
+ table: {
14
+ disable: true;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+ export declare const Default: Story;
22
+ export declare const WithMetricCards: Story;
23
+ export declare const WithChartsAndMetrics: Story;
24
+ //# sourceMappingURL=DashboardRenderer.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardRenderer.stories.d.ts","sourceRoot":"","sources":["../../src/DashboardRenderer.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIvD,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;CAUW,CAAC;AAEtB,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAInC,eAAO,MAAM,OAAO,EAAE,KAiCrB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAgD7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAkElC,CAAC"}
@@ -2,21 +2,11 @@ import { DashboardRenderer } from './DashboardRenderer';
2
2
  import { DashboardGridLayout } from './DashboardGridLayout';
3
3
  import { MetricWidget } from './MetricWidget';
4
4
  import { MetricCard } from './MetricCard';
5
- import { ReportRenderer } from './ReportRenderer';
6
- import { ReportViewer } from './ReportViewer';
7
- import { ReportBuilder } from './ReportBuilder';
8
- export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard, ReportRenderer, ReportViewer, ReportBuilder };
5
+ export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard };
9
6
  export declare const dashboardComponents: {
10
- DashboardRenderer: import('react').ForwardRefExoticComponent<Omit<{
11
- [key: string]: any;
12
- schema: import('../../types/src').DashboardSchema;
13
- className?: string;
14
- }, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
7
+ DashboardRenderer: import('react').ForwardRefExoticComponent<Omit<import('./DashboardRenderer').DashboardRendererProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
15
8
  DashboardGridLayout: import('react').FC<import('./DashboardGridLayout').DashboardGridLayoutProps>;
16
9
  MetricWidget: ({ label, value, trend, icon, className, description, ...props }: import('./MetricWidget').MetricWidgetProps) => import("react/jsx-runtime").JSX.Element;
17
10
  MetricCard: import('react').FC<import('./MetricCard').MetricCardProps>;
18
- ReportRenderer: import('react').FC<import('./ReportRenderer').ReportRendererProps>;
19
- ReportViewer: import('react').FC<import('./ReportViewer').ReportViewerProps>;
20
- ReportBuilder: import('react').FC<import('./ReportBuilder').ReportBuilderProps>;
21
11
  };
22
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AA2IzH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAQ/B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAwF5E,eAAO,MAAM,mBAAmB;;;;;CAK/B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-dashboard",
3
- "version": "0.5.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Dashboard plugin for Object UI",
@@ -15,24 +15,24 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "clsx": "^2.1.0",
18
+ "clsx": "^2.1.1",
19
19
  "lucide-react": "^0.563.0",
20
- "react": "^19.2.4",
21
- "react-dom": "^19.2.4",
22
- "react-grid-layout": "^1.4.4",
23
- "tailwind-merge": "^2.2.1",
24
- "@object-ui/components": "0.5.0",
25
- "@object-ui/core": "0.5.0",
26
- "@object-ui/react": "0.5.0",
27
- "@object-ui/types": "0.5.0"
20
+ "react": "19.2.4",
21
+ "react-dom": "19.2.4",
22
+ "react-grid-layout": "^2.2.2",
23
+ "tailwind-merge": "^2.6.1",
24
+ "@object-ui/components": "3.0.0",
25
+ "@object-ui/core": "3.0.0",
26
+ "@object-ui/react": "3.0.0",
27
+ "@object-ui/types": "3.0.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "^18.0.0",
31
31
  "react-dom": "^18.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/react-grid-layout": "^1.3.5",
35
- "@vitejs/plugin-react": "^5.1.3",
34
+ "@types/react-grid-layout": "^2.1.0",
35
+ "@vitejs/plugin-react": "^5.1.4",
36
36
  "typescript": "^5.9.3",
37
37
  "vite": "^7.3.1",
38
38
  "vite-plugin-dts": "^4.5.4"
@@ -1,12 +1,26 @@
1
1
  import * as React from 'react';
2
- import { Responsive, WidthProvider, Layout as RGLLayout } from 'react-grid-layout';
2
+ import { ResponsiveGridLayout, useContainerWidth, type LayoutItem as RGLLayout, type Layout, type ResponsiveLayouts } from 'react-grid-layout';
3
3
  import 'react-grid-layout/css/styles.css';
4
4
  import { cn, Card, CardHeader, CardTitle, CardContent, Button } from '@object-ui/components';
5
- import { Edit, GripVertical, Save, X } from 'lucide-react';
6
- import { SchemaRenderer } from '@object-ui/react';
5
+ import { Edit, GripVertical, Save, X, RefreshCw } from 'lucide-react';
6
+ import { SchemaRenderer, useHasDndProvider, useDnd } from '@object-ui/react';
7
7
  import type { DashboardSchema, DashboardWidgetSchema } from '@object-ui/types';
8
8
 
9
- const ResponsiveGridLayout = WidthProvider(Responsive);
9
+ /** Bridges editMode transitions to the ObjectUI DnD system when a DndProvider is present. */
10
+ function DndEditModeBridge({ editMode }: { editMode: boolean }) {
11
+ const dnd = useDnd();
12
+
13
+ React.useEffect(() => {
14
+ if (editMode) {
15
+ dnd.startDrag({ id: 'dashboard-layout', type: 'dashboard-widget', data: {} });
16
+ return () => { dnd.endDrag(); };
17
+ } else {
18
+ dnd.endDrag('dashboard');
19
+ }
20
+ }, [editMode, dnd]);
21
+
22
+ return null;
23
+ }
10
24
 
11
25
  const CHART_COLORS = [
12
26
  'hsl(var(--chart-1))',
@@ -21,6 +35,8 @@ export interface DashboardGridLayoutProps {
21
35
  className?: string;
22
36
  onLayoutChange?: (layout: RGLLayout[]) => void;
23
37
  persistLayoutKey?: string;
38
+ /** Callback invoked when dashboard refresh is triggered (manual or auto) */
39
+ onRefresh?: () => void;
24
40
  }
25
41
 
26
42
  export const DashboardGridLayout: React.FC<DashboardGridLayoutProps> = ({
@@ -28,8 +44,29 @@ export const DashboardGridLayout: React.FC<DashboardGridLayoutProps> = ({
28
44
  className,
29
45
  onLayoutChange,
30
46
  persistLayoutKey = 'dashboard-layout',
47
+ onRefresh,
31
48
  }) => {
49
+ const { width, containerRef, mounted } = useContainerWidth();
32
50
  const [editMode, setEditMode] = React.useState(false);
51
+ const [refreshing, setRefreshing] = React.useState(false);
52
+ const hasDndProvider = useHasDndProvider();
53
+ const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null);
54
+
55
+ const handleRefresh = React.useCallback(() => {
56
+ if (!onRefresh) return;
57
+ setRefreshing(true);
58
+ onRefresh();
59
+ setTimeout(() => setRefreshing(false), 600);
60
+ }, [onRefresh]);
61
+
62
+ // Auto-refresh interval
63
+ React.useEffect(() => {
64
+ if (!schema.refreshInterval || schema.refreshInterval <= 0 || !onRefresh) return;
65
+ intervalRef.current = setInterval(handleRefresh, schema.refreshInterval * 1000);
66
+ return () => {
67
+ if (intervalRef.current) clearInterval(intervalRef.current);
68
+ };
69
+ }, [schema.refreshInterval, onRefresh, handleRefresh]);
33
70
  const [layouts, setLayouts] = React.useState<{ lg: RGLLayout[] }>(() => {
34
71
  // Try to load saved layout
35
72
  if (typeof window !== 'undefined' && persistLayoutKey) {
@@ -56,9 +93,9 @@ export const DashboardGridLayout: React.FC<DashboardGridLayoutProps> = ({
56
93
  });
57
94
 
58
95
  const handleLayoutChange = React.useCallback(
59
- (layout: RGLLayout[], allLayouts: { lg: RGLLayout[] }) => {
60
- setLayouts(allLayouts);
61
- onLayoutChange?.(layout);
96
+ (layout: Layout, allLayouts: ResponsiveLayouts) => {
97
+ setLayouts(allLayouts as { lg: RGLLayout[] });
98
+ onLayoutChange?.(layout as RGLLayout[]);
62
99
  },
63
100
  [onLayoutChange]
64
101
  );
@@ -122,7 +159,8 @@ export const DashboardGridLayout: React.FC<DashboardGridLayoutProps> = ({
122
159
  }, []);
123
160
 
124
161
  return (
125
- <div className={cn("w-full", className)} data-testid="grid-layout">
162
+ <div ref={containerRef} className={cn("w-full", className)} data-testid="grid-layout">
163
+ {hasDndProvider && <DndEditModeBridge editMode={editMode} />}
126
164
  <div className="mb-4 flex items-center justify-between">
127
165
  <h2 className="text-2xl font-bold">{schema.title || 'Dashboard'}</h2>
128
166
  <div className="flex gap-2">
@@ -141,70 +179,86 @@ export const DashboardGridLayout: React.FC<DashboardGridLayoutProps> = ({
141
179
  </Button>
142
180
  </>
143
181
  ) : (
144
- <Button onClick={() => setEditMode(true)} size="sm" variant="outline">
145
- <Edit className="h-4 w-4 mr-2" />
146
- Edit Layout
147
- </Button>
182
+ <>
183
+ {onRefresh && (
184
+ <Button
185
+ onClick={handleRefresh}
186
+ size="sm"
187
+ variant="outline"
188
+ disabled={refreshing}
189
+ aria-label="Refresh dashboard"
190
+ >
191
+ <RefreshCw className={cn("h-4 w-4 mr-2", refreshing && "animate-spin")} />
192
+ {refreshing ? 'Refreshing…' : 'Refresh All'}
193
+ </Button>
194
+ )}
195
+ <Button onClick={() => setEditMode(true)} size="sm" variant="outline">
196
+ <Edit className="h-4 w-4 mr-2" />
197
+ Edit Layout
198
+ </Button>
199
+ </>
148
200
  )}
149
201
  </div>
150
202
  </div>
151
203
 
152
- <ResponsiveGridLayout
153
- className="layout"
154
- layouts={layouts}
155
- breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
156
- cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
157
- rowHeight={60}
158
- isDraggable={editMode}
159
- isResizable={editMode}
160
- onLayoutChange={handleLayoutChange}
161
- draggableHandle=".drag-handle"
162
- >
163
- {schema.widgets?.map((widget, index) => {
164
- const widgetId = widget.id || `widget-${index}`;
165
- const componentSchema = getComponentSchema(widget);
166
- const isSelfContained = (widget as any).type === 'metric';
167
-
168
- return (
169
- <div key={widgetId} className="h-full">
170
- {isSelfContained ? (
171
- <div className="h-full w-full relative">
172
- {editMode && (
173
- <div className="drag-handle absolute top-2 right-2 z-10 cursor-move p-1 bg-background/80 rounded border border-border">
174
- <GripVertical className="h-4 w-4" />
175
- </div>
176
- )}
177
- <SchemaRenderer schema={componentSchema} className="h-full w-full" />
178
- </div>
179
- ) : (
180
- <Card className={cn(
181
- "h-full overflow-hidden border-border/50 shadow-sm transition-all",
182
- "bg-card/50 backdrop-blur-sm",
183
- editMode && "ring-2 ring-primary/20"
184
- )}>
185
- {widget.title && (
186
- <CardHeader className="pb-2 border-b border-border/40 bg-muted/20 flex flex-row items-center justify-between">
187
- <CardTitle className="text-base font-medium tracking-tight truncate" title={widget.title}>
188
- {widget.title}
189
- </CardTitle>
190
- {editMode && (
191
- <div className="drag-handle cursor-move p-1 hover:bg-muted/40 rounded">
192
- <GripVertical className="h-4 w-4" />
193
- </div>
194
- )}
195
- </CardHeader>
196
- )}
197
- <CardContent className="p-0 h-full">
198
- <div className={cn("h-full w-full overflow-auto", !widget.title ? "p-4" : "p-4")}>
199
- <SchemaRenderer schema={componentSchema} />
200
- </div>
201
- </CardContent>
202
- </Card>
203
- )}
204
- </div>
205
- );
206
- })}
207
- </ResponsiveGridLayout>
204
+ {mounted && (
205
+ <ResponsiveGridLayout
206
+ className="layout"
207
+ width={width}
208
+ layouts={layouts}
209
+ breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
210
+ cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
211
+ rowHeight={60}
212
+ dragConfig={{ enabled: editMode, handle: ".drag-handle" }}
213
+ resizeConfig={{ enabled: editMode }}
214
+ onLayoutChange={handleLayoutChange}
215
+ >
216
+ {schema.widgets?.map((widget, index) => {
217
+ const widgetId = widget.id || `widget-${index}`;
218
+ const componentSchema = getComponentSchema(widget);
219
+ const isSelfContained = (widget as any).type === 'metric';
220
+
221
+ return (
222
+ <div key={widgetId} className="h-full">
223
+ {isSelfContained ? (
224
+ <div className="h-full w-full relative">
225
+ {editMode && (
226
+ <div className="drag-handle absolute top-2 right-2 z-10 cursor-move p-1 bg-background/80 rounded border border-border">
227
+ <GripVertical className="h-4 w-4" />
228
+ </div>
229
+ )}
230
+ <SchemaRenderer schema={componentSchema} className="h-full w-full" />
231
+ </div>
232
+ ) : (
233
+ <Card className={cn(
234
+ "h-full overflow-hidden border-border/50 shadow-sm transition-all",
235
+ "bg-card/50 backdrop-blur-sm",
236
+ editMode && "ring-2 ring-primary/20"
237
+ )}>
238
+ {widget.title && (
239
+ <CardHeader className="pb-2 border-b border-border/40 bg-muted/20 flex flex-row items-center justify-between">
240
+ <CardTitle className="text-base font-medium tracking-tight truncate" title={widget.title}>
241
+ {widget.title}
242
+ </CardTitle>
243
+ {editMode && (
244
+ <div className="drag-handle cursor-move p-1 hover:bg-muted/40 rounded">
245
+ <GripVertical className="h-4 w-4" />
246
+ </div>
247
+ )}
248
+ </CardHeader>
249
+ )}
250
+ <CardContent className="p-0 h-full">
251
+ <div className={cn("h-full w-full overflow-auto p-4")}>
252
+ <SchemaRenderer schema={componentSchema} />
253
+ </div>
254
+ </CardContent>
255
+ </Card>
256
+ )}
257
+ </div>
258
+ );
259
+ })}
260
+ </ResponsiveGridLayout>
261
+ )}
208
262
  </div>
209
263
  );
210
264
  };
@@ -0,0 +1,173 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { SchemaRenderer } from '@object-ui/react';
3
+ import type { BaseSchema } from '@object-ui/types';
4
+
5
+ const meta = {
6
+ title: 'Plugins/DashboardRenderer',
7
+ component: SchemaRenderer,
8
+ parameters: {
9
+ layout: 'padded',
10
+ },
11
+ tags: ['autodocs'],
12
+ argTypes: {
13
+ schema: { table: { disable: true } },
14
+ },
15
+ } satisfies Meta<any>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ const renderStory = (args: any) => <SchemaRenderer schema={args as unknown as BaseSchema} />;
21
+
22
+ export const Default: Story = {
23
+ render: renderStory,
24
+ args: {
25
+ type: 'dashboard',
26
+ columns: 3,
27
+ gap: 4,
28
+ widgets: [
29
+ {
30
+ id: 'metric-1',
31
+ component: {
32
+ type: 'metric',
33
+ label: 'Total Revenue',
34
+ value: '$128,430',
35
+ },
36
+ },
37
+ {
38
+ id: 'metric-2',
39
+ component: {
40
+ type: 'metric',
41
+ label: 'Active Users',
42
+ value: '3,842',
43
+ },
44
+ },
45
+ {
46
+ id: 'metric-3',
47
+ component: {
48
+ type: 'metric',
49
+ label: 'Conversion Rate',
50
+ value: '4.2%',
51
+ },
52
+ },
53
+ ],
54
+ } as any,
55
+ };
56
+
57
+ export const WithMetricCards: Story = {
58
+ render: renderStory,
59
+ args: {
60
+ type: 'dashboard',
61
+ columns: 3,
62
+ gap: 4,
63
+ widgets: [
64
+ {
65
+ id: 'mc-1',
66
+ component: {
67
+ type: 'metric-card',
68
+ title: 'Monthly Revenue',
69
+ value: '$52,489',
70
+ icon: 'DollarSign',
71
+ trend: 'up',
72
+ trendValue: '+14.2%',
73
+ description: 'vs last month',
74
+ },
75
+ layout: { x: 0, y: 0, w: 1, h: 1 },
76
+ },
77
+ {
78
+ id: 'mc-2',
79
+ component: {
80
+ type: 'metric-card',
81
+ title: 'New Signups',
82
+ value: '1,205',
83
+ icon: 'Users',
84
+ trend: 'up',
85
+ trendValue: '+8.1%',
86
+ description: 'vs last month',
87
+ },
88
+ layout: { x: 1, y: 0, w: 1, h: 1 },
89
+ },
90
+ {
91
+ id: 'mc-3',
92
+ component: {
93
+ type: 'metric-card',
94
+ title: 'Churn Rate',
95
+ value: '1.8%',
96
+ icon: 'TrendingDown',
97
+ trend: 'down',
98
+ trendValue: '-0.3%',
99
+ description: 'vs last month',
100
+ },
101
+ layout: { x: 2, y: 0, w: 1, h: 1 },
102
+ },
103
+ ],
104
+ } as any,
105
+ };
106
+
107
+ export const WithChartsAndMetrics: Story = {
108
+ render: renderStory,
109
+ args: {
110
+ type: 'dashboard',
111
+ columns: 3,
112
+ gap: 4,
113
+ widgets: [
114
+ {
115
+ id: 'd-m1',
116
+ component: {
117
+ type: 'metric-card',
118
+ title: 'Total Orders',
119
+ value: '1,284',
120
+ icon: 'ShoppingCart',
121
+ trend: 'up',
122
+ trendValue: '+11%',
123
+ },
124
+ layout: { x: 0, y: 0, w: 1, h: 1 },
125
+ },
126
+ {
127
+ id: 'd-m2',
128
+ component: {
129
+ type: 'metric-card',
130
+ title: 'Avg Order Value',
131
+ value: '$86.50',
132
+ icon: 'DollarSign',
133
+ trend: 'up',
134
+ trendValue: '+3.2%',
135
+ },
136
+ layout: { x: 1, y: 0, w: 1, h: 1 },
137
+ },
138
+ {
139
+ id: 'd-m3',
140
+ component: {
141
+ type: 'metric-card',
142
+ title: 'Return Rate',
143
+ value: '2.1%',
144
+ icon: 'TrendingDown',
145
+ trend: 'down',
146
+ trendValue: '-0.8%',
147
+ },
148
+ layout: { x: 2, y: 0, w: 1, h: 1 },
149
+ },
150
+ {
151
+ id: 'd-chart',
152
+ title: 'Weekly Sales',
153
+ component: {
154
+ type: 'chart',
155
+ chartType: 'bar',
156
+ data: [
157
+ { day: 'Mon', sales: 120 },
158
+ { day: 'Tue', sales: 180 },
159
+ { day: 'Wed', sales: 150 },
160
+ { day: 'Thu', sales: 210 },
161
+ { day: 'Fri', sales: 190 },
162
+ ],
163
+ xAxisKey: 'day',
164
+ series: [{ dataKey: 'sales' }],
165
+ config: {
166
+ sales: { label: 'Sales', color: '#3b82f6' },
167
+ },
168
+ },
169
+ layout: { x: 0, y: 1, w: 3, h: 2 },
170
+ },
171
+ ],
172
+ } as any,
173
+ };