xertica-ui 2.1.2 → 2.1.4

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 (181) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +1 -1
  3. package/bin/cli.ts +1 -1
  4. package/bin/generate-tokens.ts +13 -7
  5. package/components/assistant/xertica-assistant/index.ts +2 -0
  6. package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +97 -0
  7. package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -0
  8. package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -0
  9. package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +86 -0
  10. package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +77 -0
  11. package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +573 -0
  12. package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +65 -0
  13. package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -0
  14. package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +98 -0
  15. package/components/assistant/xertica-assistant/parts/index.ts +16 -0
  16. package/components/assistant/xertica-assistant/types.ts +139 -0
  17. package/components/assistant/xertica-assistant/use-assistant.ts +559 -0
  18. package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
  19. package/components/assistant/xertica-assistant/xertica-assistant.tsx +198 -1460
  20. package/components/brand/theme-toggle/ThemeToggle.tsx +8 -27
  21. package/components/hooks/index.ts +3 -0
  22. package/components/hooks/use-layout-shortcuts.ts +46 -0
  23. package/components/layout/sidebar/index.ts +2 -0
  24. package/components/layout/sidebar/sidebar.stories.tsx +160 -8
  25. package/components/layout/sidebar/sidebar.tsx +606 -497
  26. package/components/layout/sidebar/use-sidebar.ts +104 -0
  27. package/components/media/audio-player/AudioPlayer.tsx +131 -206
  28. package/components/media/audio-player/use-audio-player.ts +298 -0
  29. package/components/pages/home-page/HomePage.tsx +1 -1
  30. package/components/pages/template-content/TemplateContent.tsx +5 -5
  31. package/components/pages/template-page/TemplatePage.tsx +5 -5
  32. package/components/shared/CustomTooltipContent.tsx +52 -0
  33. package/components/shared/layout-constants.ts +1 -1
  34. package/components/ui/chart/chart.stories.tsx +966 -7
  35. package/components/ui/chart/chart.tsx +918 -45
  36. package/components/ui/file-upload/file-upload.stories.tsx +100 -0
  37. package/components/ui/file-upload/file-upload.tsx +14 -74
  38. package/components/ui/file-upload/index.ts +1 -0
  39. package/components/ui/file-upload/use-file-upload.ts +181 -0
  40. package/components/ui/pagination/index.ts +2 -0
  41. package/components/ui/pagination/pagination.stories.tsx +94 -0
  42. package/components/ui/pagination/use-pagination.ts +194 -0
  43. package/components/ui/rich-text-editor/index.ts +2 -0
  44. package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
  45. package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
  46. package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
  47. package/components/ui/stepper/index.ts +3 -1
  48. package/components/ui/stepper/stepper.stories.tsx +116 -0
  49. package/components/ui/stepper/stepper.tsx +4 -4
  50. package/components/ui/stepper/use-stepper.ts +137 -0
  51. package/components/ui/tree-view/index.ts +4 -1
  52. package/components/ui/tree-view/tree-view.stories.tsx +110 -4
  53. package/components/ui/tree-view/tree-view.tsx +17 -125
  54. package/components/ui/tree-view/use-tree-view.ts +229 -0
  55. package/contexts/AssistenteContext.tsx +17 -54
  56. package/contexts/BrandColorsContext.tsx +6 -17
  57. package/contexts/LayoutContext.tsx +5 -31
  58. package/dist/AssistantChart-BAudAfne.cjs +3591 -0
  59. package/dist/AssistantChart-BP8upjMk.js +3565 -0
  60. package/dist/AudioPlayer-1ypwE2Wh.cjs +936 -0
  61. package/dist/AudioPlayer-DuKXrCfy.js +937 -0
  62. package/dist/CustomTooltipContent-DHjkY0ww.js +40 -0
  63. package/dist/CustomTooltipContent-c_K-DWRr.cjs +56 -0
  64. package/dist/LanguageContext-BwhwC3G2.js +657 -0
  65. package/dist/LanguageContext-DvUt5jBg.cjs +656 -0
  66. package/dist/LayoutContext-BDmcZfMH.cjs +84 -0
  67. package/dist/LayoutContext-dbQvdC4O.js +85 -0
  68. package/dist/ThemeContext-RTy1m2Uq.js +82 -0
  69. package/dist/ThemeContext-bSzuOit2.cjs +81 -0
  70. package/dist/VerifyEmailPage-C_ihbcth.js +2828 -0
  71. package/dist/VerifyEmailPage-Dt7zgA4w.cjs +2827 -0
  72. package/dist/XerticaProvider-CW9hpCdF.cjs +39 -0
  73. package/dist/XerticaProvider-siSt9uG2.js +40 -0
  74. package/dist/XerticaXLogo-D8jf0SNv.cjs +214 -0
  75. package/dist/XerticaXLogo-fAJMy3H4.js +215 -0
  76. package/dist/assistant.cjs.js +2 -1
  77. package/dist/assistant.es.js +3 -2
  78. package/dist/brand.cjs.js +2 -2
  79. package/dist/brand.es.js +2 -2
  80. package/dist/cli.js +14 -8
  81. package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
  82. package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +13 -0
  83. package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +16 -0
  84. package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +17 -0
  85. package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +19 -0
  86. package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +11 -0
  87. package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +29 -0
  88. package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +13 -0
  89. package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +4 -0
  90. package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +17 -0
  91. package/dist/components/assistant/xertica-assistant/parts/index.d.ts +16 -0
  92. package/dist/components/assistant/xertica-assistant/types.d.ts +106 -0
  93. package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +125 -0
  94. package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +8 -97
  95. package/dist/components/hooks/index.d.ts +3 -0
  96. package/dist/components/hooks/use-layout-shortcuts.d.ts +22 -0
  97. package/dist/components/layout/sidebar/index.d.ts +2 -0
  98. package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
  99. package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
  100. package/dist/components/media/audio-player/AudioPlayer.d.ts +4 -1
  101. package/dist/components/media/audio-player/use-audio-player.d.ts +72 -0
  102. package/dist/components/shared/CustomTooltipContent.d.ts +20 -0
  103. package/dist/components/shared/layout-constants.d.ts +1 -1
  104. package/dist/components/ui/alert/alert.d.ts +1 -1
  105. package/dist/components/ui/badge/badge.d.ts +1 -1
  106. package/dist/components/ui/button/button.d.ts +2 -2
  107. package/dist/components/ui/chart/chart.d.ts +162 -5
  108. package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
  109. package/dist/components/ui/file-upload/index.d.ts +1 -0
  110. package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
  111. package/dist/components/ui/pagination/index.d.ts +2 -0
  112. package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
  113. package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
  114. package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
  115. package/dist/components/ui/stepper/index.d.ts +3 -1
  116. package/dist/components/ui/stepper/stepper.d.ts +2 -2
  117. package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
  118. package/dist/components/ui/tree-view/index.d.ts +4 -1
  119. package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
  120. package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
  121. package/dist/contexts/AssistenteContext.d.ts +10 -49
  122. package/dist/hooks.cjs.js +30 -10
  123. package/dist/hooks.es.js +25 -4
  124. package/dist/index.cjs.js +20 -9
  125. package/dist/index.es.js +38 -27
  126. package/dist/layout.cjs.js +82 -1
  127. package/dist/layout.es.js +83 -2
  128. package/dist/media.cjs.js +1 -1
  129. package/dist/media.es.js +1 -1
  130. package/dist/pages.cjs.js +1 -1
  131. package/dist/pages.es.js +1 -1
  132. package/dist/rich-text-editor-BmsjY03B.js +2949 -0
  133. package/dist/rich-text-editor-GS2kpTAK.cjs +2966 -0
  134. package/dist/sidebar-CVUGHOS_.cjs +756 -0
  135. package/dist/sidebar-CmvwjnVb.js +757 -0
  136. package/dist/ui.cjs.js +12 -2
  137. package/dist/ui.es.js +24 -14
  138. package/dist/use-audio-player-Bkh23vQ3.js +177 -0
  139. package/dist/use-audio-player-Dn1NR9xN.cjs +176 -0
  140. package/dist/utils/color-utils.d.ts +51 -0
  141. package/dist/xertica-assistant-BMqdyRVi.js +2082 -0
  142. package/dist/xertica-assistant-Bj3vBCq_.cjs +2081 -0
  143. package/dist/xertica-ui.css +1 -1
  144. package/docs/ai-usage.md +28 -10
  145. package/docs/architecture-improvements.md +463 -0
  146. package/docs/architecture.md +77 -1
  147. package/docs/components/assistant-chart.md +1 -1
  148. package/docs/components/assistant.md +159 -0
  149. package/docs/components/audio-player.md +46 -0
  150. package/docs/components/branding.md +251 -0
  151. package/docs/components/chart.md +354 -39
  152. package/docs/components/code-block.md +108 -0
  153. package/docs/components/file-upload.md +119 -2
  154. package/docs/components/formatted-document.md +113 -0
  155. package/docs/components/hooks.md +430 -0
  156. package/docs/components/image-with-fallback.md +106 -0
  157. package/docs/components/map-layers.md +140 -0
  158. package/docs/components/modern-chat-input.md +163 -0
  159. package/docs/components/pages.md +351 -0
  160. package/docs/components/pagination.md +187 -0
  161. package/docs/components/rich-text-editor.md +164 -0
  162. package/docs/components/sidebar.md +153 -4
  163. package/docs/components/stepper.md +157 -12
  164. package/docs/components/tree-view.md +164 -6
  165. package/docs/doc-audit.md +223 -0
  166. package/docs/getting-started.md +155 -1
  167. package/docs/guidelines.md +14 -8
  168. package/docs/layout.md +2 -2
  169. package/docs/llms.md +29 -9
  170. package/docs/patterns/detail-page.md +276 -0
  171. package/docs/patterns/settings.md +346 -0
  172. package/docs/patterns/wizard.md +217 -0
  173. package/guidelines/Guidelines.md +5 -3
  174. package/llms.txt +1 -1
  175. package/package.json +10 -10
  176. package/styles/xertica/tokens.css +41 -12
  177. package/templates/CLAUDE.md +16 -6
  178. package/templates/guidelines/Guidelines.md +16 -4
  179. package/templates/package.json +3 -3
  180. package/templates/src/styles/xertica/tokens.css +39 -10
  181. package/utils/color-utils.ts +72 -0
