@nextsparkjs/theme-crm 0.1.0-beta.1

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.
Files changed (140) hide show
  1. package/CRM_PLAN.md +343 -0
  2. package/about.md +122 -0
  3. package/config/app.config.ts +185 -0
  4. package/config/billing.config.ts +187 -0
  5. package/config/dashboard.config.ts +372 -0
  6. package/config/dev.config.ts +55 -0
  7. package/config/features.config.ts +336 -0
  8. package/config/flows.config.ts +511 -0
  9. package/config/permissions.config.ts +297 -0
  10. package/config/theme.config.ts +111 -0
  11. package/entities/activities/activities.config.ts +61 -0
  12. package/entities/activities/activities.fields.ts +362 -0
  13. package/entities/activities/activities.service.ts +503 -0
  14. package/entities/activities/activities.types.ts +117 -0
  15. package/entities/activities/messages/en.json +123 -0
  16. package/entities/activities/messages/es.json +123 -0
  17. package/entities/activities/migrations/020_activities_table.sql +123 -0
  18. package/entities/activities/migrations/021_activities_metas.sql +114 -0
  19. package/entities/activities/migrations/022_activities_sample_data.sql +420 -0
  20. package/entities/campaigns/campaigns.config.ts +61 -0
  21. package/entities/campaigns/campaigns.fields.ts +413 -0
  22. package/entities/campaigns/campaigns.service.ts +426 -0
  23. package/entities/campaigns/campaigns.types.ts +124 -0
  24. package/entities/campaigns/messages/en.json +145 -0
  25. package/entities/campaigns/messages/es.json +145 -0
  26. package/entities/campaigns/migrations/001_campaigns_table.sql +127 -0
  27. package/entities/campaigns/migrations/002_campaigns_metas.sql +114 -0
  28. package/entities/campaigns/migrations/003_campaigns_sample_data.sql +364 -0
  29. package/entities/companies/companies.config.ts +61 -0
  30. package/entities/companies/companies.fields.ts +429 -0
  31. package/entities/companies/companies.service.ts +566 -0
  32. package/entities/companies/companies.types.ts +125 -0
  33. package/entities/companies/messages/en.json +146 -0
  34. package/entities/companies/messages/es.json +146 -0
  35. package/entities/companies/migrations/001_companies_table.sql +150 -0
  36. package/entities/companies/migrations/002_companies_metas.sql +114 -0
  37. package/entities/companies/migrations/003_companies_sample_data.sql +246 -0
  38. package/entities/contacts/contacts.config.ts +61 -0
  39. package/entities/contacts/contacts.fields.ts +359 -0
  40. package/entities/contacts/contacts.service.ts +509 -0
  41. package/entities/contacts/contacts.types.ts +108 -0
  42. package/entities/contacts/messages/en.json +117 -0
  43. package/entities/contacts/messages/es.json +117 -0
  44. package/entities/contacts/migrations/001_contacts_table.sql +134 -0
  45. package/entities/contacts/migrations/002_contacts_metas.sql +114 -0
  46. package/entities/contacts/migrations/003_contacts_sample_data.sql +421 -0
  47. package/entities/leads/leads.config.ts +61 -0
  48. package/entities/leads/leads.fields.ts +336 -0
  49. package/entities/leads/leads.service.ts +496 -0
  50. package/entities/leads/leads.types.ts +114 -0
  51. package/entities/leads/messages/en.json +132 -0
  52. package/entities/leads/messages/es.json +132 -0
  53. package/entities/leads/migrations/001_leads_table.sql +150 -0
  54. package/entities/leads/migrations/002_leads_metas.sql +120 -0
  55. package/entities/leads/migrations/003_leads_sample_data.sql +242 -0
  56. package/entities/notes/messages/en.json +114 -0
  57. package/entities/notes/messages/es.json +114 -0
  58. package/entities/notes/migrations/020_notes_table.sql +118 -0
  59. package/entities/notes/migrations/021_notes_metas.sql +114 -0
  60. package/entities/notes/migrations/022_notes_sample_data.sql +275 -0
  61. package/entities/notes/notes.config.ts +61 -0
  62. package/entities/notes/notes.fields.ts +283 -0
  63. package/entities/notes/notes.service.ts +320 -0
  64. package/entities/notes/notes.types.ts +102 -0
  65. package/entities/opportunities/messages/en.json +107 -0
  66. package/entities/opportunities/messages/es.json +107 -0
  67. package/entities/opportunities/migrations/010_opportunities_table.sql +145 -0
  68. package/entities/opportunities/migrations/011_opportunities_metas.sql +114 -0
  69. package/entities/opportunities/migrations/012_opportunities_sample_data.sql +438 -0
  70. package/entities/opportunities/opportunities.config.ts +61 -0
  71. package/entities/opportunities/opportunities.fields.ts +416 -0
  72. package/entities/opportunities/opportunities.service.ts +525 -0
  73. package/entities/opportunities/opportunities.types.ts +135 -0
  74. package/entities/pipelines/messages/en.json +115 -0
  75. package/entities/pipelines/messages/es.json +115 -0
  76. package/entities/pipelines/migrations/001_pipelines_table.sql +106 -0
  77. package/entities/pipelines/migrations/002_pipelines_metas.sql +114 -0
  78. package/entities/pipelines/migrations/003_pipelines_sample_data.sql +91 -0
  79. package/entities/pipelines/pipelines.config.ts +62 -0
  80. package/entities/pipelines/pipelines.fields.ts +193 -0
  81. package/entities/pipelines/pipelines.service.ts +383 -0
  82. package/entities/pipelines/pipelines.types.ts +78 -0
  83. package/entities/products/messages/en.json +135 -0
  84. package/entities/products/messages/es.json +135 -0
  85. package/entities/products/migrations/001_products_table.sql +117 -0
  86. package/entities/products/migrations/002_products_metas.sql +114 -0
  87. package/entities/products/migrations/003_products_sample_data.sql +247 -0
  88. package/entities/products/products.config.ts +62 -0
  89. package/entities/products/products.fields.ts +361 -0
  90. package/entities/products/products.service.ts +437 -0
  91. package/entities/products/products.types.ts +125 -0
  92. package/lib/crm-constants.ts +77 -0
  93. package/lib/crm-utils.ts +185 -0
  94. package/lib/selectors.ts +333 -0
  95. package/messages/en.json +131 -0
  96. package/messages/es.json +131 -0
  97. package/migrations/999_theme_sample_data.sql +473 -0
  98. package/package.json +18 -0
  99. package/pendings.md +205 -0
  100. package/permissions-matrix.md +216 -0
  101. package/styles/components.css +414 -0
  102. package/styles/crm-theme.css +358 -0
  103. package/styles/globals.css +576 -0
  104. package/styles/variables.css +111 -0
  105. package/templates/dashboard/(main)/activities/components/ActivityCard.tsx +169 -0
  106. package/templates/dashboard/(main)/activities/components/ActivityTimeline.tsx +165 -0
  107. package/templates/dashboard/(main)/activities/page.tsx +297 -0
  108. package/templates/dashboard/(main)/campaigns/page.tsx +373 -0
  109. package/templates/dashboard/(main)/companies/page.tsx +296 -0
  110. package/templates/dashboard/(main)/contacts/page.tsx +347 -0
  111. package/templates/dashboard/(main)/layout.tsx +98 -0
  112. package/templates/dashboard/(main)/leads/page.tsx +335 -0
  113. package/templates/dashboard/(main)/opportunities/[id]/edit/page.tsx +95 -0
  114. package/templates/dashboard/(main)/opportunities/create/page.tsx +94 -0
  115. package/templates/dashboard/(main)/opportunities/page.tsx +350 -0
  116. package/templates/dashboard/(main)/pipelines/[id]/edit/page.tsx +95 -0
  117. package/templates/dashboard/(main)/pipelines/[id]/page.tsx +143 -0
  118. package/templates/dashboard/(main)/pipelines/create/page.tsx +94 -0
  119. package/templates/dashboard/(main)/pipelines/page.tsx +234 -0
  120. package/templates/dashboard/(main)/products/[id]/edit/page.tsx +97 -0
  121. package/templates/dashboard/(main)/products/[id]/page.tsx +509 -0
  122. package/templates/dashboard/(main)/products/create/page.tsx +96 -0
  123. package/templates/dashboard/(main)/products/page.tsx +308 -0
  124. package/templates/shared/ActionButtons.tsx +41 -0
  125. package/templates/shared/CRMDashboard.tsx +519 -0
  126. package/templates/shared/CRMDataTable.tsx +441 -0
  127. package/templates/shared/CRMMetricCard.tsx +76 -0
  128. package/templates/shared/CRMMobileNav.tsx +172 -0
  129. package/templates/shared/CRMSidebar.tsx +346 -0
  130. package/templates/shared/CRMTopBar.tsx +265 -0
  131. package/templates/shared/DealCard.tsx +123 -0
  132. package/templates/shared/EntityCard.tsx +58 -0
  133. package/templates/shared/OpportunityForm.tsx +649 -0
  134. package/templates/shared/PipelineForm.tsx +367 -0
  135. package/templates/shared/PipelineKanban.tsx +194 -0
  136. package/templates/shared/QuickFilters.tsx +47 -0
  137. package/templates/shared/StageColumn.tsx +175 -0
  138. package/templates/shared/StageSelect.tsx +177 -0
  139. package/templates/shared/StagesRepeater.tsx +317 -0
  140. package/templates/shared/index.ts +9 -0
