Skip to content

Commit 6e7580f

Browse files
committed
feat: update objectstack configuration and enhance DashboardRenderer for improved widget rendering
1 parent 78bc002 commit 6e7580f

File tree

2 files changed

+103
-27
lines changed

2 files changed

+103
-27
lines changed

examples/crm/objectstack.config.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,13 @@ export default defineStack({
9292
},
9393
// @ts-ignore
9494
data: {
95-
object: 'opportunity',
96-
aggregate: [
97-
{ $group: { _id: '$stage', amount: { $sum: '$amount' } } }
95+
provider: 'value',
96+
items: [
97+
{ stage: 'Prospecting', amount: 120000 },
98+
{ stage: 'Qualification', amount: 85000 },
99+
{ stage: 'Proposal', amount: 50000 },
100+
{ stage: 'Negotiation', amount: 35000 },
101+
{ stage: 'Closed Won', amount: 150000 }
98102
]
99103
}
100104
},
@@ -103,13 +107,22 @@ export default defineStack({
103107
type: 'table',
104108
layout: { x: 2, y: 0, w: 2, h: 2 },
105109
options: {
106-
columns: ['name', 'amount', 'stage']
110+
columns: [
111+
{ header: 'Opportunity Name', accessorKey: 'name' },
112+
{ header: 'Amount', accessorKey: 'amount' },
113+
{ header: 'Stage', accessorKey: 'stage' }
114+
]
107115
},
108116
// @ts-ignore
109117
data: {
110-
object: 'opportunity',
111-
limit: 5,
112-
sort: [['created_date', 'desc']]
118+
provider: 'value',
119+
items: [
120+
{ name: 'TechCorp License', amount: '$50,000', stage: 'Proposal' },
121+
{ name: 'Software Inc Pilot', amount: '$5,000', stage: 'Closed Won' },
122+
{ name: 'Consulting Q2', amount: '$12,000', stage: 'Negotiation' },
123+
{ name: 'Global Widget Deal', amount: '$85,000', stage: 'Qualification' },
124+
{ name: 'Startup Bundle', amount: '$2,500', stage: 'Prospecting' }
125+
]
113126
}
114127
},
115128
{

packages/plugin-dashboard/src/DashboardRenderer.tsx

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,103 @@
88

99
import type { DashboardSchema, DashboardWidgetSchema } from '@object-ui/types';
1010
import { SchemaRenderer } from '@object-ui/react';
11-
import { cn } from '@object-ui/components';
12-
import { forwardRef } from 'react';
11+
import { cn, Card, CardHeader, CardTitle, CardContent } from '@object-ui/components';
12+
import { forwardRef, useMemo } from 'react';
13+
14+
// Color palette for charts
15+
const CHART_COLORS = [
16+
'hsl(var(--chart-1))',
17+
'hsl(var(--chart-2))',
18+
'hsl(var(--chart-3))',
19+
'hsl(var(--chart-4))',
20+
'hsl(var(--chart-5))',
21+
];
1322

1423
export const DashboardRenderer = forwardRef<HTMLDivElement, { schema: DashboardSchema; className?: string; [key: string]: any }>(
1524
({ schema, className, ...props }, ref) => {
16-
const columns = schema.columns || 3;
25+
const columns = schema.columns || 4; // Default to 4 columns for better density
1726
const gap = schema.gap || 4;
1827

19-
// Use style to convert gap number to pixels or use tailwind classes if possible
20-
// Here using inline style for grid gap which maps to 0.25rem * 4 * gap = gap rem
21-
2228
return (
2329
<div
2430
ref={ref}
25-
className={cn("grid", className)}
31+
className={cn("grid auto-rows-min", className)}
2632
style={{
2733
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
2834
gap: `${gap * 0.25}rem`
2935
}}
3036
{...props}
3137
>
32-
{schema.widgets?.map((widget: DashboardWidgetSchema) => (
33-
<div
34-
key={widget.id}
35-
className={cn("border rounded-lg p-4 bg-card text-card-foreground shadow-sm")}
36-
style={widget.layout ? {
37-
gridColumn: `span ${widget.layout.w}`,
38-
gridRow: `span ${widget.layout.h}`
39-
}: undefined}
40-
>
41-
{widget.title && <h3 className="font-semibold mb-2">{widget.title}</h3>}
42-
<SchemaRenderer schema={widget.component} />
43-
</div>
44-
))}
38+
{schema.widgets?.map((widget: DashboardWidgetSchema) => {
39+
// Logic to determine what to render
40+
// Supports both Component Schema (widget.component) and Shorthand (widget.type)
41+
42+
const componentSchema = useMemo(() => {
43+
if (widget.component) return widget.component;
44+
45+
// Handle Shorthand Registry Mappings
46+
if (widget.type === 'bar' || widget.type === 'line' || widget.type === 'area' || widget.type === 'pie' || widget.type === 'donut') {
47+
// Extract data from 'data.items' or 'data' array
48+
const dataItems = Array.isArray((widget as any).data) ? (widget as any).data : (widget as any).data?.items || [];
49+
50+
// Map xField/yField to ChartRenderer expectations
51+
const options = (widget as any).options || {};
52+
const xAxisKey = options.xField || 'name';
53+
const yField = options.yField || 'value';
54+
55+
return {
56+
type: 'chart',
57+
chartType: widget.type,
58+
data: dataItems,
59+
xAxisKey: xAxisKey,
60+
series: [{ dataKey: yField }],
61+
colors: CHART_COLORS,
62+
className: "h-[300px]" // Enforce height
63+
};
64+
}
65+
66+
if (widget.type === 'table') {
67+
// Map to ObjectGrid
68+
return {
69+
type: 'data-table',
70+
...(widget as any).options,
71+
data: (widget as any).data?.items || [],
72+
searchable: false, // Simple table for dashboard
73+
pagination: false,
74+
className: "border-0"
75+
};
76+
}
77+
78+
return widget; // Fallback to widget itself as schema
79+
}, [widget]);
80+
81+
return (
82+
<Card
83+
key={widget.id || widget.title}
84+
className={cn(
85+
"overflow-hidden border-border/50 shadow-sm transition-all hover:shadow-md",
86+
"bg-card/50 backdrop-blur-sm"
87+
)}
88+
style={widget.layout ? {
89+
gridColumn: `span ${widget.layout.w}`,
90+
gridRow: `span ${widget.layout.h}`
91+
}: undefined}
92+
>
93+
{widget.title && (
94+
<CardHeader className="pb-2 border-b border-border/40 bg-muted/20">
95+
<CardTitle className="text-base font-medium tracking-tight truncate" title={widget.title}>
96+
{widget.title}
97+
</CardTitle>
98+
</CardHeader>
99+
)}
100+
<CardContent className="p-0">
101+
<div className={cn("h-full w-full", !widget.title ? "p-4" : "p-4")}>
102+
<SchemaRenderer schema={componentSchema} />
103+
</div>
104+
</CardContent>
105+
</Card>
106+
);
107+
})}
45108
</div>
46109
);
47110
}

0 commit comments

Comments
 (0)