@@ -12,7 +12,13 @@ import {
12
12
  InteractiveTimeSeriesChart,
13
13
  ComboMetricChart,
14
14
  DonutBreakdownChart,
15
+ SparklineChart,
16
+ RadarMetricChart,
17
+ PieMetricChart,
18
+ RadialBarMetricChart,
19
+ GaugeChart,
15
20
  type ChartConfig,
21
+ type GaugeChartThreshold,
16
22
  } from './chart';
17
23
  import {
18
24
  BarChart,
@@ -25,6 +31,7 @@ import {
25
31
  } from 'recharts';
26
32
  import { Card, CardHeader, CardTitle, CardContent } from '../card';
27
33
  import React from 'react';
34
+ import { TrendingUp, TrendingDown, Minus } from 'lucide-react';
28
35
 
29
36
  const meta: Meta<typeof DashboardBarChart> = {
30
37
  title: 'UI/Chart',
@@ -55,6 +62,8 @@ const meta: Meta<typeof DashboardBarChart> = {
55
62
  export default meta;
56
63
  type Story = StoryObj<typeof DashboardBarChart>;
57
64
 
65
+ // ─── Shared data ──────────────────────────────────────────────────────────────
66
+
58
67
  const data = [
59
68
  { month: 'Jan', revenue: 4000, expenses: 2400 },
60
69
  { month: 'Feb', revenue: 3000, expenses: 1398 },
@@ -73,7 +82,7 @@ const richChartConfig: ChartConfig = {
73
82
  revenue: { label: 'Revenue', color: 'var(--chart-1)' },
74
83
  pipeline: { label: 'Pipeline', color: 'var(--chart-4)' },
75
84
  conversion: { label: 'Conversion', color: 'var(--chart-2)' },
76
- churn: { label: 'Churn', color: 'var(--chart-3)' },
85
+ churn: { label: 'Churn', color: 'var(--chart-5)' },
77
86
  };
78
87
 
79
88
  const richData = [
@@ -83,6 +92,8 @@ const richData = [
83
92
  { date: 'Apr 22', revenue: 5600, pipeline: 4300, conversion: 41, churn: 9 },
84
93
  { date: 'Apr 29', revenue: 7200, pipeline: 4700, conversion: 48, churn: 5 },
85
94
  { date: 'May 06', revenue: 7900, pipeline: 5300, conversion: 52, churn: 4 },
95
+ { date: 'May 13', revenue: 8400, pipeline: 5800, conversion: 55, churn: 3 },
96
+ { date: 'May 20', revenue: 9100, pipeline: 6200, conversion: 58, churn: 4 },
86
97
  ];
87
98
 
88
99
  const breakdownConfig: ChartConfig = {
@@ -97,7 +108,10 @@ const breakdownData = [
97
108
  { name: 'startup', value: 17 },
98
109
  ];
99
110
 
111
+ // ─── Primitive stories ────────────────────────────────────────────────────────
112
+
100
113
  export const BarChartExample: Story = {
114
+ name: 'Primitive / Bar Chart',
101
115
  render: () => (
102
116
  <Card className="w-full max-w-4xl">
103
117
  <CardHeader>
@@ -120,6 +134,7 @@ export const BarChartExample: Story = {
120
134
  };
121
135
 
122
136
  export const LineChartExample: Story = {
137
+ name: 'Primitive / Line Chart',
123
138
  render: () => (
124
139
  <Card className="w-full max-w-4xl">
125
140
  <CardHeader>
@@ -128,7 +143,7 @@ export const LineChartExample: Story = {
128
143
  <CardContent>
129
144
  <ChartContainer config={chartConfig} className="h-[300px] w-full">
130
145
  <LineChart data={data}>
131
- <CartesianGrid vertical={false} />
146
+ <CartesianGrid vertical={false} strokeDasharray="3 3" />
132
147
  <XAxis dataKey="month" tickLine={false} axisLine={false} />
133
148
  <ChartTooltip content={<ChartTooltipContent />} />
134
149
  <Line
@@ -136,8 +151,8 @@ export const LineChartExample: Story = {
136
151
  dataKey="revenue"
137
152
  stroke="var(--color-revenue)"
138
153
  strokeWidth={3}
139
- dot={{ r: 4, fill: "var(--color-revenue)" }}
140
- activeDot={{ r: 6 }}
154
+ dot={{ r: 4, fill: "var(--color-revenue)", strokeWidth: 0 }}
155
+ activeDot={{ r: 6, strokeWidth: 0 }}
141
156
  />
142
157
  </LineChart>
143
158
  </ChartContainer>
@@ -146,7 +161,10 @@ export const LineChartExample: Story = {
146
161
  ),
147
162
  };
148
163
 
164
+ // ─── Dashboard chart stories ──────────────────────────────────────────────────
165
+
149
166
  export const DashboardBarChartExample: Story = {
167
+ name: 'Bar Chart',
150
168
  args: {
151
169
  data: richData,
152
170
  indexKey: 'date',
@@ -173,10 +191,11 @@ export const DashboardBarChartExample: Story = {
173
191
  };
174
192
 
175
193
  export const DashboardLineChartExample: Story = {
194
+ name: 'Line Chart',
176
195
  render: () => (
177
196
  <ChartCard
178
197
  title="Growth Trend"
179
- description="Multi-series line chart with color markers"
198
+ description="Multi-series line chart with smooth curves"
180
199
  >
181
200
  <DashboardLineChart
182
201
  data={richData}
@@ -192,12 +211,16 @@ export const DashboardLineChartExample: Story = {
192
211
  { key: 'pipeline', label: 'Pipeline' },
193
212
  { key: 'conversion', label: 'Conversion' },
194
213
  ]}
214
+ showDots
215
+ curveType="monotone"
216
+ strokeWidth={2.5}
195
217
  />
196
218
  </ChartCard>
197
219
  ),
198
220
  };
199
221
 
200
222
  export const HorizontalBarChartExample: Story = {
223
+ name: 'Horizontal Bar Chart',
201
224
  render: () => (
202
225
  <ChartCard
203
226
  title="Top Segments"
@@ -222,6 +245,7 @@ export const HorizontalBarChartExample: Story = {
222
245
  };
223
246
 
224
247
  export const InteractiveTimeSeriesExample: Story = {
248
+ name: 'Interactive Time Series',
225
249
  render: () => (
226
250
  <ChartCard
227
251
  title="Executive Trend"
@@ -237,12 +261,38 @@ export const InteractiveTimeSeriesExample: Story = {
237
261
  { key: 'pipeline', label: 'Pipeline' },
238
262
  { key: 'conversion', label: 'Conversion' },
239
263
  ]}
264
+ gradientFill
265
+ />
266
+ </ChartCard>
267
+ ),
268
+ };
269
+
270
+ export const GradientAreaChartExample: Story = {
271
+ name: 'Gradient Area Chart',
272
+ render: () => (
273
+ <ChartCard
274
+ title="Revenue Over Time"
275
+ description="Area chart with gradient fill for a modern look"
276
+ >
277
+ <InteractiveTimeSeriesChart
278
+ data={richData}
279
+ indexKey="date"
280
+ config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
281
+ colors={['var(--chart-1)']}
282
+ series={[{ key: 'revenue', label: 'Revenue' }]}
283
+ gradientFill
284
+ showDots
285
+ periods={[
286
+ { value: '7d', label: '7 days' },
287
+ { value: '30d', label: '30 days' },
288
+ ]}
240
289
  />
241
290
  </ChartCard>
242
291
  ),
243
292
  };
244
293
 
245
294
  export const ComboMetricChartExample: Story = {
295
+ name: 'Combo Metric Chart',
246
296
  render: () => (
247
297
  <ChartCard
248
298
  title="Pipeline Health"
@@ -266,7 +316,34 @@ export const ComboMetricChartExample: Story = {
266
316
  ),
267
317
  };
268
318
 
319
+ export const ComboWithGradientAreaExample: Story = {
320
+ name: 'Combo Chart with Gradient Area',
321
+ render: () => (
322
+ <ChartCard
323
+ title="Revenue & Pipeline"
324
+ description="Gradient area for revenue, line for conversion rate"
325
+ >
326
+ <ComboMetricChart
327
+ data={richData}
328
+ indexKey="date"
329
+ config={richChartConfig}
330
+ colors={{
331
+ revenue: 'var(--chart-1)',
332
+ conversion: 'var(--chart-2)',
333
+ }}
334
+ series={[
335
+ { key: 'revenue', type: 'area' },
336
+ { key: 'conversion', type: 'line' },
337
+ ]}
338
+ gradientFill
339
+ curveType="monotone"
340
+ />
341
+ </ChartCard>
342
+ ),
343
+ };
344
+
269
345
  export const DonutBreakdownExample: Story = {
346
+ name: 'Donut Chart',
270
347
  render: () => (
271
348
  <ChartCard
272
349
  title="Customer Mix"
@@ -283,7 +360,250 @@ export const DonutBreakdownExample: Story = {
283
360
  ),
284
361
  };
285
362
 
363
+ // ─── Sparkline stories ────────────────────────────────────────────────────────
364
+
365
+ const sparkData = [
366
+ { v: 12 }, { v: 18 }, { v: 14 }, { v: 22 }, { v: 19 },
367
+ { v: 28 }, { v: 24 }, { v: 31 }, { v: 27 }, { v: 35 },
368
+ ];
369
+
370
+ const sparkDataDown = [
371
+ { v: 35 }, { v: 30 }, { v: 28 }, { v: 22 }, { v: 25 },
372
+ { v: 18 }, { v: 20 }, { v: 14 }, { v: 12 }, { v: 8 },
373
+ ];
374
+
375
+ export const SparklineExample: Story = {
376
+ name: 'Sparkline Chart',
377
+ render: () => (
378
+ <div className="space-y-6 max-w-2xl">
379
+ <p className="text-sm text-muted-foreground">
380
+ Sparklines are minimal inline charts designed for stat cards and table cells.
381
+ </p>
382
+
383
+ {/* Stat cards with sparklines */}
384
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
385
+ <Card className="p-4">
386
+ <div className="flex items-start justify-between mb-3">
387
+ <div>
388
+ <p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Revenue</p>
389
+ <p className="text-2xl font-bold mt-0.5">$35.2K</p>
390
+ </div>
391
+ <span className="inline-flex items-center gap-1 text-xs font-medium text-emerald-600 bg-emerald-50 dark:bg-emerald-950 dark:text-emerald-400 px-2 py-0.5 rounded-full">
392
+ <TrendingUp className="size-3" />
393
+ +12%
394
+ </span>
395
+ </div>
396
+ <SparklineChart
397
+ data={sparkData}
398
+ dataKey="v"
399
+ color="var(--chart-2)"
400
+ filled
401
+ showEndDot
402
+ className="h-12"
403
+ />
404
+ </Card>
405
+
406
+ <Card className="p-4">
407
+ <div className="flex items-start justify-between mb-3">
408
+ <div>
409
+ <p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Churn</p>
410
+ <p className="text-2xl font-bold mt-0.5">8.4%</p>
411
+ </div>
412
+ <span className="inline-flex items-center gap-1 text-xs font-medium text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400 px-2 py-0.5 rounded-full">
413
+ <TrendingDown className="size-3" />
414
+ -3%
415
+ </span>
416
+ </div>
417
+ <SparklineChart
418
+ data={sparkDataDown}
419
+ dataKey="v"
420
+ color="var(--chart-5)"
421
+ filled
422
+ showEndDot
423
+ className="h-12"
424
+ />
425
+ </Card>
426
+
427
+ <Card className="p-4">
428
+ <div className="flex items-start justify-between mb-3">
429
+ <div>
430
+ <p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Pipeline</p>
431
+ <p className="text-2xl font-bold mt-0.5">$128K</p>
432
+ </div>
433
+ <span className="inline-flex items-center gap-1 text-xs font-medium text-muted-foreground bg-muted px-2 py-0.5 rounded-full">
434
+ <Minus className="size-3" />
435
+ 0%
436
+ </span>
437
+ </div>
438
+ <SparklineChart
439
+ data={[
440
+ { v: 20 }, { v: 22 }, { v: 19 }, { v: 23 }, { v: 21 },
441
+ { v: 24 }, { v: 22 }, { v: 25 }, { v: 23 }, { v: 24 },
442
+ ]}
443
+ dataKey="v"
444
+ color="var(--chart-4)"
445
+ filled
446
+ showEndDot
447
+ className="h-12"
448
+ />
449
+ </Card>
450
+ </div>
451
+
452
+ {/* Sparkline variants */}
453
+ <ChartCard title="Sparkline Variants" description="Line-only vs filled area">
454
+ <div className="space-y-4 py-2">
455
+ <div className="flex items-center gap-4">
456
+ <span className="text-xs text-muted-foreground w-24 shrink-0">Filled (default)</span>
457
+ <SparklineChart data={sparkData} dataKey="v" color="var(--chart-1)" filled className="h-10 flex-1" />
458
+ </div>
459
+ <div className="flex items-center gap-4">
460
+ <span className="text-xs text-muted-foreground w-24 shrink-0">Line only</span>
461
+ <SparklineChart data={sparkData} dataKey="v" color="var(--chart-3)" filled={false} showEndDot={false} className="h-10 flex-1" />
462
+ </div>
463
+ <div className="flex items-center gap-4">
464
+ <span className="text-xs text-muted-foreground w-24 shrink-0">Step curve</span>
465
+ <SparklineChart data={sparkData} dataKey="v" color="var(--chart-6)" filled curveType="step" className="h-10 flex-1" />
466
+ </div>
467
+ </div>
468
+ </ChartCard>
469
+ </div>
470
+ ),
471
+ };
472
+
473
+ // ─── Dashboard layout story ───────────────────────────────────────────────────
474
+
475
+ export const DashboardLayoutExample: Story = {
476
+ name: 'Dashboard Layout',
477
+ render: () => {
478
+ const kpiData = [
479
+ { label: 'Total Revenue', value: '$128.4K', trend: '+18%', up: true, sparkData: sparkData, color: 'var(--chart-1)' },
480
+ { label: 'Active Pipeline', value: '$62.1K', trend: '+9%', up: true, sparkData: sparkData.map(d => ({ v: d.v * 0.7 })), color: 'var(--chart-4)' },
481
+ { label: 'Conversion Rate', value: '58%', trend: '+4%', up: true, sparkData: sparkData.map(d => ({ v: d.v * 0.5 })), color: 'var(--chart-2)' },
482
+ { label: 'Churn Rate', value: '3.2%', trend: '-1%', up: false, sparkData: sparkDataDown, color: 'var(--chart-5)' },
483
+ ];
484
+
485
+ return (
486
+ <div className="space-y-6">
487
+ {/* KPI row */}
488
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
489
+ {kpiData.map((kpi) => (
490
+ <Card key={kpi.label} className="p-4">
491
+ <div className="flex items-start justify-between mb-3">
492
+ <div>
493
+ <p className="text-xs text-muted-foreground font-medium">{kpi.label}</p>
494
+ <p className="text-xl font-bold mt-0.5">{kpi.value}</p>
495
+ </div>
496
+ <span className={`inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full ${kpi.up
497
+ ? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-950 dark:text-emerald-400'
498
+ : 'text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400'
499
+ }`}>
500
+ {kpi.up ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
501
+ {kpi.trend}
502
+ </span>
503
+ </div>
504
+ <SparklineChart
505
+ data={kpi.sparkData}
506
+ dataKey="v"
507
+ color={kpi.color}
508
+ filled
509
+ showEndDot
510
+ className="h-10"
511
+ />
512
+ </Card>
513
+ ))}
514
+ </div>
515
+
516
+ {/* Main charts row */}
517
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
518
+ <div className="lg:col-span-2">
519
+ <ChartCard
520
+ title="Revenue & Pipeline"
521
+ description="Weekly performance overview"
522
+ >
523
+ <InteractiveTimeSeriesChart
524
+ data={richData}
525
+ indexKey="date"
526
+ config={richChartConfig}
527
+ colors={['var(--chart-1)', 'var(--chart-4)', 'var(--chart-2)']}
528
+ series={[
529
+ { key: 'revenue', label: 'Revenue' },
530
+ { key: 'pipeline', label: 'Pipeline' },
531
+ { key: 'conversion', label: 'Conversion' },
532
+ ]}
533
+ gradientFill
534
+ periods={[
535
+ { value: '7d', label: '7 days' },
536
+ { value: '30d', label: '30 days' },
537
+ ]}
538
+ />
539
+ </ChartCard>
540
+ </div>
541
+
542
+ <ChartCard
543
+ title="Customer Mix"
544
+ description="Segment breakdown"
545
+ >
546
+ <DonutBreakdownChart
547
+ data={breakdownData}
548
+ config={breakdownConfig}
549
+ colors={['var(--chart-1)', 'var(--chart-4)', 'var(--chart-2)']}
550
+ centerValue="100%"
551
+ centerLabel="Accounts"
552
+ />
553
+ </ChartCard>
554
+ </div>
555
+
556
+ {/* Bottom row */}
557
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
558
+ <ChartCard
559
+ title="Top Segments"
560
+ description="Revenue by customer segment"
561
+ >
562
+ <HorizontalBarChart
563
+ data={[
564
+ { segment: 'Enterprise', revenue: 7600 },
565
+ { segment: 'Mid-market', revenue: 5400 },
566
+ { segment: 'Startup', revenue: 3100 },
567
+ { segment: 'Partners', revenue: 2200 },
568
+ ]}
569
+ indexKey="segment"
570
+ config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
571
+ colors={{ revenue: 'var(--chart-1)' }}
572
+ barSize="xl"
573
+ categoryWidth={110}
574
+ showLegend={false}
575
+ />
576
+ </ChartCard>
577
+
578
+ <ChartCard
579
+ title="Pipeline Health"
580
+ description="Volume vs conversion rate"
581
+ >
582
+ <ComboMetricChart
583
+ data={richData}
584
+ indexKey="date"
585
+ config={richChartConfig}
586
+ colors={{
587
+ pipeline: 'var(--chart-4)',
588
+ conversion: 'var(--chart-2)',
589
+ }}
590
+ barSize="lg"
591
+ series={[
592
+ { key: 'pipeline', type: 'bar' },
593
+ { key: 'conversion', type: 'line' },
594
+ ]}
595
+ />
596
+ </ChartCard>
597
+ </div>
598
+ </div>
599
+ );
600
+ },
601
+ };
602
+
603
+ // ─── State stories ────────────────────────────────────────────────────────────
604
+
286
605
  export const EmptyStateExample: Story = {
606
+ name: 'State / Empty',
287
607
  render: () => (
288
608
  <ChartCard
289
609
  title="Revenue by Week"
@@ -300,6 +620,7 @@ export const EmptyStateExample: Story = {
300
620
  };
301
621
 
302
622
  export const ConnectionErrorExample: Story = {
623
+ name: 'State / Connection Error',
303
624
  render: () => (
304
625
  <ChartCard
305
626
  title="Pipeline Health"
@@ -316,10 +637,30 @@ export const ConnectionErrorExample: Story = {
316
637
  ),
317
638
  };
318
639
 
640
+ export const LoadingStateExample: Story = {
641
+ name: 'State / Loading',
642
+ render: () => (
643
+ <ChartCard
644
+ title="Revenue by Week"
645
+ description="Loading skeleton while data is fetched"
646
+ >
647
+ <DashboardBarChart
648
+ data={[]}
649
+ indexKey="date"
650
+ config={richChartConfig}
651
+ isLoading
652
+ />
653
+ </ChartCard>
654
+ ),
655
+ };
656
+
657
+ // ─── Variant stories ──────────────────────────────────────────────────────────
658
+
319
659
  export const BarSizeVariantsExample: Story = {
660
+ name: 'Variants / Bar Sizes',
320
661
  render: () => (
321
662
  <div className="space-y-6">
322
- <ChartCard title="Thin Bars" description="barSize=&quot;sm&quot;">
663
+ <ChartCard title="Thin Bars" description='barSize="sm"'>
323
664
  <DashboardBarChart
324
665
  data={richData}
325
666
  indexKey="date"
@@ -329,7 +670,7 @@ export const BarSizeVariantsExample: Story = {
329
670
  showLegend={false}
330
671
  />
331
672
  </ChartCard>
332
- <ChartCard title="Wide Bars" description="barSize=&quot;xl&quot;">
673
+ <ChartCard title="Wide Bars" description='barSize="xl"'>
333
674
  <DashboardBarChart
334
675
  data={richData}
335
676
  indexKey="date"
@@ -342,3 +683,621 @@ export const BarSizeVariantsExample: Story = {
342
683
  </div>
343
684
  ),
344
685
  };
686
+
687
+ export const CurveTypeVariantsExample: Story = {
688
+ name: 'Variants / Curve Types',
689
+ render: () => (
690
+ <div className="space-y-6">
691
+ {(['monotone', 'linear', 'step', 'natural', 'basis'] as const).map((curve) => (
692
+ <ChartCard key={curve} title={`curveType="${curve}"`} description="Line chart curve interpolation">
693
+ <DashboardLineChart
694
+ data={richData}
695
+ indexKey="date"
696
+ config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
697
+ series={[{ key: 'revenue' }]}
698
+ curveType={curve}
699
+ showLegend={false}
700
+ showDots={curve === 'linear'}
701
+ />
702
+ </ChartCard>
703
+ ))}
704
+ </div>
705
+ ),
706
+ };
707
+
708
+ export const ColorPaletteExample: Story = {
709
+ name: 'Variants / Color Palette',
710
+ render: () => {
711
+ const paletteConfig: ChartConfig = {
712
+ chart1: { label: 'chart-1 (indigo)', color: 'var(--chart-1)' },
713
+ chart2: { label: 'chart-2 (emerald)', color: 'var(--chart-2)' },
714
+ chart3: { label: 'chart-3 (amber)', color: 'var(--chart-3)' },
715
+ chart4: { label: 'chart-4 (blue)', color: 'var(--chart-4)' },
716
+ chart5: { label: 'chart-5 (red)', color: 'var(--chart-5)' },
717
+ chart6: { label: 'chart-6 (purple)', color: 'var(--chart-6)' },
718
+ chart7: { label: 'chart-7 (teal)', color: 'var(--chart-7)' },
719
+ chart8: { label: 'chart-8 (rose)', color: 'var(--chart-8)' },
720
+ };
721
+
722
+ const paletteData = [
723
+ { name: 'A', chart1: 80, chart2: 70, chart3: 60, chart4: 75, chart5: 55, chart6: 65, chart7: 72, chart8: 68 },
724
+ ];
725
+
726
+ return (
727
+ <ChartCard
728
+ title="Chart Color Palette"
729
+ description="All 8 chart tokens — vibrant, accessible, harmonious"
730
+ >
731
+ <DashboardBarChart
732
+ data={paletteData}
733
+ indexKey="name"
734
+ config={paletteConfig}
735
+ barSize="xl"
736
+ showGrid={false}
737
+ />
738
+ </ChartCard>
739
+ );
740
+ },
741
+ };
742
+
743
+ // ─── Radar Chart Stories ───────────────────────────────────────────────────────
744
+
745
+ const radarData = [
746
+ { skill: 'Performance', score: 88 },
747
+ { skill: 'Reliability', score: 72 },
748
+ { skill: 'Security', score: 95 },
749
+ { skill: 'Scalability', score: 65 },
750
+ { skill: 'Usability', score: 80 },
751
+ { skill: 'Maintainability', score: 58 },
752
+ ];
753
+
754
+ const radarMultiData = [
755
+ { skill: 'Performance', teamA: 88, teamB: 62 },
756
+ { skill: 'Reliability', teamA: 72, teamB: 85 },
757
+ { skill: 'Security', teamA: 95, teamB: 70 },
758
+ { skill: 'Scalability', teamA: 65, teamB: 90 },
759
+ { skill: 'Usability', teamA: 80, teamB: 55 },
760
+ { skill: 'Maintainability', teamA: 58, teamB: 78 },
761
+ ];
762
+
763
+ export const RadarChartFilled: Story = {
764
+ name: 'Radar Chart / Filled',
765
+ render: () => (
766
+ <ChartCard
767
+ title="System Health Score"
768
+ description="Filled radar — single series"
769
+ >
770
+ <RadarMetricChart
771
+ data={radarData}
772
+ labelKey="skill"
773
+ series={[{ key: 'score', label: 'Score' }]}
774
+ filled
775
+ showLegend={false}
776
+ />
777
+ </ChartCard>
778
+ ),
779
+ };
780
+
781
+ export const RadarChartOutline: Story = {
782
+ name: 'Radar Chart / Outline',
783
+ render: () => (
784
+ <ChartCard
785
+ title="System Health Score"
786
+ description="Outline only — no fill"
787
+ >
788
+ <RadarMetricChart
789
+ data={radarData}
790
+ labelKey="skill"
791
+ series={[{ key: 'score', label: 'Score' }]}
792
+ filled={false}
793
+ showDots
794
+ showLegend={false}
795
+ />
796
+ </ChartCard>
797
+ ),
798
+ };
799
+
800
+ export const RadarChartMultiSeries: Story = {
801
+ name: 'Radar Chart / Multi-Series',
802
+ render: () => (
803
+ <ChartCard
804
+ title="Team Comparison"
805
+ description="Two teams across 6 dimensions"
806
+ >
807
+ <RadarMetricChart
808
+ data={radarMultiData}
809
+ labelKey="skill"
810
+ series={[
811
+ { key: 'teamA', label: 'Team Alpha' },
812
+ { key: 'teamB', label: 'Team Beta' },
813
+ ]}
814
+ filled
815
+ fillOpacity={0.2}
816
+ showDots
817
+ />
818
+ </ChartCard>
819
+ ),
820
+ };
821
+
822
+ export const RadarChartVariants: Story = {
823
+ name: 'Radar Chart / All Variants',
824
+ render: () => (
825
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
826
+ <ChartCard title="Filled" description="Default filled area">
827
+ <RadarMetricChart
828
+ data={radarData}
829
+ labelKey="skill"
830
+ series={[{ key: 'score', label: 'Score' }]}
831
+ filled
832
+ showLegend={false}
833
+ />
834
+ </ChartCard>
835
+ <ChartCard title="Outline + Dots" description="No fill, with dots">
836
+ <RadarMetricChart
837
+ data={radarData}
838
+ labelKey="skill"
839
+ series={[{ key: 'score', label: 'Score' }]}
840
+ filled={false}
841
+ showDots
842
+ showLegend={false}
843
+ />
844
+ </ChartCard>
845
+ <ChartCard title="Multi-Series" description="Comparison overlay">
846
+ <RadarMetricChart
847
+ data={radarMultiData}
848
+ labelKey="skill"
849
+ series={[
850
+ { key: 'teamA', label: 'Team A' },
851
+ { key: 'teamB', label: 'Team B' },
852
+ ]}
853
+ filled
854
+ fillOpacity={0.18}
855
+ />
856
+ </ChartCard>
857
+ </div>
858
+ ),
859
+ };
860
+
861
+ // ─── Pie Chart Stories ────────────────────────────────────────────────────────
862
+
863
+ const pieData = [
864
+ { category: 'Infrastructure', spend: 42000 },
865
+ { category: 'Engineering', spend: 31000 },
866
+ { category: 'Marketing', spend: 18500 },
867
+ { category: 'Operations', spend: 12000 },
868
+ { category: 'Other', spend: 6500 },
869
+ ];
870
+
871
+ export const PieChartSimple: Story = {
872
+ name: 'Pie Chart / Simple',
873
+ render: () => (
874
+ <ChartCard
875
+ title="Budget Breakdown"
876
+ description="Simple pie — no labels"
877
+ >
878
+ <PieMetricChart
879
+ data={pieData}
880
+ nameKey="category"
881
+ valueKey="spend"
882
+ valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
883
+ />
884
+ </ChartCard>
885
+ ),
886
+ };
887
+
888
+ export const PieChartWithLabels: Story = {
889
+ name: 'Pie Chart / With Labels',
890
+ render: () => (
891
+ <ChartCard
892
+ title="Budget Breakdown"
893
+ description="Pie with percentage labels"
894
+ >
895
+ <PieMetricChart
896
+ data={pieData}
897
+ nameKey="category"
898
+ valueKey="spend"
899
+ showLabels
900
+ valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
901
+ />
902
+ </ChartCard>
903
+ ),
904
+ };
905
+
906
+ export const PieChartExploded: Story = {
907
+ name: 'Pie Chart / Exploded Slice',
908
+ render: () => (
909
+ <ChartCard
910
+ title="Budget Breakdown"
911
+ description="Largest slice highlighted with drop shadow"
912
+ >
913
+ <PieMetricChart
914
+ data={pieData}
915
+ nameKey="category"
916
+ valueKey="spend"
917
+ showLabels
918
+ explodeIndex={0}
919
+ valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
920
+ />
921
+ </ChartCard>
922
+ ),
923
+ };
924
+
925
+ export const PieChartDonutVariant: Story = {
926
+ name: 'Pie Chart / Donut Variant',
927
+ render: () => (
928
+ <ChartCard
929
+ title="Budget Breakdown"
930
+ description="Pie with inner radius (donut style)"
931
+ >
932
+ <PieMetricChart
933
+ data={pieData}
934
+ nameKey="category"
935
+ valueKey="spend"
936
+ innerRadius="45%"
937
+ showLabels
938
+ valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
939
+ />
940
+ </ChartCard>
941
+ ),
942
+ };
943
+
944
+ export const PieChartVariants: Story = {
945
+ name: 'Pie Chart / All Variants',
946
+ render: () => (
947
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
948
+ <ChartCard title="Simple" description="No labels">
949
+ <PieMetricChart data={pieData} nameKey="category" valueKey="spend" />
950
+ </ChartCard>
951
+ <ChartCard title="With Labels" description="Percentage labels">
952
+ <PieMetricChart data={pieData} nameKey="category" valueKey="spend" showLabels />
953
+ </ChartCard>
954
+ <ChartCard title="Exploded" description="First slice highlighted">
955
+ <PieMetricChart data={pieData} nameKey="category" valueKey="spend" showLabels explodeIndex={0} />
956
+ </ChartCard>
957
+ <ChartCard title="Donut" description="Inner radius 45%">
958
+ <PieMetricChart data={pieData} nameKey="category" valueKey="spend" innerRadius="45%" showLabels />
959
+ </ChartCard>
960
+ </div>
961
+ ),
962
+ };
963
+
964
+ // ─── Radial Bar Chart Stories ─────────────────────────────────────────────────
965
+
966
+ const radialData = [
967
+ { name: 'Infrastructure', value: 78 },
968
+ { name: 'Engineering', value: 62 },
969
+ { name: 'Marketing', value: 45 },
970
+ { name: 'Operations', value: 91 },
971
+ ];
972
+
973
+ export const RadialBarChartSingle: Story = {
974
+ name: 'Radial Bar / Single Bar',
975
+ render: () => (
976
+ <ChartCard
977
+ title="Deployment Success Rate"
978
+ description="Single radial bar — full circle"
979
+ >
980
+ <RadialBarMetricChart
981
+ data={[{ name: 'Success Rate', value: 87 }]}
982
+ dataKey="value"
983
+ nameKey="name"
984
+ showLegend={false}
985
+ innerRadius="60%"
986
+ outerRadius="90%"
987
+ valueFormatter={(v) => `${v}%`}
988
+ />
989
+ </ChartCard>
990
+ ),
991
+ };
992
+
993
+ export const RadialBarChartMulti: Story = {
994
+ name: 'Radial Bar / Multi-Bar',
995
+ render: () => (
996
+ <ChartCard
997
+ title="Department KPIs"
998
+ description="Multiple radial bars with background track"
999
+ >
1000
+ <RadialBarMetricChart
1001
+ data={radialData}
1002
+ dataKey="value"
1003
+ nameKey="name"
1004
+ innerRadius="20%"
1005
+ outerRadius="90%"
1006
+ valueFormatter={(v) => `${v}%`}
1007
+ />
1008
+ </ChartCard>
1009
+ ),
1010
+ };
1011
+
1012
+ export const RadialBarChartSemiCircle: Story = {
1013
+ name: 'Radial Bar / Semi-Circle',
1014
+ render: () => (
1015
+ <ChartCard
1016
+ title="Department KPIs"
1017
+ description="Semi-circle layout (startAngle=180, endAngle=0)"
1018
+ >
1019
+ <RadialBarMetricChart
1020
+ data={radialData}
1021
+ dataKey="value"
1022
+ nameKey="name"
1023
+ innerRadius="30%"
1024
+ outerRadius="90%"
1025
+ startAngle={180}
1026
+ endAngle={0}
1027
+ valueFormatter={(v) => `${v}%`}
1028
+ />
1029
+ </ChartCard>
1030
+ ),
1031
+ };
1032
+
1033
+ export const RadialBarChartVariants: Story = {
1034
+ name: 'Radial Bar / All Variants',
1035
+ render: () => (
1036
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
1037
+ <ChartCard title="Single Bar" description="Full circle">
1038
+ <RadialBarMetricChart
1039
+ data={[{ name: 'Success', value: 87 }]}
1040
+ dataKey="value"
1041
+ nameKey="name"
1042
+ showLegend={false}
1043
+ innerRadius="60%"
1044
+ outerRadius="90%"
1045
+ valueFormatter={(v) => `${v}%`}
1046
+ />
1047
+ </ChartCard>
1048
+ <ChartCard title="Multi-Bar" description="Stacked radial bars">
1049
+ <RadialBarMetricChart
1050
+ data={radialData}
1051
+ dataKey="value"
1052
+ nameKey="name"
1053
+ innerRadius="20%"
1054
+ outerRadius="90%"
1055
+ valueFormatter={(v) => `${v}%`}
1056
+ />
1057
+ </ChartCard>
1058
+ <ChartCard title="Semi-Circle" description="180° arc layout">
1059
+ <RadialBarMetricChart
1060
+ data={radialData}
1061
+ dataKey="value"
1062
+ nameKey="name"
1063
+ innerRadius="30%"
1064
+ outerRadius="90%"
1065
+ startAngle={180}
1066
+ endAngle={0}
1067
+ valueFormatter={(v) => `${v}%`}
1068
+ />
1069
+ </ChartCard>
1070
+ </div>
1071
+ ),
1072
+ };
1073
+
1074
+ // ─── Gauge Chart Stories ──────────────────────────────────────────────────────
1075
+
1076
+ const gaugeThresholds: GaugeChartThreshold[] = [
1077
+ { value: 33, color: 'var(--chart-5)', label: 'Critical' },
1078
+ { value: 66, color: 'var(--chart-3)', label: 'Warning' },
1079
+ { value: 100, color: 'var(--chart-2)', label: 'Healthy' },
1080
+ ];
1081
+
1082
+ export const GaugeChartSimple: Story = {
1083
+ name: 'Gauge Chart / Simple',
1084
+ render: () => (
1085
+ <ChartCard
1086
+ title="CPU Usage"
1087
+ description="Simple gauge — single color"
1088
+ className="max-w-sm mx-auto"
1089
+ >
1090
+ <div className="flex justify-center py-4">
1091
+ <GaugeChart value={67} label="CPU Usage" />
1092
+ </div>
1093
+ </ChartCard>
1094
+ ),
1095
+ };
1096
+
1097
+ export const GaugeChartWithThresholds: Story = {
1098
+ name: 'Gauge Chart / With Thresholds',
1099
+ render: () => (
1100
+ <ChartCard
1101
+ title="System Health"
1102
+ description="Color zones: critical / warning / healthy"
1103
+ className="max-w-sm mx-auto"
1104
+ >
1105
+ <div className="flex justify-center py-4">
1106
+ <GaugeChart
1107
+ value={72}
1108
+ label="Overall Health"
1109
+ thresholds={gaugeThresholds}
1110
+ />
1111
+ </div>
1112
+ </ChartCard>
1113
+ ),
1114
+ };
1115
+
1116
+ export const GaugeChartNoNeedle: Story = {
1117
+ name: 'Gauge Chart / No Needle',
1118
+ render: () => (
1119
+ <ChartCard
1120
+ title="Memory Usage"
1121
+ description="Arc only — no needle indicator"
1122
+ className="max-w-sm mx-auto"
1123
+ >
1124
+ <div className="flex justify-center py-4">
1125
+ <GaugeChart
1126
+ value={45}
1127
+ label="Memory"
1128
+ showNeedle={false}
1129
+ thresholds={gaugeThresholds}
1130
+ />
1131
+ </div>
1132
+ </ChartCard>
1133
+ ),
1134
+ };
1135
+
1136
+ export const GaugeChartVariants: Story = {
1137
+ name: 'Gauge Chart / All Variants',
1138
+ render: () => (
1139
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
1140
+ <ChartCard title="Low (22%)" description="Critical zone">
1141
+ <div className="flex justify-center py-2">
1142
+ <GaugeChart value={22} label="CPU" thresholds={gaugeThresholds} />
1143
+ </div>
1144
+ </ChartCard>
1145
+ <ChartCard title="Medium (55%)" description="Warning zone">
1146
+ <div className="flex justify-center py-2">
1147
+ <GaugeChart value={55} label="Memory" thresholds={gaugeThresholds} />
1148
+ </div>
1149
+ </ChartCard>
1150
+ <ChartCard title="High (88%)" description="Healthy zone">
1151
+ <div className="flex justify-center py-2">
1152
+ <GaugeChart value={88} label="Disk I/O" thresholds={gaugeThresholds} />
1153
+ </div>
1154
+ </ChartCard>
1155
+ </div>
1156
+ ),
1157
+ };
1158
+
1159
+ export const GaugeChartDashboard: Story = {
1160
+ name: 'Gauge Chart / Infrastructure Dashboard',
1161
+ render: () => {
1162
+ const metrics = [
1163
+ { label: 'CPU', value: 34, unit: '%' },
1164
+ { label: 'Memory', value: 71, unit: '%' },
1165
+ { label: 'Disk', value: 58, unit: '%' },
1166
+ { label: 'Network', value: 22, unit: '%' },
1167
+ ];
1168
+
1169
+ return (
1170
+ <div className="space-y-4">
1171
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
1172
+ {metrics.map((m) => (
1173
+ <ChartCard key={m.label} title={m.label} description={`Current ${m.label} utilization`}>
1174
+ <div className="flex justify-center py-2">
1175
+ <GaugeChart
1176
+ value={m.value}
1177
+ label={m.label}
1178
+ thresholds={gaugeThresholds}
1179
+ valueFormatter={(v) => `${v}${m.unit}`}
1180
+ />
1181
+ </div>
1182
+ </ChartCard>
1183
+ ))}
1184
+ </div>
1185
+ </div>
1186
+ );
1187
+ },
1188
+ };
1189
+
1190
+ // ─── Complete Chart Library Overview ─────────────────────────────────────────
1191
+
1192
+ export const CompleteLibraryOverview: Story = {
1193
+ name: 'Complete Library / All Chart Types',
1194
+ render: () => (
1195
+ <div className="space-y-6">
1196
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1197
+ {/* Bar */}
1198
+ <ChartCard title="Bar Chart" description="Categorical comparison">
1199
+ <DashboardBarChart
1200
+ data={[
1201
+ { month: 'Jan', revenue: 42000 },
1202
+ { month: 'Feb', revenue: 38000 },
1203
+ { month: 'Mar', revenue: 51000 },
1204
+ { month: 'Apr', revenue: 47000 },
1205
+ { month: 'May', revenue: 55000 },
1206
+ ]}
1207
+ indexKey="month"
1208
+ config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
1209
+ series={[{ key: 'revenue', label: 'Revenue' }]}
1210
+ showLegend={false}
1211
+ />
1212
+ </ChartCard>
1213
+
1214
+ {/* Line */}
1215
+ <ChartCard title="Line Chart" description="Trend over time">
1216
+ <DashboardLineChart
1217
+ data={[
1218
+ { month: 'Jan', users: 1200 },
1219
+ { month: 'Feb', users: 1450 },
1220
+ { month: 'Mar', users: 1380 },
1221
+ { month: 'Apr', users: 1620 },
1222
+ { month: 'May', users: 1890 },
1223
+ ]}
1224
+ indexKey="month"
1225
+ config={{ users: { label: 'Users', color: 'var(--chart-2)' } }}
1226
+ series={[{ key: 'users', label: 'Users' }]}
1227
+ showLegend={false}
1228
+ />
1229
+ </ChartCard>
1230
+
1231
+ {/* Donut */}
1232
+ <ChartCard title="Donut Chart" description="Part-to-whole">
1233
+ <DonutBreakdownChart
1234
+ data={[
1235
+ { name: 'Active', value: 68 },
1236
+ { name: 'Idle', value: 22 },
1237
+ { name: 'Offline', value: 10 },
1238
+ ]}
1239
+ nameKey="name"
1240
+ valueKey="value"
1241
+ config={{
1242
+ Active: { label: 'Active', color: 'var(--chart-2)' },
1243
+ Idle: { label: 'Idle', color: 'var(--chart-3)' },
1244
+ Offline: { label: 'Offline', color: 'var(--chart-5)' },
1245
+ }}
1246
+ centerValue="68%"
1247
+ centerLabel="Active"
1248
+ showLegend={false}
1249
+ />
1250
+ </ChartCard>
1251
+
1252
+ {/* Radar */}
1253
+ <ChartCard title="Radar Chart" description="Multi-dimensional">
1254
+ <RadarMetricChart
1255
+ data={radarData}
1256
+ labelKey="skill"
1257
+ series={[{ key: 'score', label: 'Score' }]}
1258
+ filled
1259
+ showLegend={false}
1260
+ />
1261
+ </ChartCard>
1262
+
1263
+ {/* Pie */}
1264
+ <ChartCard title="Pie Chart" description="Proportional slices">
1265
+ <PieMetricChart
1266
+ data={pieData}
1267
+ nameKey="category"
1268
+ valueKey="spend"
1269
+ showLabels
1270
+ showLegend={false}
1271
+ />
1272
+ </ChartCard>
1273
+
1274
+ {/* Radial Bar */}
1275
+ <ChartCard title="Radial Bar" description="Progress rings">
1276
+ <RadialBarMetricChart
1277
+ data={radialData}
1278
+ dataKey="value"
1279
+ nameKey="name"
1280
+ innerRadius="20%"
1281
+ outerRadius="90%"
1282
+ valueFormatter={(v) => `${v}%`}
1283
+ />
1284
+ </ChartCard>
1285
+ </div>
1286
+
1287
+ {/* Gauge row */}
1288
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
1289
+ {[
1290
+ { label: 'CPU', value: 34 },
1291
+ { label: 'Memory', value: 71 },
1292
+ { label: 'Disk', value: 58 },
1293
+ ].map((m) => (
1294
+ <ChartCard key={m.label} title={`Gauge — ${m.label}`} description="Semicircle gauge">
1295
+ <div className="flex justify-center py-2">
1296
+ <GaugeChart value={m.value} label={m.label} thresholds={gaugeThresholds} />
1297
+ </div>
1298
+ </ChartCard>
1299
+ ))}
1300
+ </div>
1301
+ </div>
1302
+ ),
1303
+ };