@wakastellar/ui 2.3.4 → 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.
Files changed (194) hide show
  1. package/dist/blocks/dashboard/index.d.ts +4 -1
  2. package/dist/blocks/empty-states/index.d.ts +4 -1
  3. package/dist/blocks/error-pages/index.d.ts +4 -1
  4. package/dist/blocks/index.d.ts +1 -1
  5. package/dist/blocks/landing/index.d.ts +4 -1
  6. package/dist/blocks/pricing/index.d.ts +5 -1
  7. package/dist/blocks/sidebar/index.d.ts +5 -1
  8. package/dist/index.cjs.js +130 -130
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.es.js +7905 -7856
  11. package/dist/stories/Button.d.ts +14 -0
  12. package/dist/stories/Button.stories.d.ts +8 -0
  13. package/dist/stories/Header.d.ts +11 -0
  14. package/dist/stories/Header.stories.d.ts +6 -0
  15. package/dist/stories/Page.d.ts +2 -0
  16. package/dist/stories/Page.stories.d.ts +6 -0
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/link.d.ts +23 -0
  19. package/package.json +11 -3
  20. package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
  21. package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
  22. package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
  23. package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
  24. package/src/blocks/chat/Chat.stories.tsx +466 -0
  25. package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
  26. package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
  27. package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
  28. package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
  29. package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
  30. package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
  31. package/src/blocks/dashboard/index.tsx +68 -27
  32. package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
  33. package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
  34. package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
  35. package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
  36. package/src/blocks/empty-states/index.tsx +26 -8
  37. package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
  38. package/src/blocks/error-pages/index.tsx +26 -8
  39. package/src/blocks/faq/FAQ.stories.tsx +416 -0
  40. package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
  41. package/src/blocks/footer/Footer.stories.tsx +328 -0
  42. package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
  43. package/src/blocks/header/WakaHeader.stories.tsx +455 -0
  44. package/src/blocks/headtab/Headtab.stories.tsx +369 -0
  45. package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
  46. package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
  47. package/src/blocks/index.ts +1 -1
  48. package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
  49. package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
  50. package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
  51. package/src/blocks/landing/index.tsx +125 -66
  52. package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
  53. package/src/blocks/layout/Layout.stories.tsx +373 -0
  54. package/src/blocks/login/Login.stories.tsx +443 -0
  55. package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
  56. package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
  57. package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
  58. package/src/blocks/pricing/index.tsx +38 -4
  59. package/src/blocks/profile/Profile.stories.tsx +371 -0
  60. package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
  61. package/src/blocks/settings/Settings.stories.tsx +520 -0
  62. package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
  63. package/src/blocks/sidebar/index.tsx +49 -20
  64. package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
  65. package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
  66. package/src/blocks/wizard/Wizard.stories.tsx +683 -0
  67. package/src/components/accordion/Accordion.stories.tsx +93 -0
  68. package/src/components/alert/Alert.stories.tsx +95 -0
  69. package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
  70. package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
  71. package/src/components/avatar/Avatar.stories.tsx +104 -0
  72. package/src/components/button/Button.stories.tsx +12 -1
  73. package/src/components/calendar/Calendar.stories.tsx +102 -0
  74. package/src/components/card/Card.stories.tsx +125 -0
  75. package/src/components/checkbox/Checkbox.stories.tsx +100 -0
  76. package/src/components/code/Code.stories.tsx +402 -0
  77. package/src/components/collapsible/Collapsible.stories.tsx +123 -0
  78. package/src/components/command/Command.stories.tsx +207 -0
  79. package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
  80. package/src/components/dialog/Dialog.stories.tsx +157 -0
  81. package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
  82. package/src/components/form/Form.stories.tsx +413 -0
  83. package/src/components/hover-card/HoverCard.stories.tsx +148 -0
  84. package/src/components/input-otp/InputOTP.stories.tsx +255 -0
  85. package/src/components/label/Label.stories.tsx +68 -0
  86. package/src/components/menubar/Menubar.stories.tsx +278 -0
  87. package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
  88. package/src/components/popover/Popover.stories.tsx +199 -0
  89. package/src/components/progress/Progress.stories.tsx +104 -0
  90. package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
  91. package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
  92. package/src/components/select/Select.stories.tsx +146 -0
  93. package/src/components/separator/Separator.stories.tsx +117 -0
  94. package/src/components/sheet/Sheet.stories.tsx +195 -0
  95. package/src/components/skeleton/Skeleton.stories.tsx +114 -0
  96. package/src/components/slider/Slider.stories.tsx +157 -0
  97. package/src/components/switch/Switch.stories.tsx +114 -0
  98. package/src/components/table/Table.stories.tsx +153 -0
  99. package/src/components/tabs/Tabs.stories.tsx +155 -0
  100. package/src/components/textarea/Textarea.stories.tsx +116 -0
  101. package/src/components/toast/Toast.stories.tsx +160 -0
  102. package/src/components/toggle/Toggle.stories.tsx +125 -0
  103. package/src/components/tooltip/Tooltip.stories.tsx +133 -0
  104. package/src/components/typography/Typography.stories.tsx +207 -0
  105. package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
  106. package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
  107. package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
  108. package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
  109. package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
  110. package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
  111. package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
  112. package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
  113. package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
  114. package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
  115. package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
  116. package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
  117. package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
  118. package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
  119. package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
  120. package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
  121. package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
  122. package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
  123. package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
  124. package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
  125. package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
  126. package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
  127. package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
  128. package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
  129. package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
  130. package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
  131. package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
  132. package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
  133. package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
  134. package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
  135. package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
  136. package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
  137. package/src/components/waka-image/WakaImage.stories.tsx +255 -0
  138. package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
  139. package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
  140. package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
  141. package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
  142. package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
  143. package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
  144. package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
  145. package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
  146. package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
  147. package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
  148. package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
  149. package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
  150. package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
  151. package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
  152. package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
  153. package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
  154. package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
  155. package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
  156. package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
  157. package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
  158. package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
  159. package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
  160. package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
  161. package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
  162. package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
  163. package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
  164. package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
  165. package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
  166. package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
  167. package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
  168. package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
  169. package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
  170. package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
  171. package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
  172. package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
  173. package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
  174. package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
  175. package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
  176. package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
  177. package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
  178. package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
  179. package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
  180. package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
  181. package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
  182. package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
  183. package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
  184. package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
  185. package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
  186. package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
  187. package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
  188. package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
  189. package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
  190. package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
  191. package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
  192. package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
  193. package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
  194. package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
