Skip to content

Commit 6044450

Browse files
committed
feat: enhance CRM dashboard with new KPI metrics and charts for better insights
1 parent 651a22d commit 6044450

File tree

2 files changed

+129
-26
lines changed

2 files changed

+129
-26
lines changed

examples/crm/objectstack.config.ts

Lines changed: 116 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,100 @@ export default defineStack({
8282
name: 'crm_dashboard',
8383
label: 'CRM Overview',
8484
widgets: [
85+
// --- KPI Row ---
86+
{
87+
type: 'metric',
88+
component: {
89+
type: 'metric',
90+
label: 'Total Revenue',
91+
value: '$1,245,000',
92+
trend: { value: 12.5, direction: 'up', label: 'vs last month' },
93+
icon: 'DollarSign'
94+
},
95+
layout: { x: 0, y: 0, w: 1, h: 1 }
96+
},
97+
{
98+
type: 'metric',
99+
component: {
100+
type: 'metric',
101+
label: 'Active Deals',
102+
value: '45',
103+
trend: { value: 2.1, direction: 'down', label: 'vs last month' },
104+
icon: 'Briefcase'
105+
},
106+
layout: { x: 1, y: 0, w: 1, h: 1 }
107+
},
108+
{
109+
type: 'metric',
110+
component: {
111+
type: 'metric',
112+
label: 'Win Rate',
113+
value: '68%',
114+
trend: { value: 4.3, direction: 'up', label: 'vs last month' },
115+
icon: 'Trophy'
116+
},
117+
layout: { x: 2, y: 0, w: 1, h: 1 }
118+
},
119+
{
120+
type: 'metric',
121+
component: {
122+
type: 'metric',
123+
label: 'Avg Deal Size',
124+
value: '$24,000',
125+
trend: { value: 1.2, direction: 'up', label: 'vs last month' },
126+
icon: 'BarChart3'
127+
},
128+
layout: { x: 3, y: 0, w: 1, h: 1 }
129+
},
130+
131+
// --- Row 2: Charts ---
132+
{
133+
title: 'Revenue Trends',
134+
type: 'area',
135+
layout: { x: 0, y: 1, w: 3, h: 2 },
136+
options: {
137+
xField: 'month',
138+
yField: 'revenue'
139+
},
140+
// @ts-ignore
141+
data: {
142+
provider: 'value',
143+
items: [
144+
{ month: 'Jan', revenue: 45000 },
145+
{ month: 'Feb', revenue: 52000 },
146+
{ month: 'Mar', revenue: 48000 },
147+
{ month: 'Apr', revenue: 61000 },
148+
{ month: 'May', revenue: 55000 },
149+
{ month: 'Jun', revenue: 67000 },
150+
{ month: 'Jul', revenue: 72000 }
151+
]
152+
}
153+
},
154+
{
155+
title: 'Lead Source',
156+
type: 'donut',
157+
layout: { x: 3, y: 1, w: 1, h: 2 },
158+
options: {
159+
xField: 'source',
160+
yField: 'value'
161+
},
162+
// @ts-ignore
163+
data: {
164+
provider: 'value',
165+
items: [
166+
{ source: 'Website', value: 45 },
167+
{ source: 'Referral', value: 25 },
168+
{ source: 'Partner', value: 20 },
169+
{ source: 'Ads', value: 10 }
170+
]
171+
}
172+
},
173+
174+
// --- Row 3: More Charts ---
85175
{
86176
title: 'Pipeline by Stage',
87177
type: 'bar',
88-
layout: { x: 0, y: 0, w: 2, h: 2 },
178+
layout: { x: 0, y: 3, w: 2, h: 2 },
89179
options: {
90180
xField: 'stage',
91181
yField: 'amount'
@@ -103,44 +193,47 @@ export default defineStack({
103193
}
104194
},
105195
{
106-
title: 'Recent Opportunities',
107-
type: 'table',
108-
layout: { x: 2, y: 0, w: 2, h: 2 },
196+
title: 'Top Products',
197+
type: 'bar',
198+
layout: { x: 2, y: 3, w: 2, h: 2 },
109199
options: {
110-
columns: [
111-
{ header: 'Opportunity Name', accessorKey: 'name' },
112-
{ header: 'Amount', accessorKey: 'amount' },
113-
{ header: 'Stage', accessorKey: 'stage' }
114-
]
200+
xField: 'name',
201+
yField: 'sales'
115202
},
116203
// @ts-ignore
117204
data: {
118205
provider: 'value',
119206
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' }
207+
{ name: 'Enterprise License', sales: 450 },
208+
{ name: 'Pro Subscription', sales: 320 },
209+
{ name: 'Basic Plan', sales: 210 },
210+
{ name: 'Consulting Hours', sales: 150 }
125211
]
126212
}
127213
},
214+
215+
// --- Row 4: Table ---
128216
{
129-
title: 'Revenue Trends',
130-
type: 'line',
131-
layout: { x: 0, y: 2, w: 4, h: 2 },
217+
title: 'Recent Opportunities',
218+
type: 'table',
219+
layout: { x: 0, y: 5, w: 4, h: 2 },
132220
options: {
133-
xField: 'month',
134-
yField: 'revenue'
221+
columns: [
222+
{ header: 'Opportunity Name', accessorKey: 'name' },
223+
{ header: 'Amount', accessorKey: 'amount' },
224+
{ header: 'Stage', accessorKey: 'stage' },
225+
{ header: 'Close Date', accessorKey: 'date' }
226+
]
135227
},
136228
// @ts-ignore
137229
data: {
138230
provider: 'value',
139231
items: [
140-
{ month: 'Jan', revenue: 45000 },
141-
{ month: 'Feb', revenue: 52000 },
142-
{ month: 'Mar', revenue: 48000 },
143-
{ month: 'Apr', revenue: 61000 }
232+
{ name: 'TechCorp License', amount: '$50,000', stage: 'Proposal', date: '2024-06-30' },
233+
{ name: 'Software Inc Pilot', amount: '$5,000', stage: 'Closed Won', date: '2024-01-15' },
234+
{ name: 'Consulting Q2', amount: '$12,000', stage: 'Negotiation', date: '2024-05-20' },
235+
{ name: 'Global Widget Deal', amount: '$85,000', stage: 'Qualification', date: '2024-07-10' },
236+
{ name: 'Startup Bundle', amount: '$2,500', stage: 'Prospecting', date: '2024-08-01' }
144237
]
145238
}
146239
}

packages/plugin-dashboard/src/MetricWidget.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import { Card, CardContent, CardHeader, CardTitle } from '@object-ui/components';
33
import { cn } from '@object-ui/components';
44
import { ArrowDownIcon, ArrowUpIcon, MinusIcon } from 'lucide-react';
5+
import * as LucideIcons from 'lucide-react';
56

67
export interface MetricWidgetProps {
78
label: string;
@@ -11,7 +12,7 @@ export interface MetricWidgetProps {
1112
label?: string;
1213
direction?: 'up' | 'down' | 'neutral';
1314
};
14-
icon?: React.ReactNode;
15+
icon?: React.ReactNode | string;
1516
className?: string;
1617
description?: string;
1718
}
@@ -25,13 +26,22 @@ export const MetricWidget = ({
2526
description,
2627
...props
2728
}: MetricWidgetProps) => {
29+
// Resolve icon if it's a string
30+
const resolvedIcon = useMemo(() => {
31+
if (typeof icon === 'string') {
32+
const IconComponent = (LucideIcons as any)[icon];
33+
return IconComponent ? <IconComponent className="h-4 w-4 text-muted-foreground" /> : null;
34+
}
35+
return icon;
36+
}, [icon]);
37+
2838
return (
2939
<Card className={cn("h-full", className)} {...props}>
3040
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
3141
<CardTitle className="text-sm font-medium">
3242
{label}
3343
</CardTitle>
34-
{icon && <div className="h-4 w-4 text-muted-foreground">{icon}</div>}
44+
{resolvedIcon && <div className="h-4 w-4 text-muted-foreground">{resolvedIcon}</div>}
3545
</CardHeader>
3646
<CardContent>
3747
<div className="text-2xl font-bold">{value}</div>

0 commit comments

Comments
 (0)