@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.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +34 -0
- package/dist/index.css +1 -1
- package/dist/index.js +3612 -4764
- package/dist/index.umd.cjs +5 -5
- package/dist/src/DashboardGridLayout.d.ts +3 -1
- package/dist/src/DashboardGridLayout.d.ts.map +1 -1
- package/dist/src/DashboardRenderer.d.ts +6 -3
- package/dist/src/DashboardRenderer.d.ts.map +1 -1
- package/dist/src/DashboardRenderer.stories.d.ts +24 -0
- package/dist/src/DashboardRenderer.stories.d.ts.map +1 -0
- package/dist/src/index.d.ts +2 -12
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/DashboardGridLayout.tsx +122 -68
- package/src/DashboardRenderer.stories.tsx +173 -0
- package/src/DashboardRenderer.tsx +161 -99
- package/src/__tests__/DashboardRenderer.autoRefresh.test.tsx +124 -0
- package/src/index.tsx +2 -60
- package/dist/src/ReportBuilder.d.ts +0 -11
- package/dist/src/ReportBuilder.d.ts.map +0 -1
- package/dist/src/ReportRenderer.d.ts +0 -15
- package/dist/src/ReportRenderer.d.ts.map +0 -1
- package/dist/src/ReportViewer.d.ts +0 -11
- package/dist/src/ReportViewer.d.ts.map +0 -1
- package/src/ReportBuilder.tsx +0 -625
- package/src/ReportRenderer.tsx +0 -89
- package/src/ReportViewer.tsx +0 -232
- package/src/__tests__/ReportBuilder.test.tsx +0 -115
- package/src/__tests__/ReportViewer.test.tsx +0 -107
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
import type { DashboardSchema, DashboardWidgetSchema } from '@object-ui/types';
|
|
10
10
|
import { SchemaRenderer } from '@object-ui/react';
|
|
11
|
-
import { cn, Card, CardHeader, CardTitle, CardContent } from '@object-ui/components';
|
|
12
|
-
import { forwardRef } from 'react';
|
|
11
|
+
import { cn, Card, CardHeader, CardTitle, CardContent, Button } from '@object-ui/components';
|
|
12
|
+
import { forwardRef, useState, useEffect, useCallback, useRef } from 'react';
|
|
13
|
+
import { RefreshCw } from 'lucide-react';
|
|
13
14
|
|
|
14
15
|
// Color palette for charts
|
|
15
16
|
const CHART_COLORS = [
|
|
@@ -20,117 +21,178 @@ const CHART_COLORS = [
|
|
|
20
21
|
'hsl(var(--chart-5))',
|
|
21
22
|
];
|
|
22
23
|
|
|
23
|
-
export
|
|
24
|
-
|
|
24
|
+
export interface DashboardRendererProps {
|
|
25
|
+
schema: DashboardSchema;
|
|
26
|
+
className?: string;
|
|
27
|
+
/** Callback invoked when dashboard refresh is triggered (manual or auto) */
|
|
28
|
+
onRefresh?: () => void;
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererProps>(
|
|
33
|
+
({ schema, className, dataSource, onRefresh, ...props }, ref) => {
|
|
25
34
|
const columns = schema.columns || 4; // Default to 4 columns for better density
|
|
26
35
|
const gap = schema.gap || 4;
|
|
36
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
37
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
38
|
+
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (widgetType === 'table') {
|
|
68
|
-
// Map to ObjectGrid
|
|
69
|
-
return {
|
|
70
|
-
type: 'data-table',
|
|
71
|
-
...(widget as any).options,
|
|
72
|
-
data: (widget as any).data?.items || [],
|
|
73
|
-
searchable: false, // Simple table for dashboard
|
|
74
|
-
pagination: false,
|
|
75
|
-
className: "border-0"
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// For generic widgets (like 'metric'), we simply spread the options into the schema
|
|
80
|
-
// so they appear as top-level props for the component.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const checkMobile = () => setIsMobile(window.innerWidth < 640);
|
|
42
|
+
checkMobile();
|
|
43
|
+
window.addEventListener('resize', checkMobile);
|
|
44
|
+
return () => window.removeEventListener('resize', checkMobile);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
const handleRefresh = useCallback(() => {
|
|
48
|
+
if (!onRefresh) return;
|
|
49
|
+
setRefreshing(true);
|
|
50
|
+
onRefresh();
|
|
51
|
+
// Reset refreshing indicator after a short delay
|
|
52
|
+
setTimeout(() => setRefreshing(false), 600);
|
|
53
|
+
}, [onRefresh]);
|
|
54
|
+
|
|
55
|
+
// Auto-refresh interval
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!schema.refreshInterval || schema.refreshInterval <= 0 || !onRefresh) return;
|
|
58
|
+
intervalRef.current = setInterval(handleRefresh, schema.refreshInterval * 1000);
|
|
59
|
+
return () => {
|
|
60
|
+
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
61
|
+
};
|
|
62
|
+
}, [schema.refreshInterval, onRefresh, handleRefresh]);
|
|
63
|
+
|
|
64
|
+
const renderWidget = (widget: DashboardWidgetSchema) => {
|
|
65
|
+
const getComponentSchema = () => {
|
|
66
|
+
if (widget.component) return widget.component;
|
|
67
|
+
|
|
68
|
+
// Handle Shorthand Registry Mappings
|
|
69
|
+
const widgetType = (widget as any).type;
|
|
70
|
+
if (widgetType === 'bar' || widgetType === 'line' || widgetType === 'area' || widgetType === 'pie' || widgetType === 'donut') {
|
|
71
|
+
const dataItems = Array.isArray((widget as any).data) ? (widget as any).data : (widget as any).data?.items || [];
|
|
72
|
+
const options = (widget as any).options || {};
|
|
73
|
+
const xAxisKey = options.xField || 'name';
|
|
74
|
+
const yField = options.yField || 'value';
|
|
75
|
+
|
|
81
76
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
type: 'chart',
|
|
78
|
+
chartType: widgetType,
|
|
79
|
+
data: dataItems,
|
|
80
|
+
xAxisKey: xAxisKey,
|
|
81
|
+
series: [{ dataKey: yField }],
|
|
82
|
+
colors: CHART_COLORS,
|
|
83
|
+
className: "h-[200px] sm:h-[250px] md:h-[300px]"
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (widgetType === 'table') {
|
|
88
|
+
return {
|
|
89
|
+
type: 'data-table',
|
|
90
|
+
...(widget as any).options,
|
|
91
|
+
data: (widget as any).data?.items || [],
|
|
92
|
+
searchable: false,
|
|
93
|
+
pagination: false,
|
|
94
|
+
className: "border-0"
|
|
84
95
|
};
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const componentSchema = getComponentSchema();
|
|
88
|
-
|
|
89
|
-
// Check if the widget is self-contained (like a Metric Card) to avoid double borders
|
|
90
|
-
const isSelfContained = (widget as any).type === 'metric';
|
|
91
|
-
|
|
92
|
-
if (isSelfContained) {
|
|
93
|
-
return (
|
|
94
|
-
<div
|
|
95
|
-
key={widget.id || widget.title}
|
|
96
|
-
className="h-full w-full"
|
|
97
|
-
style={widget.layout ? {
|
|
98
|
-
gridColumn: `span ${widget.layout.w}`,
|
|
99
|
-
gridRow: `span ${widget.layout.h}`
|
|
100
|
-
}: undefined}
|
|
101
|
-
>
|
|
102
|
-
<SchemaRenderer schema={componentSchema} className="h-full w-full" />
|
|
103
|
-
</div>
|
|
104
|
-
);
|
|
105
96
|
}
|
|
106
97
|
|
|
98
|
+
return {
|
|
99
|
+
...widget,
|
|
100
|
+
...((widget as any).options || {})
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const componentSchema = getComponentSchema();
|
|
105
|
+
const isSelfContained = (widget as any).type === 'metric';
|
|
106
|
+
|
|
107
|
+
if (isSelfContained) {
|
|
107
108
|
return (
|
|
108
|
-
<
|
|
109
|
+
<div
|
|
109
110
|
key={widget.id || widget.title}
|
|
110
|
-
className={cn(
|
|
111
|
-
|
|
112
|
-
"bg-card/50 backdrop-blur-sm"
|
|
113
|
-
)}
|
|
114
|
-
style={widget.layout ? {
|
|
111
|
+
className={cn("h-full w-full", isMobile && "w-[85vw] shrink-0 snap-center")}
|
|
112
|
+
style={!isMobile && widget.layout ? {
|
|
115
113
|
gridColumn: `span ${widget.layout.w}`,
|
|
116
114
|
gridRow: `span ${widget.layout.h}`
|
|
117
115
|
}: undefined}
|
|
118
116
|
>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<CardTitle className="text-base font-medium tracking-tight truncate" title={widget.title}>
|
|
122
|
-
{widget.title}
|
|
123
|
-
</CardTitle>
|
|
124
|
-
</CardHeader>
|
|
125
|
-
)}
|
|
126
|
-
<CardContent className="p-0">
|
|
127
|
-
<div className={cn("h-full w-full", !widget.title ? "p-4" : "p-4")}>
|
|
128
|
-
<SchemaRenderer schema={componentSchema} />
|
|
129
|
-
</div>
|
|
130
|
-
</CardContent>
|
|
131
|
-
</Card>
|
|
117
|
+
<SchemaRenderer schema={componentSchema} className="h-full w-full" />
|
|
118
|
+
</div>
|
|
132
119
|
);
|
|
133
|
-
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Card
|
|
124
|
+
key={widget.id || widget.title}
|
|
125
|
+
className={cn(
|
|
126
|
+
"overflow-hidden border-border/50 shadow-sm transition-all hover:shadow-md",
|
|
127
|
+
"bg-card/50 backdrop-blur-sm",
|
|
128
|
+
isMobile && "w-[85vw] shrink-0 snap-center"
|
|
129
|
+
)}
|
|
130
|
+
style={!isMobile && widget.layout ? {
|
|
131
|
+
gridColumn: `span ${widget.layout.w}`,
|
|
132
|
+
gridRow: `span ${widget.layout.h}`
|
|
133
|
+
}: undefined}
|
|
134
|
+
>
|
|
135
|
+
{widget.title && (
|
|
136
|
+
<CardHeader className="pb-2 border-b border-border/40 bg-muted/20 px-3 sm:px-6">
|
|
137
|
+
<CardTitle className="text-sm sm:text-base font-medium tracking-tight truncate" title={widget.title}>
|
|
138
|
+
{widget.title}
|
|
139
|
+
</CardTitle>
|
|
140
|
+
</CardHeader>
|
|
141
|
+
)}
|
|
142
|
+
<CardContent className="p-0">
|
|
143
|
+
<div className={cn("h-full w-full", "p-3 sm:p-4 md:p-6")}>
|
|
144
|
+
<SchemaRenderer schema={componentSchema} />
|
|
145
|
+
</div>
|
|
146
|
+
</CardContent>
|
|
147
|
+
</Card>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const refreshButton = onRefresh && (
|
|
152
|
+
<div className={cn(isMobile ? "flex justify-end mb-2" : "col-span-full flex justify-end mb-2")}>
|
|
153
|
+
<Button
|
|
154
|
+
variant="outline"
|
|
155
|
+
size="sm"
|
|
156
|
+
onClick={handleRefresh}
|
|
157
|
+
disabled={refreshing}
|
|
158
|
+
aria-label="Refresh dashboard"
|
|
159
|
+
>
|
|
160
|
+
<RefreshCw className={cn("h-4 w-4 mr-2", refreshing && "animate-spin")} />
|
|
161
|
+
{refreshing ? 'Refreshing…' : 'Refresh All'}
|
|
162
|
+
</Button>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (isMobile) {
|
|
167
|
+
return (
|
|
168
|
+
<div ref={ref} className={cn("flex flex-col", className)} {...props}>
|
|
169
|
+
{refreshButton}
|
|
170
|
+
<div
|
|
171
|
+
className="flex overflow-x-auto snap-x snap-mandatory gap-3 pb-4 [-webkit-overflow-scrolling:touch]"
|
|
172
|
+
style={{ scrollPaddingLeft: '0.75rem' }}
|
|
173
|
+
>
|
|
174
|
+
{schema.widgets?.map((widget: DashboardWidgetSchema) => renderWidget(widget))}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div
|
|
182
|
+
ref={ref}
|
|
183
|
+
className={cn(
|
|
184
|
+
"grid auto-rows-min",
|
|
185
|
+
"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
186
|
+
className
|
|
187
|
+
)}
|
|
188
|
+
style={{
|
|
189
|
+
...(columns > 4 && { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }),
|
|
190
|
+
gap: `${gap * 0.25}rem`
|
|
191
|
+
}}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
{refreshButton}
|
|
195
|
+
{schema.widgets?.map((widget: DashboardWidgetSchema) => renderWidget(widget))}
|
|
134
196
|
</div>
|
|
135
197
|
);
|
|
136
198
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
11
|
+
import { DashboardRenderer } from '../DashboardRenderer';
|
|
12
|
+
import type { DashboardSchema } from '@object-ui/types';
|
|
13
|
+
|
|
14
|
+
describe('DashboardRenderer auto-refresh', () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.useFakeTimers();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
vi.useRealTimers();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const mockSchema: DashboardSchema = {
|
|
24
|
+
type: 'dashboard',
|
|
25
|
+
name: 'test_dashboard',
|
|
26
|
+
title: 'Test Dashboard',
|
|
27
|
+
widgets: [],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
it('should not render refresh button when onRefresh is not provided', () => {
|
|
31
|
+
render(<DashboardRenderer schema={mockSchema} />);
|
|
32
|
+
expect(screen.queryByLabelText('Refresh dashboard')).not.toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should render refresh button when onRefresh is provided', () => {
|
|
36
|
+
const onRefresh = vi.fn();
|
|
37
|
+
render(<DashboardRenderer schema={mockSchema} onRefresh={onRefresh} />);
|
|
38
|
+
expect(screen.getByLabelText('Refresh dashboard')).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should call onRefresh when refresh button is clicked', () => {
|
|
42
|
+
const onRefresh = vi.fn();
|
|
43
|
+
render(<DashboardRenderer schema={mockSchema} onRefresh={onRefresh} />);
|
|
44
|
+
|
|
45
|
+
const button = screen.getByLabelText('Refresh dashboard');
|
|
46
|
+
fireEvent.click(button);
|
|
47
|
+
|
|
48
|
+
expect(onRefresh).toHaveBeenCalledTimes(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should auto-refresh at the configured interval', () => {
|
|
52
|
+
const onRefresh = vi.fn();
|
|
53
|
+
const schemaWithRefresh: DashboardSchema = {
|
|
54
|
+
...mockSchema,
|
|
55
|
+
refreshInterval: 30, // 30 seconds
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
render(<DashboardRenderer schema={schemaWithRefresh} onRefresh={onRefresh} />);
|
|
59
|
+
|
|
60
|
+
expect(onRefresh).not.toHaveBeenCalled();
|
|
61
|
+
|
|
62
|
+
// Advance past one interval
|
|
63
|
+
act(() => {
|
|
64
|
+
vi.advanceTimersByTime(30_000);
|
|
65
|
+
});
|
|
66
|
+
expect(onRefresh).toHaveBeenCalledTimes(1);
|
|
67
|
+
|
|
68
|
+
// Advance past another interval
|
|
69
|
+
act(() => {
|
|
70
|
+
vi.advanceTimersByTime(30_000);
|
|
71
|
+
});
|
|
72
|
+
expect(onRefresh).toHaveBeenCalledTimes(2);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should not auto-refresh when refreshInterval is 0', () => {
|
|
76
|
+
const onRefresh = vi.fn();
|
|
77
|
+
const schemaWithZeroInterval: DashboardSchema = {
|
|
78
|
+
...mockSchema,
|
|
79
|
+
refreshInterval: 0,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
render(<DashboardRenderer schema={schemaWithZeroInterval} onRefresh={onRefresh} />);
|
|
83
|
+
|
|
84
|
+
act(() => {
|
|
85
|
+
vi.advanceTimersByTime(60_000);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(onRefresh).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should not auto-refresh when onRefresh is not provided', () => {
|
|
92
|
+
const schemaWithRefresh: DashboardSchema = {
|
|
93
|
+
...mockSchema,
|
|
94
|
+
refreshInterval: 10,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Should not throw
|
|
98
|
+
render(<DashboardRenderer schema={schemaWithRefresh} />);
|
|
99
|
+
|
|
100
|
+
act(() => {
|
|
101
|
+
vi.advanceTimersByTime(30_000);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should clean up interval on unmount', () => {
|
|
106
|
+
const onRefresh = vi.fn();
|
|
107
|
+
const schemaWithRefresh: DashboardSchema = {
|
|
108
|
+
...mockSchema,
|
|
109
|
+
refreshInterval: 5,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const { unmount } = render(
|
|
113
|
+
<DashboardRenderer schema={schemaWithRefresh} onRefresh={onRefresh} />
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
unmount();
|
|
117
|
+
|
|
118
|
+
act(() => {
|
|
119
|
+
vi.advanceTimersByTime(30_000);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(onRefresh).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
});
|
package/src/index.tsx
CHANGED
|
@@ -11,18 +11,15 @@ import { DashboardRenderer } from './DashboardRenderer';
|
|
|
11
11
|
import { DashboardGridLayout } from './DashboardGridLayout';
|
|
12
12
|
import { MetricWidget } from './MetricWidget';
|
|
13
13
|
import { MetricCard } from './MetricCard';
|
|
14
|
-
import { ReportRenderer } from './ReportRenderer';
|
|
15
|
-
import { ReportViewer } from './ReportViewer';
|
|
16
|
-
import { ReportBuilder } from './ReportBuilder';
|
|
17
14
|
|
|
18
|
-
export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard
|
|
15
|
+
export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard };
|
|
19
16
|
|
|
20
17
|
// Register dashboard component
|
|
21
18
|
ComponentRegistry.register(
|
|
22
19
|
'dashboard',
|
|
23
20
|
DashboardRenderer,
|
|
24
21
|
{
|
|
25
|
-
namespace: '
|
|
22
|
+
namespace: 'view',
|
|
26
23
|
label: 'Dashboard',
|
|
27
24
|
category: 'Complex',
|
|
28
25
|
icon: 'layout-dashboard',
|
|
@@ -80,57 +77,6 @@ ComponentRegistry.register(
|
|
|
80
77
|
}
|
|
81
78
|
);
|
|
82
79
|
|
|
83
|
-
// Register report component (legacy)
|
|
84
|
-
ComponentRegistry.register(
|
|
85
|
-
'report',
|
|
86
|
-
ReportRenderer,
|
|
87
|
-
{
|
|
88
|
-
namespace: 'plugin-dashboard',
|
|
89
|
-
label: 'Report',
|
|
90
|
-
category: 'Dashboard',
|
|
91
|
-
inputs: [
|
|
92
|
-
{ name: 'title', type: 'string', label: 'Title' },
|
|
93
|
-
{ name: 'description', type: 'string', label: 'Description' },
|
|
94
|
-
{ name: 'chart', type: 'code', label: 'Chart Configuration' },
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// Register report viewer component
|
|
100
|
-
ComponentRegistry.register(
|
|
101
|
-
'report-viewer',
|
|
102
|
-
ReportViewer,
|
|
103
|
-
{
|
|
104
|
-
namespace: 'plugin-dashboard',
|
|
105
|
-
label: 'Report Viewer',
|
|
106
|
-
category: 'Reports',
|
|
107
|
-
inputs: [
|
|
108
|
-
{ name: 'report', type: 'code', label: 'Report Configuration', required: true },
|
|
109
|
-
{ name: 'data', type: 'code', label: 'Report Data' },
|
|
110
|
-
{ name: 'showToolbar', type: 'boolean', label: 'Show Toolbar', defaultValue: true },
|
|
111
|
-
{ name: 'allowExport', type: 'boolean', label: 'Allow Export', defaultValue: true },
|
|
112
|
-
{ name: 'allowPrint', type: 'boolean', label: 'Allow Print', defaultValue: true },
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// Register report builder component
|
|
118
|
-
ComponentRegistry.register(
|
|
119
|
-
'report-builder',
|
|
120
|
-
ReportBuilder,
|
|
121
|
-
{
|
|
122
|
-
namespace: 'plugin-dashboard',
|
|
123
|
-
label: 'Report Builder',
|
|
124
|
-
category: 'Reports',
|
|
125
|
-
inputs: [
|
|
126
|
-
{ name: 'report', type: 'code', label: 'Initial Report Config' },
|
|
127
|
-
{ name: 'dataSources', type: 'code', label: 'Available Data Sources' },
|
|
128
|
-
{ name: 'availableFields', type: 'code', label: 'Available Fields' },
|
|
129
|
-
{ name: 'showPreview', type: 'boolean', label: 'Show Preview', defaultValue: true },
|
|
130
|
-
]
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
|
|
134
80
|
// Register dashboard grid layout component
|
|
135
81
|
ComponentRegistry.register(
|
|
136
82
|
'dashboard-grid',
|
|
@@ -159,8 +105,4 @@ export const dashboardComponents = {
|
|
|
159
105
|
DashboardGridLayout,
|
|
160
106
|
MetricWidget,
|
|
161
107
|
MetricCard,
|
|
162
|
-
ReportRenderer,
|
|
163
|
-
ReportViewer,
|
|
164
|
-
ReportBuilder,
|
|
165
108
|
};
|
|
166
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
import { ReportBuilderSchema } from '../../types/src';
|
|
3
|
-
export interface ReportBuilderProps {
|
|
4
|
-
schema: ReportBuilderSchema;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* ReportBuilder - Interactive report builder component
|
|
8
|
-
* Allows users to configure report fields, filters, grouping, sections, and export settings
|
|
9
|
-
*/
|
|
10
|
-
export declare const ReportBuilder: React.FC<ReportBuilderProps>;
|
|
11
|
-
//# sourceMappingURL=ReportBuilder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReportBuilder.d.ts","sourceRoot":"","sources":["../../src/ReportBuilder.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAyE,MAAM,kBAAkB,CAAC;AAInI,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA0lBtD,CAAC"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
export interface ReportRendererProps {
|
|
3
|
-
schema: {
|
|
4
|
-
type: string;
|
|
5
|
-
id?: string;
|
|
6
|
-
title?: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
chart?: any;
|
|
9
|
-
data?: any[];
|
|
10
|
-
columns?: any[];
|
|
11
|
-
className?: string;
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
export declare const ReportRenderer: React.FC<ReportRendererProps>;
|
|
15
|
-
//# sourceMappingURL=ReportRenderer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReportRenderer.d.ts","sourceRoot":"","sources":["../../src/ReportRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,GAAG,CAAC;QACZ,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAuExD,CAAC"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
import { ReportViewerSchema } from '../../types/src';
|
|
3
|
-
export interface ReportViewerProps {
|
|
4
|
-
schema: ReportViewerSchema;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* ReportViewer - Display a generated report with optional toolbar
|
|
8
|
-
* Supports rendering report sections, charts, tables, and export functionality
|
|
9
|
-
*/
|
|
10
|
-
export declare const ReportViewer: React.FC<ReportViewerProps>;
|
|
11
|
-
//# sourceMappingURL=ReportViewer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReportViewer.d.ts","sourceRoot":"","sources":["../../src/ReportViewer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,kBAAkB,EAAiB,MAAM,kBAAkB,CAAC;AAG1E,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgNpD,CAAC"}
|