@@ -0,0 +1,312 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaLogViewer, defaultLogs } from './index'
3
+ import type { LogEntry, LogLevel } from './index'
4
+ import * as React from 'react'
5
+
6
+ const generateLogs = (count: number): LogEntry[] => {
7
+ const sources = ['api', 'db', 'cache', 'auth', 'worker', 'main']
8
+ const levels: LogLevel[] = ['info', 'debug', 'warn', 'error', 'trace']
9
+ const messages = [
10
+ 'Request processed successfully',
11
+ 'Loading configuration from environment',
12
+ 'Connection established',
13
+ 'Cache miss, fetching from database',
14
+ 'User authentication successful',
15
+ 'Processing batch job',
16
+ 'Deprecated API version detected',
17
+ 'High memory usage warning',
18
+ 'Failed to connect to service',
19
+ 'Query execution timeout',
20
+ ]
21
+
22
+ return Array.from({ length: count }, (_, i) => ({
23
+ id: `log-${i}`,
24
+ timestamp: new Date(Date.now() - (count - i) * 1000),
25
+ level: levels[Math.floor(Math.random() * levels.length)],
26
+ message: messages[Math.floor(Math.random() * messages.length)],
27
+ source: sources[Math.floor(Math.random() * sources.length)],
28
+ }))
29
+ }
30
+
31
+ const logsWithMetadata: LogEntry[] = [
32
+ {
33
+ id: '1',
34
+ timestamp: new Date(),
35
+ level: 'info',
36
+ message: 'Application started successfully',
37
+ source: 'main',
38
+ },
39
+ {
40
+ id: '2',
41
+ timestamp: new Date(),
42
+ level: 'debug',
43
+ message: 'Loading configuration from /etc/app/config.yaml',
44
+ source: 'config',
45
+ metadata: { path: '/etc/app/config.yaml', format: 'yaml', size: '2.4KB' },
46
+ },
47
+ {
48
+ id: '3',
49
+ timestamp: new Date(),
50
+ level: 'info',
51
+ message: 'Database connection established',
52
+ source: 'db',
53
+ metadata: { host: 'postgres.example.com', port: 5432, database: 'production' },
54
+ },
55
+ {
56
+ id: '4',
57
+ timestamp: new Date(),
58
+ level: 'warn',
59
+ message: 'Deprecated API version detected, please upgrade to v2',
60
+ source: 'api',
61
+ metadata: { currentVersion: 'v1', recommendedVersion: 'v2', deprecationDate: '2024-06-01' },
62
+ },
63
+ {
64
+ id: '5',
65
+ timestamp: new Date(),
66
+ level: 'error',
67
+ message: 'Failed to connect to Redis cache',
68
+ source: 'cache',
69
+ metadata: { host: 'redis.example.com', port: 6379 },
70
+ stackTrace: `Error: Connection refused
71
+ at RedisClient.connect (/app/node_modules/redis/lib/client.js:123:15)
72
+ at async CacheService.init (/app/src/services/cache.ts:45:5)
73
+ at async bootstrap (/app/src/main.ts:22:3)`,
74
+ },
75
+ {
76
+ id: '6',
77
+ timestamp: new Date(),
78
+ level: 'info',
79
+ message: 'Retry connection attempt 1/3',
80
+ source: 'cache',
81
+ },
82
+ {
83
+ id: '7',
84
+ timestamp: new Date(),
85
+ level: 'debug',
86
+ message: 'Request: GET /api/users/123',
87
+ source: 'api',
88
+ metadata: { method: 'GET', path: '/api/users/123', statusCode: 200, duration: '45ms' },
89
+ },
90
+ {
91
+ id: '8',
92
+ timestamp: new Date(),
93
+ level: 'trace',
94
+ message: 'SQL query executed',
95
+ source: 'db',
96
+ metadata: { query: 'SELECT * FROM users WHERE id = $1', params: [123], duration: '12ms' },
97
+ },
98
+ ]
99
+
100
+ const meta: Meta<typeof WakaLogViewer> = {
101
+ title: 'Components/DevOps/WakaLogViewer',
102
+ component: WakaLogViewer,
103
+ parameters: {
104
+ layout: 'centered',
105
+ docs: {
106
+ description: {
107
+ component: 'A log viewer with level filtering, search, expandable metadata, stack traces, streaming support, and export functionality.',
108
+ },
109
+ },
110
+ },
111
+ tags: ['autodocs'],
112
+ argTypes: {
113
+ showTimestamp: {
114
+ control: 'boolean',
115
+ description: 'Show timestamps',
116
+ },
117
+ showSource: {
118
+ control: 'boolean',
119
+ description: 'Show log source',
120
+ },
121
+ autoScroll: {
122
+ control: 'boolean',
123
+ description: 'Enable auto-scroll to bottom',
124
+ },
125
+ maxLogs: {
126
+ control: { type: 'number', min: 100, max: 5000, step: 100 },
127
+ description: 'Maximum number of logs to display',
128
+ },
129
+ },
130
+ }
131
+
132
+ export default meta
133
+ type Story = StoryObj<typeof WakaLogViewer>
134
+
135
+ export const Default: Story = {
136
+ args: {
137
+ logs: defaultLogs,
138
+ showTimestamp: true,
139
+ showSource: true,
140
+ autoScroll: true,
141
+ maxLogs: 1000,
142
+ },
143
+ render: (args) => (
144
+ <div className="w-[800px] h-[500px]">
145
+ <WakaLogViewer {...args} />
146
+ </div>
147
+ ),
148
+ }
149
+
150
+ export const WithMetadata: Story = {
151
+ render: () => (
152
+ <div className="w-[800px] h-[500px]">
153
+ <p className="text-sm text-muted-foreground mb-4">
154
+ Click on log entries to expand metadata and stack traces
155
+ </p>
156
+ <WakaLogViewer logs={logsWithMetadata} />
157
+ </div>
158
+ ),
159
+ }
160
+
161
+ export const LiveStreaming: Story = {
162
+ render: () => {
163
+ const [logs, setLogs] = React.useState<LogEntry[]>(defaultLogs)
164
+ const [isStreaming, setIsStreaming] = React.useState(true)
165
+
166
+ React.useEffect(() => {
167
+ if (!isStreaming) return
168
+
169
+ const levels: LogLevel[] = ['info', 'debug', 'warn', 'error']
170
+ const sources = ['api', 'db', 'cache', 'worker']
171
+ const messages = [
172
+ 'Request processed',
173
+ 'Cache hit',
174
+ 'Query executed',
175
+ 'Task completed',
176
+ 'High latency detected',
177
+ 'Connection timeout',
178
+ ]
179
+
180
+ const interval = setInterval(() => {
181
+ const newLog: LogEntry = {
182
+ id: `log-${Date.now()}`,
183
+ timestamp: new Date(),
184
+ level: levels[Math.floor(Math.random() * levels.length)],
185
+ message: messages[Math.floor(Math.random() * messages.length)],
186
+ source: sources[Math.floor(Math.random() * sources.length)],
187
+ }
188
+ setLogs(prev => [...prev.slice(-99), newLog])
189
+ }, 500)
190
+
191
+ return () => clearInterval(interval)
192
+ }, [isStreaming])
193
+
194
+ return (
195
+ <div className="w-[800px] h-[500px]">
196
+ <WakaLogViewer
197
+ logs={logs}
198
+ isStreaming={isStreaming}
199
+ onToggleStreaming={() => setIsStreaming(!isStreaming)}
200
+ onClear={() => setLogs([])}
201
+ />
202
+ </div>
203
+ )
204
+ },
205
+ }
206
+
207
+ export const ManyLogs: Story = {
208
+ render: () => (
209
+ <div className="w-[800px] h-[500px]">
210
+ <p className="text-sm text-muted-foreground mb-4">
211
+ 500 logs with virtualization
212
+ </p>
213
+ <WakaLogViewer logs={generateLogs(500)} />
214
+ </div>
215
+ ),
216
+ }
217
+
218
+ export const NoTimestamps: Story = {
219
+ render: () => (
220
+ <div className="w-[800px] h-[400px]">
221
+ <WakaLogViewer logs={logsWithMetadata} showTimestamp={false} />
222
+ </div>
223
+ ),
224
+ }
225
+
226
+ export const NoSource: Story = {
227
+ render: () => (
228
+ <div className="w-[800px] h-[400px]">
229
+ <WakaLogViewer logs={logsWithMetadata} showSource={false} />
230
+ </div>
231
+ ),
232
+ }
233
+
234
+ export const WithCallbacks: Story = {
235
+ render: () => {
236
+ const [logs, setLogs] = React.useState<LogEntry[]>(logsWithMetadata)
237
+
238
+ return (
239
+ <div className="w-[800px] h-[500px]">
240
+ <WakaLogViewer
241
+ logs={logs}
242
+ onClear={() => setLogs([])}
243
+ onExport={(filteredLogs) => {
244
+ console.log('Exporting logs:', filteredLogs)
245
+ alert(`Exporting ${filteredLogs.length} logs`)
246
+ }}
247
+ onLoadMore={() => {
248
+ console.log('Loading more logs...')
249
+ setLogs(prev => [...generateLogs(10), ...prev])
250
+ }}
251
+ />
252
+ </div>
253
+ )
254
+ },
255
+ }
256
+
257
+ export const ErrorsOnly: Story = {
258
+ render: () => {
259
+ const errorLogs: LogEntry[] = [
260
+ {
261
+ id: '1',
262
+ timestamp: new Date(Date.now() - 5 * 60 * 1000),
263
+ level: 'error',
264
+ message: 'TypeError: Cannot read property "id" of undefined',
265
+ source: 'api',
266
+ stackTrace: `TypeError: Cannot read property 'id' of undefined
267
+ at UserController.getUser (/app/src/controllers/user.ts:45:15)
268
+ at processRequest (/app/src/middleware/router.ts:89:12)`,
269
+ },
270
+ {
271
+ id: '2',
272
+ timestamp: new Date(Date.now() - 10 * 60 * 1000),
273
+ level: 'error',
274
+ message: 'ECONNREFUSED: Connection refused to database',
275
+ source: 'db',
276
+ metadata: { host: 'db.example.com', port: 5432 },
277
+ },
278
+ {
279
+ id: '3',
280
+ timestamp: new Date(Date.now() - 15 * 60 * 1000),
281
+ level: 'error',
282
+ message: 'OutOfMemoryError: Heap space exceeded',
283
+ source: 'worker',
284
+ stackTrace: `java.lang.OutOfMemoryError: Java heap space
285
+ at java.util.Arrays.copyOf(Arrays.java:3210)
286
+ at java.util.ArrayList.grow(ArrayList.java:265)`,
287
+ },
288
+ ]
289
+
290
+ return (
291
+ <div className="w-[800px] h-[400px]">
292
+ <WakaLogViewer logs={errorLogs} title="Error Logs" />
293
+ </div>
294
+ )
295
+ },
296
+ }
297
+
298
+ export const CustomTitle: Story = {
299
+ render: () => (
300
+ <div className="w-[800px] h-[400px]">
301
+ <WakaLogViewer logs={logsWithMetadata} title="Application Logs - Production" />
302
+ </div>
303
+ ),
304
+ }
305
+
306
+ export const Empty: Story = {
307
+ render: () => (
308
+ <div className="w-[800px] h-[300px]">
309
+ <WakaLogViewer logs={[]} />
310
+ </div>
311
+ ),
312
+ }
@@ -0,0 +1,374 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaLootBox, useLootBox } from './index'
3
+ import type { LootBoxItem, LootBoxRarity } from './index'
4
+ import * as React from 'react'
5
+ import { Sword, Shield, Crown, Gem, Star, Zap, Heart, Coins } from 'lucide-react'
6
+
7
+ const sampleItems: LootBoxItem[] = [
8
+ {
9
+ id: '1',
10
+ name: 'Legendary Sword',
11
+ description: 'A blade forged in dragon fire',
12
+ rarity: 'legendary',
13
+ icon: <Sword className="h-12 w-12" />,
14
+ },
15
+ {
16
+ id: '2',
17
+ name: 'Epic Shield',
18
+ description: 'Unbreakable defense',
19
+ rarity: 'epic',
20
+ icon: <Shield className="h-12 w-12" />,
21
+ },
22
+ {
23
+ id: '3',
24
+ name: 'Rare Gem',
25
+ description: 'A precious stone',
26
+ rarity: 'rare',
27
+ icon: <Gem className="h-12 w-12" />,
28
+ quantity: 5,
29
+ },
30
+ ]
31
+
32
+ const commonLoot: LootBoxItem[] = [
33
+ { id: 'c1', name: 'Gold Coins', description: '+100 gold', rarity: 'common', icon: <Coins className="h-10 w-10" />, quantity: 100 },
34
+ { id: 'c2', name: 'Health Potion', description: 'Restore 50 HP', rarity: 'common', icon: <Heart className="h-10 w-10" />, quantity: 3 },
35
+ ]
36
+
37
+ const rareLoot: LootBoxItem[] = [
38
+ { id: 'r1', name: 'Energy Crystal', description: '+50 Energy', rarity: 'rare', icon: <Zap className="h-10 w-10" /> },
39
+ { id: 'r2', name: 'Sapphire', description: 'A rare gemstone', rarity: 'rare', icon: <Gem className="h-10 w-10" /> },
40
+ ]
41
+
42
+ const epicLoot: LootBoxItem[] = [
43
+ { id: 'e1', name: 'Phoenix Feather', description: 'Grants revival', rarity: 'epic', icon: <Star className="h-10 w-10" /> },
44
+ ]
45
+
46
+ const legendaryLoot: LootBoxItem[] = [
47
+ { id: 'l1', name: 'Crown of Kings', description: 'Ultimate power', rarity: 'legendary', icon: <Crown className="h-10 w-10" /> },
48
+ ]
49
+
50
+ const meta: Meta<typeof WakaLootBox> = {
51
+ title: 'Components/Gamification/WakaLootBox',
52
+ component: WakaLootBox,
53
+ parameters: {
54
+ layout: 'centered',
55
+ docs: {
56
+ description: {
57
+ component: 'A loot box opening experience with shake animation, glow effects, particle bursts, and item reveal sequence.',
58
+ },
59
+ },
60
+ },
61
+ tags: ['autodocs'],
62
+ argTypes: {
63
+ variant: {
64
+ control: 'select',
65
+ options: ['chest', 'crate', 'gift', 'orb'],
66
+ description: 'Visual style of the loot box',
67
+ },
68
+ size: {
69
+ control: 'select',
70
+ options: ['sm', 'default', 'lg'],
71
+ description: 'Size of the loot box',
72
+ },
73
+ autoReveal: {
74
+ control: 'boolean',
75
+ description: 'Auto-reveal items without navigation',
76
+ },
77
+ autoRevealDelay: {
78
+ control: { type: 'number', min: 500, max: 5000, step: 500 },
79
+ description: 'Delay between auto-reveal items (ms)',
80
+ },
81
+ disabled: {
82
+ control: 'boolean',
83
+ description: 'Disable interaction',
84
+ },
85
+ },
86
+ }
87
+
88
+ export default meta
89
+ type Story = StoryObj<typeof WakaLootBox>
90
+
91
+ export const Default: Story = {
92
+ args: {
93
+ items: sampleItems,
94
+ variant: 'chest',
95
+ size: 'default',
96
+ autoReveal: false,
97
+ autoRevealDelay: 1500,
98
+ disabled: false,
99
+ },
100
+ render: (args) => <WakaLootBox {...args} />,
101
+ }
102
+
103
+ export const Variants: Story = {
104
+ render: () => (
105
+ <div className="flex flex-wrap gap-8 items-end">
106
+ <div className="text-center">
107
+ <p className="text-sm text-muted-foreground mb-2">Chest</p>
108
+ <WakaLootBox items={sampleItems} variant="chest" />
109
+ </div>
110
+ <div className="text-center">
111
+ <p className="text-sm text-muted-foreground mb-2">Crate</p>
112
+ <WakaLootBox items={rareLoot} variant="crate" />
113
+ </div>
114
+ <div className="text-center">
115
+ <p className="text-sm text-muted-foreground mb-2">Gift</p>
116
+ <WakaLootBox items={epicLoot} variant="gift" />
117
+ </div>
118
+ <div className="text-center">
119
+ <p className="text-sm text-muted-foreground mb-2">Orb</p>
120
+ <WakaLootBox items={legendaryLoot} variant="orb" />
121
+ </div>
122
+ </div>
123
+ ),
124
+ }
125
+
126
+ export const Sizes: Story = {
127
+ render: () => (
128
+ <div className="flex flex-wrap gap-8 items-end">
129
+ <div className="text-center">
130
+ <p className="text-sm text-muted-foreground mb-2">Small</p>
131
+ <WakaLootBox items={commonLoot} size="sm" />
132
+ </div>
133
+ <div className="text-center">
134
+ <p className="text-sm text-muted-foreground mb-2">Default</p>
135
+ <WakaLootBox items={rareLoot} size="default" />
136
+ </div>
137
+ <div className="text-center">
138
+ <p className="text-sm text-muted-foreground mb-2">Large</p>
139
+ <WakaLootBox items={epicLoot} size="lg" />
140
+ </div>
141
+ </div>
142
+ ),
143
+ }
144
+
145
+ export const RarityEffects: Story = {
146
+ render: () => (
147
+ <div className="flex flex-wrap gap-8 items-end">
148
+ <div className="text-center">
149
+ <p className="text-sm text-muted-foreground mb-2">Common Loot</p>
150
+ <WakaLootBox items={commonLoot} variant="crate" />
151
+ </div>
152
+ <div className="text-center">
153
+ <p className="text-sm text-muted-foreground mb-2">Rare Loot</p>
154
+ <WakaLootBox items={rareLoot} variant="gift" />
155
+ </div>
156
+ <div className="text-center">
157
+ <p className="text-sm text-muted-foreground mb-2">Epic Loot</p>
158
+ <WakaLootBox items={epicLoot} variant="chest" />
159
+ </div>
160
+ <div className="text-center">
161
+ <p className="text-sm text-muted-foreground mb-2">Legendary Loot</p>
162
+ <WakaLootBox items={legendaryLoot} variant="orb" />
163
+ </div>
164
+ </div>
165
+ ),
166
+ }
167
+
168
+ export const AutoReveal: Story = {
169
+ render: () => (
170
+ <div className="space-y-4">
171
+ <p className="text-sm text-muted-foreground text-center">
172
+ Items will automatically cycle through after opening
173
+ </p>
174
+ <WakaLootBox items={sampleItems} autoReveal autoRevealDelay={2000} />
175
+ </div>
176
+ ),
177
+ }
178
+
179
+ export const Interactive: Story = {
180
+ render: () => {
181
+ const [openedBoxes, setOpenedBoxes] = React.useState(0)
182
+ const [totalItems, setTotalItems] = React.useState<LootBoxItem[]>([])
183
+
184
+ const handleRevealComplete = (items: LootBoxItem[]) => {
185
+ setOpenedBoxes(prev => prev + 1)
186
+ setTotalItems(prev => [...prev, ...items])
187
+ }
188
+
189
+ return (
190
+ <div className="space-y-6">
191
+ <div className="flex gap-8">
192
+ <WakaLootBox
193
+ items={commonLoot}
194
+ variant="crate"
195
+ onRevealComplete={handleRevealComplete}
196
+ />
197
+ <WakaLootBox
198
+ items={rareLoot}
199
+ variant="gift"
200
+ onRevealComplete={handleRevealComplete}
201
+ />
202
+ <WakaLootBox
203
+ items={epicLoot}
204
+ variant="chest"
205
+ onRevealComplete={handleRevealComplete}
206
+ />
207
+ </div>
208
+
209
+ <div className="p-4 rounded-lg border bg-muted/30">
210
+ <div className="flex justify-between items-center">
211
+ <span className="text-sm text-muted-foreground">Boxes Opened: {openedBoxes}</span>
212
+ <span className="text-sm text-muted-foreground">Items Collected: {totalItems.length}</span>
213
+ </div>
214
+
215
+ {totalItems.length > 0 && (
216
+ <div className="mt-3 flex flex-wrap gap-2">
217
+ {totalItems.map((item, i) => (
218
+ <div
219
+ key={`${item.id}-${i}`}
220
+ className={`px-2 py-1 rounded text-xs font-medium ${
221
+ item.rarity === 'legendary' ? 'bg-amber-500/20 text-amber-500' :
222
+ item.rarity === 'epic' ? 'bg-purple-500/20 text-purple-500' :
223
+ item.rarity === 'rare' ? 'bg-blue-500/20 text-blue-500' :
224
+ 'bg-slate-500/20 text-slate-500'
225
+ }`}
226
+ >
227
+ {item.name}
228
+ </div>
229
+ ))}
230
+ </div>
231
+ )}
232
+ </div>
233
+ </div>
234
+ )
235
+ },
236
+ }
237
+
238
+ export const WithHook: Story = {
239
+ render: () => {
240
+ const [inventory, setInventory] = React.useState<LootBoxItem[]>([])
241
+
242
+ const lootBox = useLootBox({
243
+ items: sampleItems,
244
+ onRevealComplete: (items) => {
245
+ setInventory(prev => [...prev, ...items])
246
+ },
247
+ })
248
+
249
+ return (
250
+ <div className="space-y-6 w-[400px]">
251
+ <div className="flex justify-between items-center">
252
+ <h3 className="font-semibold">Loot Box Demo</h3>
253
+ <span className="text-sm text-muted-foreground">
254
+ Phase: {lootBox.phase}
255
+ </span>
256
+ </div>
257
+
258
+ <WakaLootBox
259
+ items={sampleItems}
260
+ isOpen={lootBox.isOpen}
261
+ onOpen={lootBox.open}
262
+ onClose={lootBox.close}
263
+ />
264
+
265
+ <div className="flex gap-2">
266
+ <button
267
+ onClick={() => lootBox.open()}
268
+ disabled={lootBox.isOpen}
269
+ className="px-3 py-1.5 text-sm rounded bg-primary text-primary-foreground disabled:opacity-50"
270
+ >
271
+ Open Box
272
+ </button>
273
+ <button
274
+ onClick={() => lootBox.reset()}
275
+ className="px-3 py-1.5 text-sm rounded border hover:bg-muted"
276
+ >
277
+ Reset
278
+ </button>
279
+ </div>
280
+
281
+ {inventory.length > 0 && (
282
+ <div className="p-4 rounded-lg border">
283
+ <h4 className="text-sm font-medium mb-2">Inventory ({inventory.length})</h4>
284
+ <div className="flex flex-wrap gap-2">
285
+ {inventory.map((item, i) => (
286
+ <div key={`${item.id}-${i}`} className="text-xs px-2 py-1 bg-muted rounded">
287
+ {item.name} {item.quantity && `x${item.quantity}`}
288
+ </div>
289
+ ))}
290
+ </div>
291
+ </div>
292
+ )}
293
+ </div>
294
+ )
295
+ },
296
+ }
297
+
298
+ export const MixedRarityBox: Story = {
299
+ render: () => {
300
+ const mixedItems: LootBoxItem[] = [
301
+ ...commonLoot,
302
+ ...rareLoot,
303
+ ...epicLoot,
304
+ ...legendaryLoot,
305
+ ]
306
+
307
+ return (
308
+ <div className="space-y-4">
309
+ <p className="text-sm text-muted-foreground text-center">
310
+ Contains items of all rarities - the box glows based on the highest rarity inside
311
+ </p>
312
+ <WakaLootBox items={mixedItems} variant="chest" size="lg" />
313
+ </div>
314
+ )
315
+ },
316
+ }
317
+
318
+ export const GachaSimulator: Story = {
319
+ render: () => {
320
+ const [pulls, setPulls] = React.useState(0)
321
+ const [legendary, setLegendary] = React.useState(0)
322
+ const [currentItems, setCurrentItems] = React.useState<LootBoxItem[]>(commonLoot)
323
+
324
+ const getRandomLoot = (): LootBoxItem[] => {
325
+ const roll = Math.random()
326
+ if (roll < 0.01) return legendaryLoot
327
+ if (roll < 0.10) return epicLoot
328
+ if (roll < 0.30) return rareLoot
329
+ return commonLoot
330
+ }
331
+
332
+ const handleOpen = () => {
333
+ setPulls(prev => prev + 1)
334
+ const loot = getRandomLoot()
335
+ setCurrentItems(loot)
336
+ if (loot[0]?.rarity === 'legendary') {
337
+ setLegendary(prev => prev + 1)
338
+ }
339
+ }
340
+
341
+ return (
342
+ <div className="space-y-6 w-[400px]">
343
+ <div className="p-4 rounded-lg border bg-gradient-to-r from-amber-500/10 to-orange-500/10">
344
+ <h3 className="font-bold text-center mb-2">Gacha Simulator</h3>
345
+ <div className="flex justify-between text-sm">
346
+ <span>Total Pulls: {pulls}</span>
347
+ <span className="text-amber-500">Legendaries: {legendary}</span>
348
+ </div>
349
+ <div className="text-xs text-muted-foreground text-center mt-2">
350
+ 1% Legendary | 10% Epic | 30% Rare | 59% Common
351
+ </div>
352
+ </div>
353
+
354
+ <WakaLootBox
355
+ key={pulls}
356
+ items={currentItems}
357
+ variant="orb"
358
+ onOpen={handleOpen}
359
+ />
360
+ </div>
361
+ )
362
+ },
363
+ }
364
+
365
+ export const Disabled: Story = {
366
+ render: () => (
367
+ <div className="space-y-4">
368
+ <p className="text-sm text-muted-foreground text-center">
369
+ Disabled state - cannot be opened
370
+ </p>
371
+ <WakaLootBox items={sampleItems} disabled />
372
+ </div>
373
+ ),
374
+ }