@wakastellar/ui 2.3.5 → 2.4.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/dist/stories/Button.d.ts +14 -0
- package/dist/stories/Button.stories.d.ts +8 -0
- package/dist/stories/Header.d.ts +11 -0
- package/dist/stories/Header.stories.d.ts +6 -0
- package/dist/stories/Page.d.ts +2 -0
- package/dist/stories/Page.stories.d.ts +6 -0
- package/package.json +11 -3
- package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
- package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
- package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
- package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
- package/src/blocks/chat/Chat.stories.tsx +466 -0
- package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
- package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
- package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
- package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
- package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
- package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
- package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
- package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
- package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
- package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
- package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
- package/src/blocks/faq/FAQ.stories.tsx +416 -0
- package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
- package/src/blocks/footer/Footer.stories.tsx +328 -0
- package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
- package/src/blocks/header/WakaHeader.stories.tsx +455 -0
- package/src/blocks/headtab/Headtab.stories.tsx +369 -0
- package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
- package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
- package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
- package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
- package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
- package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
- package/src/blocks/layout/Layout.stories.tsx +373 -0
- package/src/blocks/login/Login.stories.tsx +443 -0
- package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
- package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
- package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
- package/src/blocks/profile/Profile.stories.tsx +371 -0
- package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
- package/src/blocks/settings/Settings.stories.tsx +520 -0
- package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
- package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
- package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
- package/src/blocks/wizard/Wizard.stories.tsx +683 -0
- package/src/components/accordion/Accordion.stories.tsx +93 -0
- package/src/components/alert/Alert.stories.tsx +95 -0
- package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
- package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
- package/src/components/avatar/Avatar.stories.tsx +104 -0
- package/src/components/button/Button.stories.tsx +12 -1
- package/src/components/calendar/Calendar.stories.tsx +102 -0
- package/src/components/card/Card.stories.tsx +125 -0
- package/src/components/checkbox/Checkbox.stories.tsx +100 -0
- package/src/components/code/Code.stories.tsx +402 -0
- package/src/components/collapsible/Collapsible.stories.tsx +123 -0
- package/src/components/command/Command.stories.tsx +207 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
- package/src/components/dialog/Dialog.stories.tsx +157 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
- package/src/components/form/Form.stories.tsx +413 -0
- package/src/components/hover-card/HoverCard.stories.tsx +148 -0
- package/src/components/input-otp/InputOTP.stories.tsx +255 -0
- package/src/components/label/Label.stories.tsx +68 -0
- package/src/components/menubar/Menubar.stories.tsx +278 -0
- package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
- package/src/components/popover/Popover.stories.tsx +199 -0
- package/src/components/progress/Progress.stories.tsx +104 -0
- package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
- package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
- package/src/components/select/Select.stories.tsx +146 -0
- package/src/components/separator/Separator.stories.tsx +117 -0
- package/src/components/sheet/Sheet.stories.tsx +195 -0
- package/src/components/skeleton/Skeleton.stories.tsx +114 -0
- package/src/components/slider/Slider.stories.tsx +157 -0
- package/src/components/switch/Switch.stories.tsx +114 -0
- package/src/components/table/Table.stories.tsx +153 -0
- package/src/components/tabs/Tabs.stories.tsx +155 -0
- package/src/components/textarea/Textarea.stories.tsx +116 -0
- package/src/components/toast/Toast.stories.tsx +160 -0
- package/src/components/toggle/Toggle.stories.tsx +125 -0
- package/src/components/tooltip/Tooltip.stories.tsx +133 -0
- package/src/components/typography/Typography.stories.tsx +207 -0
- package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
- package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
- package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
- package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
- package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
- package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
- package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
- package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
- package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
- package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
- package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
- package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
- package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
- package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
- package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
- package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
- package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
- package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
- package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
- package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
- package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
- package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
- package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
- package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
- package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
- package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
- package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
- package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
- package/src/components/waka-image/WakaImage.stories.tsx +255 -0
- package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
- package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
- package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
- package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
- package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
- package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
- package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
- package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
- package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
- package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
- package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
- package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
- package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
- package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
- package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
- package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
- package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
- package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
- package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
- package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
- package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
- package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
- package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
- package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
- package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
- package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
- package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
- package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
- package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
- package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
- package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
- package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
- package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
- package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
- package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
- package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
- package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
- package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
- package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
- package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
- package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
- package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
- package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
- package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
- package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
- package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
- package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
- package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
- package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
- package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaHealthPulse } from './index'
|
|
3
|
+
import type { HealthStatus } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof WakaHealthPulse> = {
|
|
7
|
+
title: 'Components/DevOps/WakaHealthPulse',
|
|
8
|
+
component: WakaHealthPulse,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: 'An ECG-style health pulse visualization with status-specific patterns, animated waveforms, and metric display.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
argTypes: {
|
|
19
|
+
status: {
|
|
20
|
+
control: 'select',
|
|
21
|
+
options: ['healthy', 'warning', 'critical', 'down', 'unknown'],
|
|
22
|
+
description: 'Health status of the service',
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
control: 'select',
|
|
26
|
+
options: ['sm', 'md', 'lg'],
|
|
27
|
+
description: 'Size variant',
|
|
28
|
+
},
|
|
29
|
+
pulseRate: {
|
|
30
|
+
control: { type: 'number', min: 20, max: 120, step: 5 },
|
|
31
|
+
description: 'Pulse rate in beats per minute',
|
|
32
|
+
},
|
|
33
|
+
showValue: {
|
|
34
|
+
control: 'boolean',
|
|
35
|
+
description: 'Show the metric value',
|
|
36
|
+
},
|
|
37
|
+
animated: {
|
|
38
|
+
control: 'boolean',
|
|
39
|
+
description: 'Enable animations',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default meta
|
|
45
|
+
type Story = StoryObj<typeof WakaHealthPulse>
|
|
46
|
+
|
|
47
|
+
export const Default: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
status: 'healthy',
|
|
50
|
+
value: 245,
|
|
51
|
+
unit: 'req/s',
|
|
52
|
+
label: 'API Server',
|
|
53
|
+
pulseRate: 60,
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const AllStatuses: Story = {
|
|
58
|
+
render: () => {
|
|
59
|
+
const statuses: Array<{ status: HealthStatus; value: number; unit: string }> = [
|
|
60
|
+
{ status: 'healthy', value: 245, unit: 'req/s' },
|
|
61
|
+
{ status: 'warning', value: 89, unit: 'ms' },
|
|
62
|
+
{ status: 'critical', value: 523, unit: 'ms' },
|
|
63
|
+
{ status: 'down', value: 0, unit: 'req/s' },
|
|
64
|
+
{ status: 'unknown', value: 42, unit: '?' },
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="space-y-4">
|
|
69
|
+
{statuses.map(({ status, value, unit }) => (
|
|
70
|
+
<WakaHealthPulse
|
|
71
|
+
key={status}
|
|
72
|
+
status={status}
|
|
73
|
+
value={value}
|
|
74
|
+
unit={unit}
|
|
75
|
+
label={`Service (${status})`}
|
|
76
|
+
pulseRate={status === 'down' ? 0 : status === 'critical' ? 120 : 60}
|
|
77
|
+
/>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const Sizes: Story = {
|
|
85
|
+
render: () => (
|
|
86
|
+
<div className="space-y-4">
|
|
87
|
+
<div>
|
|
88
|
+
<p className="text-sm text-muted-foreground mb-2">Small</p>
|
|
89
|
+
<WakaHealthPulse
|
|
90
|
+
status="healthy"
|
|
91
|
+
value={120}
|
|
92
|
+
unit="ms"
|
|
93
|
+
label="Response Time"
|
|
94
|
+
size="sm"
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
<div>
|
|
98
|
+
<p className="text-sm text-muted-foreground mb-2">Medium (Default)</p>
|
|
99
|
+
<WakaHealthPulse
|
|
100
|
+
status="healthy"
|
|
101
|
+
value={120}
|
|
102
|
+
unit="ms"
|
|
103
|
+
label="Response Time"
|
|
104
|
+
size="md"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<div>
|
|
108
|
+
<p className="text-sm text-muted-foreground mb-2">Large</p>
|
|
109
|
+
<WakaHealthPulse
|
|
110
|
+
status="healthy"
|
|
111
|
+
value={120}
|
|
112
|
+
unit="ms"
|
|
113
|
+
label="Response Time"
|
|
114
|
+
size="lg"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const PulseRates: Story = {
|
|
122
|
+
render: () => (
|
|
123
|
+
<div className="space-y-4">
|
|
124
|
+
<div>
|
|
125
|
+
<p className="text-sm text-muted-foreground mb-2">Slow (30 BPM)</p>
|
|
126
|
+
<WakaHealthPulse
|
|
127
|
+
status="healthy"
|
|
128
|
+
value={99.9}
|
|
129
|
+
unit="%"
|
|
130
|
+
label="Uptime"
|
|
131
|
+
pulseRate={30}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<p className="text-sm text-muted-foreground mb-2">Normal (60 BPM)</p>
|
|
136
|
+
<WakaHealthPulse
|
|
137
|
+
status="healthy"
|
|
138
|
+
value={245}
|
|
139
|
+
unit="req/s"
|
|
140
|
+
label="Throughput"
|
|
141
|
+
pulseRate={60}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
<div>
|
|
145
|
+
<p className="text-sm text-muted-foreground mb-2">Fast (120 BPM)</p>
|
|
146
|
+
<WakaHealthPulse
|
|
147
|
+
status="critical"
|
|
148
|
+
value={2340}
|
|
149
|
+
unit="ms"
|
|
150
|
+
label="Latency"
|
|
151
|
+
pulseRate={120}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const NoValue: Story = {
|
|
159
|
+
render: () => (
|
|
160
|
+
<WakaHealthPulse
|
|
161
|
+
status="healthy"
|
|
162
|
+
label="Database Connection"
|
|
163
|
+
showValue={false}
|
|
164
|
+
/>
|
|
165
|
+
),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const NoAnimation: Story = {
|
|
169
|
+
render: () => (
|
|
170
|
+
<WakaHealthPulse
|
|
171
|
+
status="healthy"
|
|
172
|
+
value={245}
|
|
173
|
+
unit="req/s"
|
|
174
|
+
label="Static Display"
|
|
175
|
+
animated={false}
|
|
176
|
+
/>
|
|
177
|
+
),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export const CustomColors: Story = {
|
|
181
|
+
render: () => (
|
|
182
|
+
<WakaHealthPulse
|
|
183
|
+
status="healthy"
|
|
184
|
+
value={100}
|
|
185
|
+
unit="%"
|
|
186
|
+
label="Custom Colors"
|
|
187
|
+
colors={{
|
|
188
|
+
healthy: '#06b6d4', // cyan
|
|
189
|
+
warning: '#f97316', // orange
|
|
190
|
+
critical: '#dc2626', // red
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
),
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const ServiceDashboard: Story = {
|
|
197
|
+
render: () => {
|
|
198
|
+
const [services] = React.useState([
|
|
199
|
+
{ id: 'api', label: 'API Server', status: 'healthy' as const, value: 245, unit: 'req/s', pulseRate: 60 },
|
|
200
|
+
{ id: 'db', label: 'Database', status: 'healthy' as const, value: 23, unit: 'ms', pulseRate: 45 },
|
|
201
|
+
{ id: 'cache', label: 'Redis Cache', status: 'warning' as const, value: 89, unit: '%', pulseRate: 80 },
|
|
202
|
+
{ id: 'queue', label: 'Message Queue', status: 'critical' as const, value: 1523, unit: 'pending', pulseRate: 100 },
|
|
203
|
+
{ id: 'storage', label: 'Object Storage', status: 'down' as const, value: 0, unit: 'req/s', pulseRate: 0 },
|
|
204
|
+
{ id: 'cdn', label: 'CDN', status: 'healthy' as const, value: 12, unit: 'ms', pulseRate: 50 },
|
|
205
|
+
])
|
|
206
|
+
|
|
207
|
+
const healthyCount = services.filter(s => s.status === 'healthy').length
|
|
208
|
+
const warningCount = services.filter(s => s.status === 'warning').length
|
|
209
|
+
const criticalCount = services.filter(s => s.status === 'critical').length
|
|
210
|
+
const downCount = services.filter(s => s.status === 'down').length
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div className="p-6 rounded-xl border bg-card">
|
|
214
|
+
<div className="flex justify-between items-center mb-6">
|
|
215
|
+
<h2 className="text-xl font-bold">Service Health</h2>
|
|
216
|
+
<div className="flex gap-4 text-sm">
|
|
217
|
+
<span className="text-green-500">● {healthyCount} Healthy</span>
|
|
218
|
+
<span className="text-yellow-500">● {warningCount} Warning</span>
|
|
219
|
+
<span className="text-red-500">● {criticalCount} Critical</span>
|
|
220
|
+
<span className="text-gray-500">● {downCount} Down</span>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<div className="grid grid-cols-2 gap-4">
|
|
225
|
+
{services.map((service) => (
|
|
226
|
+
<WakaHealthPulse
|
|
227
|
+
key={service.id}
|
|
228
|
+
status={service.status}
|
|
229
|
+
value={service.value}
|
|
230
|
+
unit={service.unit}
|
|
231
|
+
label={service.label}
|
|
232
|
+
pulseRate={service.pulseRate}
|
|
233
|
+
size="md"
|
|
234
|
+
/>
|
|
235
|
+
))}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export const LiveMetrics: Story = {
|
|
243
|
+
render: () => {
|
|
244
|
+
const [value, setValue] = React.useState(245)
|
|
245
|
+
const [status, setStatus] = React.useState<HealthStatus>('healthy')
|
|
246
|
+
|
|
247
|
+
React.useEffect(() => {
|
|
248
|
+
const interval = setInterval(() => {
|
|
249
|
+
const newValue = Math.floor(Math.random() * 100) + 200
|
|
250
|
+
setValue(newValue)
|
|
251
|
+
|
|
252
|
+
if (newValue > 280) {
|
|
253
|
+
setStatus('warning')
|
|
254
|
+
} else if (newValue < 220) {
|
|
255
|
+
setStatus('critical')
|
|
256
|
+
} else {
|
|
257
|
+
setStatus('healthy')
|
|
258
|
+
}
|
|
259
|
+
}, 2000)
|
|
260
|
+
|
|
261
|
+
return () => clearInterval(interval)
|
|
262
|
+
}, [])
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div className="space-y-4">
|
|
266
|
+
<p className="text-sm text-muted-foreground">
|
|
267
|
+
Live updating metrics (simulated)
|
|
268
|
+
</p>
|
|
269
|
+
<WakaHealthPulse
|
|
270
|
+
status={status}
|
|
271
|
+
value={value}
|
|
272
|
+
unit="req/s"
|
|
273
|
+
label="API Throughput"
|
|
274
|
+
pulseRate={status === 'critical' ? 100 : status === 'warning' ? 80 : 60}
|
|
275
|
+
size="lg"
|
|
276
|
+
/>
|
|
277
|
+
</div>
|
|
278
|
+
)
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export const CompactGrid: Story = {
|
|
283
|
+
render: () => (
|
|
284
|
+
<div className="grid grid-cols-3 gap-2">
|
|
285
|
+
<WakaHealthPulse status="healthy" value={99.9} unit="%" label="Uptime" size="sm" />
|
|
286
|
+
<WakaHealthPulse status="healthy" value={23} unit="ms" label="Latency" size="sm" />
|
|
287
|
+
<WakaHealthPulse status="warning" value={85} unit="%" label="CPU" size="sm" />
|
|
288
|
+
<WakaHealthPulse status="healthy" value={64} unit="%" label="Memory" size="sm" />
|
|
289
|
+
<WakaHealthPulse status="healthy" value={245} unit="req/s" label="Requests" size="sm" />
|
|
290
|
+
<WakaHealthPulse status="critical" value={92} unit="%" label="Disk" size="sm" />
|
|
291
|
+
</div>
|
|
292
|
+
),
|
|
293
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaHeatmap, useHeatmap } from './index'
|
|
3
|
+
import type { HeatmapData, HeatmapSelection } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
// Generate sample data
|
|
7
|
+
function generateHeatmapData(rows: number, cols: number, pattern: 'random' | 'gradient' | 'checkerboard' = 'random'): HeatmapData {
|
|
8
|
+
const values: number[][] = []
|
|
9
|
+
const rowLabels: string[] = []
|
|
10
|
+
const columnLabels: string[] = []
|
|
11
|
+
|
|
12
|
+
for (let r = 0; r < rows; r++) {
|
|
13
|
+
rowLabels.push(`Row ${r + 1}`)
|
|
14
|
+
const row: number[] = []
|
|
15
|
+
for (let c = 0; c < cols; c++) {
|
|
16
|
+
if (r === 0) columnLabels.push(`Col ${c + 1}`)
|
|
17
|
+
|
|
18
|
+
let value: number
|
|
19
|
+
switch (pattern) {
|
|
20
|
+
case 'gradient':
|
|
21
|
+
value = (r + c) / (rows + cols - 2)
|
|
22
|
+
break
|
|
23
|
+
case 'checkerboard':
|
|
24
|
+
value = (r + c) % 2 === 0 ? 0.8 : 0.2
|
|
25
|
+
break
|
|
26
|
+
default:
|
|
27
|
+
value = Math.random()
|
|
28
|
+
}
|
|
29
|
+
row.push(value)
|
|
30
|
+
}
|
|
31
|
+
values.push(row)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { values, rowLabels, columnLabels }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Weekly activity data (hours worked per day)
|
|
38
|
+
const weeklyActivityData: HeatmapData = {
|
|
39
|
+
values: [
|
|
40
|
+
[8, 7, 6, 8, 7, 2, 0],
|
|
41
|
+
[7, 8, 9, 7, 8, 4, 1],
|
|
42
|
+
[6, 7, 8, 8, 6, 3, 0],
|
|
43
|
+
[9, 8, 7, 9, 8, 5, 2],
|
|
44
|
+
[7, 6, 8, 7, 7, 3, 1],
|
|
45
|
+
],
|
|
46
|
+
rowLabels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5'],
|
|
47
|
+
columnLabels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Correlation matrix
|
|
51
|
+
const correlationData: HeatmapData = {
|
|
52
|
+
values: [
|
|
53
|
+
[1.0, 0.8, 0.2, -0.3, 0.5],
|
|
54
|
+
[0.8, 1.0, 0.1, -0.5, 0.6],
|
|
55
|
+
[0.2, 0.1, 1.0, 0.4, -0.2],
|
|
56
|
+
[-0.3, -0.5, 0.4, 1.0, -0.1],
|
|
57
|
+
[0.5, 0.6, -0.2, -0.1, 1.0],
|
|
58
|
+
],
|
|
59
|
+
rowLabels: ['Revenue', 'Users', 'Bounce Rate', 'Load Time', 'Conversions'],
|
|
60
|
+
columnLabels: ['Revenue', 'Users', 'Bounce Rate', 'Load Time', 'Conversions'],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const meta: Meta<typeof WakaHeatmap> = {
|
|
64
|
+
title: 'Components/Charts/WakaHeatmap',
|
|
65
|
+
component: WakaHeatmap,
|
|
66
|
+
parameters: {
|
|
67
|
+
layout: 'centered',
|
|
68
|
+
docs: {
|
|
69
|
+
description: {
|
|
70
|
+
component: 'An interactive heatmap component with multiple color schemes, zoom/pan controls, tooltips, and export functionality.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
tags: ['autodocs'],
|
|
75
|
+
argTypes: {
|
|
76
|
+
colorSchemeName: {
|
|
77
|
+
control: 'select',
|
|
78
|
+
options: ['blues', 'greens', 'reds', 'viridis', 'plasma', 'coolwarm', 'rdylgn'],
|
|
79
|
+
description: 'Predefined color scheme',
|
|
80
|
+
},
|
|
81
|
+
cellSize: {
|
|
82
|
+
control: { type: 'range', min: 12, max: 48, step: 4 },
|
|
83
|
+
description: 'Cell size in pixels',
|
|
84
|
+
},
|
|
85
|
+
cellGap: {
|
|
86
|
+
control: { type: 'range', min: 0, max: 8, step: 1 },
|
|
87
|
+
description: 'Gap between cells',
|
|
88
|
+
},
|
|
89
|
+
showRowLabels: {
|
|
90
|
+
control: 'boolean',
|
|
91
|
+
description: 'Show row labels',
|
|
92
|
+
},
|
|
93
|
+
showColumnLabels: {
|
|
94
|
+
control: 'boolean',
|
|
95
|
+
description: 'Show column labels',
|
|
96
|
+
},
|
|
97
|
+
showLegend: {
|
|
98
|
+
control: 'boolean',
|
|
99
|
+
description: 'Show color legend',
|
|
100
|
+
},
|
|
101
|
+
enableZoom: {
|
|
102
|
+
control: 'boolean',
|
|
103
|
+
description: 'Enable zoom controls',
|
|
104
|
+
},
|
|
105
|
+
enablePan: {
|
|
106
|
+
control: 'boolean',
|
|
107
|
+
description: 'Enable pan mode',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default meta
|
|
113
|
+
type Story = StoryObj<typeof WakaHeatmap>
|
|
114
|
+
|
|
115
|
+
export const Default: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
data: weeklyActivityData,
|
|
118
|
+
title: 'Weekly Activity',
|
|
119
|
+
cellSize: 32,
|
|
120
|
+
},
|
|
121
|
+
render: (args) => <WakaHeatmap {...args} valueFormatter={(v) => `${v}h`} />,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const ColorSchemes: Story = {
|
|
125
|
+
render: () => {
|
|
126
|
+
const data = generateHeatmapData(5, 8, 'gradient')
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="space-y-8">
|
|
130
|
+
<WakaHeatmap data={data} colorSchemeName="blues" title="Blues" cellSize={24} />
|
|
131
|
+
<WakaHeatmap data={data} colorSchemeName="greens" title="Greens" cellSize={24} />
|
|
132
|
+
<WakaHeatmap data={data} colorSchemeName="viridis" title="Viridis" cellSize={24} />
|
|
133
|
+
<WakaHeatmap data={data} colorSchemeName="plasma" title="Plasma" cellSize={24} />
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const DivergingSchemes: Story = {
|
|
140
|
+
render: () => (
|
|
141
|
+
<div className="space-y-8">
|
|
142
|
+
<WakaHeatmap
|
|
143
|
+
data={correlationData}
|
|
144
|
+
colorSchemeName="coolwarm"
|
|
145
|
+
title="Correlation Matrix (Coolwarm)"
|
|
146
|
+
cellSize={48}
|
|
147
|
+
valueFormatter={(v) => v.toFixed(2)}
|
|
148
|
+
/>
|
|
149
|
+
<WakaHeatmap
|
|
150
|
+
data={correlationData}
|
|
151
|
+
colorSchemeName="rdylgn"
|
|
152
|
+
title="Correlation Matrix (Red-Yellow-Green)"
|
|
153
|
+
cellSize={48}
|
|
154
|
+
valueFormatter={(v) => v.toFixed(2)}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const CellSizes: Story = {
|
|
161
|
+
render: () => {
|
|
162
|
+
const data = generateHeatmapData(4, 6, 'random')
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div className="space-y-8">
|
|
166
|
+
<WakaHeatmap data={data} cellSize={16} cellGap={1} title="Small (16px)" />
|
|
167
|
+
<WakaHeatmap data={data} cellSize={28} cellGap={2} title="Medium (28px)" />
|
|
168
|
+
<WakaHeatmap data={data} cellSize={40} cellGap={3} title="Large (40px)" />
|
|
169
|
+
</div>
|
|
170
|
+
)
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const WithoutLabels: Story = {
|
|
175
|
+
render: () => {
|
|
176
|
+
const data = generateHeatmapData(8, 12, 'random')
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div className="space-y-8">
|
|
180
|
+
<WakaHeatmap
|
|
181
|
+
data={data}
|
|
182
|
+
showRowLabels
|
|
183
|
+
showColumnLabels
|
|
184
|
+
title="With Labels"
|
|
185
|
+
cellSize={20}
|
|
186
|
+
/>
|
|
187
|
+
<WakaHeatmap
|
|
188
|
+
data={data}
|
|
189
|
+
showRowLabels={false}
|
|
190
|
+
showColumnLabels={false}
|
|
191
|
+
title="Without Labels"
|
|
192
|
+
cellSize={20}
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
)
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const LargeHeatmap: Story = {
|
|
200
|
+
render: () => {
|
|
201
|
+
const data = generateHeatmapData(20, 30, 'random')
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<WakaHeatmap
|
|
205
|
+
data={data}
|
|
206
|
+
title="Large Heatmap (20x30)"
|
|
207
|
+
cellSize={16}
|
|
208
|
+
cellGap={1}
|
|
209
|
+
width={700}
|
|
210
|
+
height={450}
|
|
211
|
+
showRowLabels={false}
|
|
212
|
+
showColumnLabels={false}
|
|
213
|
+
/>
|
|
214
|
+
)
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export const InteractiveSelection: Story = {
|
|
219
|
+
render: () => {
|
|
220
|
+
const [selected, setSelected] = React.useState<HeatmapSelection | null>(null)
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div className="space-y-4">
|
|
224
|
+
<WakaHeatmap
|
|
225
|
+
data={weeklyActivityData}
|
|
226
|
+
title="Click to Select"
|
|
227
|
+
cellSize={32}
|
|
228
|
+
enableSelection
|
|
229
|
+
onCellClick={(cell) => setSelected(cell)}
|
|
230
|
+
onSelectionChange={(selection) => console.log('Selection:', selection)}
|
|
231
|
+
/>
|
|
232
|
+
{selected && (
|
|
233
|
+
<div className="p-4 border rounded-lg bg-muted/50">
|
|
234
|
+
<p className="text-sm">
|
|
235
|
+
Selected: Row {selected.row + 1}, Column {selected.col + 1}
|
|
236
|
+
</p>
|
|
237
|
+
<p className="text-lg font-semibold">{selected.value} hours</p>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
)
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const ZoomAndPan: Story = {
|
|
246
|
+
render: () => {
|
|
247
|
+
const data = generateHeatmapData(15, 20, 'random')
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<WakaHeatmap
|
|
251
|
+
data={data}
|
|
252
|
+
title="Zoom & Pan (use scroll wheel and pan button)"
|
|
253
|
+
cellSize={18}
|
|
254
|
+
width={500}
|
|
255
|
+
height={350}
|
|
256
|
+
enableZoom
|
|
257
|
+
enablePan
|
|
258
|
+
initialZoom={1}
|
|
259
|
+
minZoom={0.5}
|
|
260
|
+
maxZoom={3}
|
|
261
|
+
/>
|
|
262
|
+
)
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const MinMaxIndicators: Story = {
|
|
267
|
+
render: () => (
|
|
268
|
+
<WakaHeatmap
|
|
269
|
+
data={weeklyActivityData}
|
|
270
|
+
title="With Min/Max Indicators"
|
|
271
|
+
cellSize={32}
|
|
272
|
+
showMinMax
|
|
273
|
+
valueFormatter={(v) => `${v}h`}
|
|
274
|
+
/>
|
|
275
|
+
),
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export const NoTooltip: Story = {
|
|
279
|
+
render: () => (
|
|
280
|
+
<WakaHeatmap
|
|
281
|
+
data={weeklyActivityData}
|
|
282
|
+
title="Without Tooltip"
|
|
283
|
+
cellSize={32}
|
|
284
|
+
showTooltip={false}
|
|
285
|
+
/>
|
|
286
|
+
),
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export const WithHook: Story = {
|
|
290
|
+
render: () => {
|
|
291
|
+
const heatmap = useHeatmap({
|
|
292
|
+
initialZoom: 1,
|
|
293
|
+
minZoom: 0.5,
|
|
294
|
+
maxZoom: 2,
|
|
295
|
+
onSelectionChange: (selection) => console.log('Selection changed:', selection),
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div className="space-y-4">
|
|
300
|
+
<div className="flex gap-2">
|
|
301
|
+
<button
|
|
302
|
+
onClick={heatmap.zoomIn}
|
|
303
|
+
className="px-3 py-1 text-sm border rounded hover:bg-muted"
|
|
304
|
+
>
|
|
305
|
+
Zoom In
|
|
306
|
+
</button>
|
|
307
|
+
<button
|
|
308
|
+
onClick={heatmap.zoomOut}
|
|
309
|
+
className="px-3 py-1 text-sm border rounded hover:bg-muted"
|
|
310
|
+
>
|
|
311
|
+
Zoom Out
|
|
312
|
+
</button>
|
|
313
|
+
<button
|
|
314
|
+
onClick={heatmap.resetZoom}
|
|
315
|
+
className="px-3 py-1 text-sm border rounded hover:bg-muted"
|
|
316
|
+
>
|
|
317
|
+
Reset
|
|
318
|
+
</button>
|
|
319
|
+
<span className="px-2 py-1 text-sm bg-muted rounded">
|
|
320
|
+
Zoom: {Math.round(heatmap.zoom * 100)}%
|
|
321
|
+
</span>
|
|
322
|
+
</div>
|
|
323
|
+
<WakaHeatmap
|
|
324
|
+
data={weeklyActivityData}
|
|
325
|
+
title="Controlled via Hook"
|
|
326
|
+
cellSize={32}
|
|
327
|
+
enableZoom={false}
|
|
328
|
+
enablePan={false}
|
|
329
|
+
initialZoom={heatmap.zoom}
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
)
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export const CorrelationMatrix: Story = {
|
|
337
|
+
render: () => (
|
|
338
|
+
<WakaHeatmap
|
|
339
|
+
data={correlationData}
|
|
340
|
+
title="Feature Correlation Matrix"
|
|
341
|
+
colorSchemeName="coolwarm"
|
|
342
|
+
cellSize={60}
|
|
343
|
+
valueFormatter={(v) => v.toFixed(2)}
|
|
344
|
+
/>
|
|
345
|
+
),
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export const ServerLoadExample: Story = {
|
|
349
|
+
render: () => {
|
|
350
|
+
const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`)
|
|
351
|
+
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
352
|
+
|
|
353
|
+
const values = days.map((_, dayIndex) =>
|
|
354
|
+
hours.map((_, hourIndex) => {
|
|
355
|
+
// Simulate server load pattern: higher during business hours, lower at night/weekends
|
|
356
|
+
const isWeekend = dayIndex >= 5
|
|
357
|
+
const isBusinessHours = hourIndex >= 9 && hourIndex <= 17
|
|
358
|
+
const baseLoad = isWeekend ? 20 : isBusinessHours ? 70 : 30
|
|
359
|
+
return Math.min(100, Math.max(0, baseLoad + (Math.random() - 0.5) * 40))
|
|
360
|
+
})
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<WakaHeatmap
|
|
365
|
+
data={{ values, rowLabels: days, columnLabels: hours }}
|
|
366
|
+
title="Server Load by Hour"
|
|
367
|
+
colorSchemeName="reds"
|
|
368
|
+
cellSize={16}
|
|
369
|
+
cellGap={1}
|
|
370
|
+
width={700}
|
|
371
|
+
height={250}
|
|
372
|
+
valueFormatter={(v) => `${Math.round(v)}%`}
|
|
373
|
+
/>
|
|
374
|
+
)
|
|
375
|
+
},
|
|
376
|
+
}
|