@@ -0,0 +1,519 @@
1
+ /**
2
+ * CRM Dashboard Component
3
+ * Professional dashboard with KPIs and Pipeline Summary
4
+ *
5
+ * Use this component in your dashboard page to display CRM metrics
6
+ */
7
+
8
+ 'use client'
9
+
10
+ import { useEffect, useState } from 'react'
11
+ import Link from 'next/link'
12
+ import { Button } from '@nextsparkjs/core/components/ui/button'
13
+ import {
14
+ DollarSign,
15
+ Users,
16
+ UserPlus,
17
+ Target,
18
+ TrendingUp,
19
+ TrendingDown,
20
+ ArrowRight,
21
+ Calendar,
22
+ CheckCircle2,
23
+ Clock,
24
+ AlertCircle,
25
+ Flame,
26
+ Building2,
27
+ Activity,
28
+ BarChart3
29
+ } from 'lucide-react'
30
+ import { cn } from '@nextsparkjs/core/lib/utils'
31
+
32
+ // Types
33
+ interface KPIData {
34
+ totalRevenue: number
35
+ revenueTrend: number
36
+ totalDeals: number
37
+ dealsTrend: number
38
+ activeLeads: number
39
+ leadsTrend: number
40
+ conversionRate: number
41
+ conversionTrend: number
42
+ }
43
+
44
+ interface PipelineStage {
45
+ id: string
46
+ name: string
47
+ value: number
48
+ count: number
49
+ color: string
50
+ }
51
+
52
+ interface ActivityItem {
53
+ id: string
54
+ type: 'call' | 'email' | 'meeting' | 'task'
55
+ title: string
56
+ time: string
57
+ status: 'scheduled' | 'completed' | 'overdue'
58
+ }
59
+
60
+ interface Deal {
61
+ id: string
62
+ name: string
63
+ value: number
64
+ company?: string
65
+ probability: number
66
+ stage: string
67
+ }
68
+
69
+ // KPI Card component
70
+ function KPICard({
71
+ title,
72
+ value,
73
+ trend,
74
+ icon: Icon,
75
+ iconBg,
76
+ prefix = '',
77
+ suffix = '',
78
+ }: {
79
+ title: string
80
+ value: number | string
81
+ trend?: number
82
+ icon: React.ElementType
83
+ iconBg: string
84
+ prefix?: string
85
+ suffix?: string
86
+ }) {
87
+ const isPositive = trend && trend > 0
88
+ const TrendIcon = isPositive ? TrendingUp : TrendingDown
89
+
90
+ return (
91
+ <div className="bg-card border rounded-xl p-5 transition-all hover:shadow-md hover:border-primary/20">
92
+ <div className="flex items-start justify-between">
93
+ <div className={cn('w-11 h-11 rounded-xl flex items-center justify-center', iconBg)}>
94
+ <Icon className="w-5 h-5" />
95
+ </div>
96
+ {trend !== undefined && (
97
+ <div className={cn(
98
+ 'flex items-center gap-1 text-sm font-medium px-2 py-0.5 rounded-full',
99
+ isPositive
100
+ ? 'bg-emerald-500/10 text-emerald-600'
101
+ : 'bg-destructive/10 text-destructive'
102
+ )}>
103
+ <TrendIcon className="w-3.5 h-3.5" />
104
+ <span>{Math.abs(trend)}%</span>
105
+ </div>
106
+ )}
107
+ </div>
108
+ <div className="mt-4">
109
+ <p className="text-2xl font-bold text-foreground">
110
+ {prefix}{typeof value === 'number' ? value.toLocaleString() : value}{suffix}
111
+ </p>
112
+ <p className="text-sm text-muted-foreground mt-0.5">{title}</p>
113
+ </div>
114
+ </div>
115
+ )
116
+ }
117
+
118
+ // Pipeline Funnel component
119
+ function PipelineFunnel({ stages }: { stages: PipelineStage[] }) {
120
+ const maxValue = Math.max(...stages.map(s => s.value), 1)
121
+
122
+ return (
123
+ <div className="bg-card border rounded-xl p-5">
124
+ <div className="flex items-center justify-between mb-5">
125
+ <div>
126
+ <h3 className="font-semibold text-foreground">Pipeline Overview</h3>
127
+ <p className="text-sm text-muted-foreground">Current deal distribution</p>
128
+ </div>
129
+ <Link href="/dashboard/pipelines">
130
+ <Button variant="ghost" size="sm" className="gap-1">
131
+ View All
132
+ <ArrowRight className="w-4 h-4" />
133
+ </Button>
134
+ </Link>
135
+ </div>
136
+
137
+ <div className="space-y-3">
138
+ {stages.map((stage) => (
139
+ <div key={stage.id} className="group">
140
+ <div className="flex items-center justify-between text-sm mb-1.5">
141
+ <span className="font-medium text-foreground">{stage.name}</span>
142
+ <span className="text-muted-foreground">
143
+ {stage.count} deals · ${stage.value.toLocaleString()}
144
+ </span>
145
+ </div>
146
+ <div className="h-8 bg-muted/50 rounded-lg overflow-hidden relative">
147
+ <div
148
+ className={cn(
149
+ 'h-full rounded-lg transition-all duration-500 ease-out',
150
+ stage.color
151
+ )}
152
+ style={{
153
+ width: `${Math.max((stage.value / maxValue) * 100, 5)}%`,
154
+ }}
155
+ />
156
+ <div className="absolute inset-0 flex items-center px-3">
157
+ <span className="text-xs font-medium text-white drop-shadow-sm">
158
+ ${stage.value.toLocaleString()}
159
+ </span>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ ))}
164
+ </div>
165
+
166
+ {/* Summary */}
167
+ <div className="mt-5 pt-4 border-t border-border">
168
+ <div className="flex items-center justify-between">
169
+ <div>
170
+ <p className="text-sm text-muted-foreground">Total Pipeline Value</p>
171
+ <p className="text-xl font-bold text-foreground">
172
+ ${stages.reduce((sum, s) => sum + s.value, 0).toLocaleString()}
173
+ </p>
174
+ </div>
175
+ <div className="text-right">
176
+ <p className="text-sm text-muted-foreground">Total Deals</p>
177
+ <p className="text-xl font-bold text-foreground">
178
+ {stages.reduce((sum, s) => sum + s.count, 0)}
179
+ </p>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ )
185
+ }
186
+
187
+ // Activity status icon
188
+ function ActivityStatusIcon({ status }: { status: ActivityItem['status'] }) {
189
+ if (status === 'completed') {
190
+ return <CheckCircle2 className="w-4 h-4 text-emerald-600" />
191
+ }
192
+ if (status === 'overdue') {
193
+ return <AlertCircle className="w-4 h-4 text-destructive" />
194
+ }
195
+ return <Clock className="w-4 h-4 text-amber-600" />
196
+ }
197
+
198
+ // Recent Activities component
199
+ function RecentActivities({ activities }: { activities: ActivityItem[] }) {
200
+ const typeIcons = {
201
+ call: '📞',
202
+ email: '✉️',
203
+ meeting: '📅',
204
+ task: '✓',
205
+ }
206
+
207
+ return (
208
+ <div className="bg-card border rounded-xl p-5">
209
+ <div className="flex items-center justify-between mb-5">
210
+ <div>
211
+ <h3 className="font-semibold text-foreground">Upcoming Activities</h3>
212
+ <p className="text-sm text-muted-foreground">Your schedule for today</p>
213
+ </div>
214
+ <Link href="/dashboard/activities">
215
+ <Button variant="ghost" size="sm" className="gap-1">
216
+ View All
217
+ <ArrowRight className="w-4 h-4" />
218
+ </Button>
219
+ </Link>
220
+ </div>
221
+
222
+ <div className="space-y-3">
223
+ {activities.length > 0 ? (
224
+ activities.map((activity) => (
225
+ <div
226
+ key={activity.id}
227
+ className="flex items-center gap-3 p-3 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors cursor-pointer"
228
+ >
229
+ <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center text-sm">
230
+ {typeIcons[activity.type]}
231
+ </div>
232
+ <div className="flex-1 min-w-0">
233
+ <p className="font-medium text-foreground text-sm truncate">
234
+ {activity.title}
235
+ </p>
236
+ <p className="text-xs text-muted-foreground">{activity.time}</p>
237
+ </div>
238
+ <ActivityStatusIcon status={activity.status} />
239
+ </div>
240
+ ))
241
+ ) : (
242
+ <div className="py-8 text-center">
243
+ <Calendar className="w-8 h-8 mx-auto text-muted-foreground mb-2" />
244
+ <p className="text-sm text-muted-foreground">No activities scheduled</p>
245
+ </div>
246
+ )}
247
+ </div>
248
+ </div>
249
+ )
250
+ }
251
+
252
+ // Hot Deals component
253
+ function HotDeals({ deals }: { deals: Deal[] }) {
254
+ return (
255
+ <div className="bg-card border rounded-xl p-5">
256
+ <div className="flex items-center justify-between mb-5">
257
+ <div className="flex items-center gap-2">
258
+ <Flame className="w-5 h-5 text-destructive" />
259
+ <div>
260
+ <h3 className="font-semibold text-foreground">Hot Deals</h3>
261
+ <p className="text-sm text-muted-foreground">High-value opportunities</p>
262
+ </div>
263
+ </div>
264
+ <Link href="/dashboard/pipelines">
265
+ <Button variant="ghost" size="sm" className="gap-1">
266
+ View All
267
+ <ArrowRight className="w-4 h-4" />
268
+ </Button>
269
+ </Link>
270
+ </div>
271
+
272
+ <div className="space-y-3">
273
+ {deals.length > 0 ? (
274
+ deals.map((deal) => (
275
+ <div
276
+ key={deal.id}
277
+ className="flex items-center gap-3 p-3 rounded-lg border border-border hover:border-primary/30 hover:shadow-sm transition-all cursor-pointer"
278
+ >
279
+ <div className="flex-1 min-w-0">
280
+ <p className="font-medium text-foreground text-sm truncate">
281
+ {deal.name}
282
+ </p>
283
+ <div className="flex items-center gap-2 mt-1">
284
+ {deal.company && (
285
+ <span className="flex items-center gap-1 text-xs text-muted-foreground">
286
+ <Building2 className="w-3 h-3" />
287
+ {deal.company}
288
+ </span>
289
+ )}
290
+ <span className="text-xs text-muted-foreground">
291
+ {deal.stage}
292
+ </span>
293
+ </div>
294
+ </div>
295
+ <div className="text-right">
296
+ <p className="font-semibold text-primary text-sm">
297
+ ${deal.value.toLocaleString()}
298
+ </p>
299
+ <p className="text-xs text-muted-foreground">
300
+ {deal.probability}% likely
301
+ </p>
302
+ </div>
303
+ </div>
304
+ ))
305
+ ) : (
306
+ <div className="py-8 text-center">
307
+ <Target className="w-8 h-8 mx-auto text-muted-foreground mb-2" />
308
+ <p className="text-sm text-muted-foreground">No hot deals yet</p>
309
+ </div>
310
+ )}
311
+ </div>
312
+ </div>
313
+ )
314
+ }
315
+
316
+ // Quick Stats Bar
317
+ function QuickStatsBar() {
318
+ return (
319
+ <div className="bg-gradient-to-r from-primary/5 via-primary/10 to-primary/5 border border-primary/10 rounded-xl p-4">
320
+ <div className="flex items-center justify-between flex-wrap gap-4">
321
+ <div className="flex items-center gap-3">
322
+ <div className="w-10 h-10 rounded-xl bg-primary/20 flex items-center justify-center">
323
+ <Activity className="w-5 h-5 text-primary" />
324
+ </div>
325
+ <div>
326
+ <p className="text-sm font-medium text-foreground">Welcome back!</p>
327
+ <p className="text-xs text-muted-foreground">Here's what's happening with your sales today.</p>
328
+ </div>
329
+ </div>
330
+ <div className="flex items-center gap-3">
331
+ <Link href="/dashboard/leads/create">
332
+ <Button size="sm" variant="outline" className="gap-2">
333
+ <UserPlus className="w-4 h-4" />
334
+ Add Lead
335
+ </Button>
336
+ </Link>
337
+ <Link href="/dashboard/pipelines">
338
+ <Button size="sm" className="gap-2">
339
+ <BarChart3 className="w-4 h-4" />
340
+ View Pipeline
341
+ </Button>
342
+ </Link>
343
+ </div>
344
+ </div>
345
+ </div>
346
+ )
347
+ }
348
+
349
+ // Sales Performance Chart
350
+ function SalesPerformanceChart() {
351
+ return (
352
+ <div className="bg-card border rounded-xl p-5">
353
+ <div className="flex items-center justify-between mb-5">
354
+ <div>
355
+ <h3 className="font-semibold text-foreground">Sales Performance</h3>
356
+ <p className="text-sm text-muted-foreground">Monthly revenue trend</p>
357
+ </div>
358
+ <Button variant="ghost" size="sm">
359
+ This Month
360
+ </Button>
361
+ </div>
362
+
363
+ {/* Simple bar chart visualization */}
364
+ <div className="flex items-end gap-2 h-40 mt-4">
365
+ {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'].map((month, i) => {
366
+ const heights = [45, 60, 55, 75, 85, 70]
367
+ return (
368
+ <div key={month} className="flex-1 flex flex-col items-center gap-2">
369
+ <div
370
+ className="w-full bg-primary/80 rounded-t-md transition-all hover:bg-primary"
371
+ style={{ height: `${heights[i]}%` }}
372
+ />
373
+ <span className="text-xs text-muted-foreground">{month}</span>
374
+ </div>
375
+ )
376
+ })}
377
+ </div>
378
+
379
+ <div className="mt-4 pt-4 border-t border-border flex items-center justify-between">
380
+ <div>
381
+ <p className="text-sm text-muted-foreground">vs. Last Month</p>
382
+ <p className="text-lg font-semibold text-emerald-600">+18.2%</p>
383
+ </div>
384
+ <div className="text-right">
385
+ <p className="text-sm text-muted-foreground">Forecast</p>
386
+ <p className="text-lg font-semibold text-foreground">$312,000</p>
387
+ </div>
388
+ </div>
389
+ </div>
390
+ )
391
+ }
392
+
393
+ // Main CRM Dashboard Component
394
+ export interface CRMDashboardProps {
395
+ kpis?: KPIData
396
+ pipelineStages?: PipelineStage[]
397
+ activities?: ActivityItem[]
398
+ hotDeals?: Deal[]
399
+ isLoading?: boolean
400
+ }
401
+
402
+ export function CRMDashboard({
403
+ kpis,
404
+ pipelineStages,
405
+ activities,
406
+ hotDeals,
407
+ isLoading = false,
408
+ }: CRMDashboardProps) {
409
+ // Default mock data if not provided
410
+ const defaultKPIs: KPIData = kpis || {
411
+ totalRevenue: 284500,
412
+ revenueTrend: 12.5,
413
+ totalDeals: 47,
414
+ dealsTrend: 8,
415
+ activeLeads: 156,
416
+ leadsTrend: -3,
417
+ conversionRate: 24,
418
+ conversionTrend: 5,
419
+ }
420
+
421
+ const defaultPipelineStages: PipelineStage[] = pipelineStages || [
422
+ { id: '1', name: 'Discovery', value: 125000, count: 18, color: 'bg-violet-500' },
423
+ { id: '2', name: 'Qualification', value: 89000, count: 12, color: 'bg-primary' },
424
+ { id: '3', name: 'Proposal', value: 67000, count: 8, color: 'bg-amber-500' },
425
+ { id: '4', name: 'Negotiation', value: 45000, count: 5, color: 'bg-orange-500' },
426
+ { id: '5', name: 'Closed Won', value: 284500, count: 4, color: 'bg-emerald-500' },
427
+ ]
428
+
429
+ const defaultActivities: ActivityItem[] = activities || [
430
+ { id: '1', type: 'call', title: 'Follow up with Acme Corp', time: '10:00 AM', status: 'scheduled' },
431
+ { id: '2', type: 'meeting', title: 'Demo with Tech Solutions', time: '2:00 PM', status: 'scheduled' },
432
+ { id: '3', type: 'email', title: 'Send proposal to GlobalTech', time: '4:00 PM', status: 'scheduled' },
433
+ { id: '4', type: 'task', title: 'Update CRM records', time: 'Yesterday', status: 'overdue' },
434
+ ]
435
+
436
+ const defaultHotDeals: Deal[] = hotDeals || [
437
+ { id: '1', name: 'Enterprise License Deal', value: 85000, company: 'Acme Corp', probability: 85, stage: 'Negotiation' },
438
+ { id: '2', name: 'Annual Subscription', value: 45000, company: 'Tech Solutions', probability: 75, stage: 'Proposal' },
439
+ { id: '3', name: 'Consulting Package', value: 32000, company: 'GlobalTech', probability: 70, stage: 'Qualification' },
440
+ ]
441
+
442
+ if (isLoading) {
443
+ return (
444
+ <div className="p-6 space-y-6">
445
+ <div className="h-16 bg-muted animate-pulse rounded-xl" />
446
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
447
+ {[1, 2, 3, 4].map(i => (
448
+ <div key={i} className="h-32 bg-muted animate-pulse rounded-xl" />
449
+ ))}
450
+ </div>
451
+ <div className="grid lg:grid-cols-2 gap-6">
452
+ <div className="h-80 bg-muted animate-pulse rounded-xl" />
453
+ <div className="h-80 bg-muted animate-pulse rounded-xl" />
454
+ </div>
455
+ </div>
456
+ )
457
+ }
458
+
459
+ return (
460
+ <div className="p-6 space-y-6">
461
+ {/* Quick Stats Bar */}
462
+ <QuickStatsBar />
463
+
464
+ {/* KPI Cards */}
465
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
466
+ <KPICard
467
+ title="Total Revenue"
468
+ value={defaultKPIs.totalRevenue}
469
+ trend={defaultKPIs.revenueTrend}
470
+ icon={DollarSign}
471
+ iconBg="bg-emerald-500/10 text-emerald-600"
472
+ prefix="$"
473
+ />
474
+ <KPICard
475
+ title="Active Deals"
476
+ value={defaultKPIs.totalDeals}
477
+ trend={defaultKPIs.dealsTrend}
478
+ icon={Target}
479
+ iconBg="bg-primary/10 text-primary"
480
+ />
481
+ <KPICard
482
+ title="Active Leads"
483
+ value={defaultKPIs.activeLeads}
484
+ trend={defaultKPIs.leadsTrend}
485
+ icon={Users}
486
+ iconBg="bg-amber-500/10 text-amber-600"
487
+ />
488
+ <KPICard
489
+ title="Conversion Rate"
490
+ value={defaultKPIs.conversionRate}
491
+ trend={defaultKPIs.conversionTrend}
492
+ icon={TrendingUp}
493
+ iconBg="bg-violet-500/10 text-violet-600"
494
+ suffix="%"
495
+ />
496
+ </div>
497
+
498
+ {/* Main Content Grid */}
499
+ <div className="grid lg:grid-cols-2 gap-6">
500
+ {/* Pipeline Funnel */}
501
+ <PipelineFunnel stages={defaultPipelineStages} />
502
+
503
+ {/* Hot Deals */}
504
+ <HotDeals deals={defaultHotDeals} />
505
+ </div>
506
+
507
+ {/* Bottom Section */}
508
+ <div className="grid lg:grid-cols-2 gap-6">
509
+ {/* Recent Activities */}
510
+ <RecentActivities activities={defaultActivities} />
511
+
512
+ {/* Performance Chart */}
513
+ <SalesPerformanceChart />
514
+ </div>
515
+ </div>
516
+ )
517
+ }
518
+
519
+ export default CRMDashboard