@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,308 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import {
3
+ Auth2FA,
4
+ defaultBackupCodes,
5
+ defaultStatus,
6
+ } from './index'
7
+ import type { TwoFactorStatus, TwoFactorMethod, BackupCode } from './index'
8
+ import * as React from 'react'
9
+
10
+ const meta: Meta<typeof Auth2FA> = {
11
+ title: 'Blocks/Auth2FA',
12
+ component: Auth2FA,
13
+ parameters: {
14
+ layout: 'centered',
15
+ docs: {
16
+ description: {
17
+ component:
18
+ 'A two-factor authentication setup wizard supporting authenticator apps, SMS, email, and hardware keys with backup codes management and security tips.',
19
+ },
20
+ },
21
+ },
22
+ tags: ['autodocs'],
23
+ }
24
+
25
+ export default meta
26
+ type Story = StoryObj<typeof Auth2FA>
27
+
28
+ export const Default: Story = {
29
+ render: () => (
30
+ <div className="p-6 max-w-2xl">
31
+ <Auth2FA
32
+ status={{ enabled: false }}
33
+ availableMethods={['authenticator', 'sms', 'email']}
34
+ qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
35
+ secretKey="JBSWY3DPEHPK3PXP"
36
+ onEnable={async (method, code) => {
37
+ console.log('Enable 2FA:', method, code)
38
+ await new Promise((resolve) => setTimeout(resolve, 1000))
39
+ return code === '123456'
40
+ }}
41
+ onSendCode={async (method) => {
42
+ console.log('Send code via:', method)
43
+ await new Promise((resolve) => setTimeout(resolve, 1000))
44
+ return true
45
+ }}
46
+ />
47
+ </div>
48
+ ),
49
+ }
50
+
51
+ export const Enabled: Story = {
52
+ render: () => (
53
+ <div className="p-6 max-w-2xl">
54
+ <Auth2FA
55
+ status={defaultStatus}
56
+ backupCodes={defaultBackupCodes}
57
+ onDisable={async (code) => {
58
+ console.log('Disable 2FA with code:', code)
59
+ await new Promise((resolve) => setTimeout(resolve, 1000))
60
+ return code === '123456'
61
+ }}
62
+ onRegenerateBackupCodes={async () => {
63
+ console.log('Regenerate backup codes')
64
+ await new Promise((resolve) => setTimeout(resolve, 1000))
65
+ return [
66
+ { code: 'NEW1-1234', used: false },
67
+ { code: 'NEW2-5678', used: false },
68
+ { code: 'NEW3-9012', used: false },
69
+ { code: 'NEW4-3456', used: false },
70
+ { code: 'NEW5-7890', used: false },
71
+ { code: 'NEW6-1234', used: false },
72
+ { code: 'NEW7-5678', used: false },
73
+ { code: 'NEW8-9012', used: false },
74
+ ]
75
+ }}
76
+ />
77
+ </div>
78
+ ),
79
+ }
80
+
81
+ export const EnabledWithSMS: Story = {
82
+ render: () => {
83
+ const smsStatus: TwoFactorStatus = {
84
+ enabled: true,
85
+ method: 'sms',
86
+ phone: '+1 (555) ***-**89',
87
+ lastVerified: new Date(),
88
+ }
89
+
90
+ return (
91
+ <div className="p-6 max-w-2xl">
92
+ <Auth2FA
93
+ status={smsStatus}
94
+ backupCodes={defaultBackupCodes}
95
+ onDisable={async (code) => {
96
+ await new Promise((resolve) => setTimeout(resolve, 1000))
97
+ return code === '123456'
98
+ }}
99
+ />
100
+ </div>
101
+ )
102
+ },
103
+ }
104
+
105
+ export const EnabledWithEmail: Story = {
106
+ render: () => {
107
+ const emailStatus: TwoFactorStatus = {
108
+ enabled: true,
109
+ method: 'email',
110
+ email: 'u***@example.com',
111
+ lastVerified: new Date(),
112
+ }
113
+
114
+ return (
115
+ <div className="p-6 max-w-2xl">
116
+ <Auth2FA
117
+ status={emailStatus}
118
+ backupCodes={defaultBackupCodes}
119
+ onDisable={async (code) => {
120
+ await new Promise((resolve) => setTimeout(resolve, 1000))
121
+ return code === '123456'
122
+ }}
123
+ />
124
+ </div>
125
+ )
126
+ },
127
+ }
128
+
129
+ export const AllMethods: Story = {
130
+ render: () => (
131
+ <div className="p-6 max-w-2xl">
132
+ <Auth2FA
133
+ status={{ enabled: false }}
134
+ availableMethods={['authenticator', 'sms', 'email', 'hardware']}
135
+ qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
136
+ secretKey="JBSWY3DPEHPK3PXP"
137
+ onEnable={async (method, code) => {
138
+ console.log('Enable 2FA:', method, code)
139
+ await new Promise((resolve) => setTimeout(resolve, 1000))
140
+ return true
141
+ }}
142
+ onSendCode={async (method) => {
143
+ console.log('Send code via:', method)
144
+ await new Promise((resolve) => setTimeout(resolve, 1000))
145
+ return true
146
+ }}
147
+ />
148
+ </div>
149
+ ),
150
+ }
151
+
152
+ export const AuthenticatorOnly: Story = {
153
+ render: () => (
154
+ <div className="p-6 max-w-2xl">
155
+ <Auth2FA
156
+ status={{ enabled: false }}
157
+ availableMethods={['authenticator']}
158
+ qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/SecureApp:admin@secureapp.com?secret=ABCDEFGHIJKLMNOP&issuer=SecureApp"
159
+ secretKey="ABCDEFGHIJKLMNOP"
160
+ onEnable={async (method, code) => {
161
+ console.log('Enable authenticator:', code)
162
+ await new Promise((resolve) => setTimeout(resolve, 1000))
163
+ return code.length === 6
164
+ }}
165
+ />
166
+ </div>
167
+ ),
168
+ }
169
+
170
+ export const LowBackupCodes: Story = {
171
+ render: () => {
172
+ const lowCodes: BackupCode[] = [
173
+ { code: 'LAST-1234', used: false },
174
+ { code: 'LAST-5678', used: false },
175
+ { code: 'USED-0001', used: true },
176
+ { code: 'USED-0002', used: true },
177
+ { code: 'USED-0003', used: true },
178
+ { code: 'USED-0004', used: true },
179
+ { code: 'USED-0005', used: true },
180
+ { code: 'USED-0006', used: true },
181
+ ]
182
+
183
+ return (
184
+ <div className="p-6 max-w-2xl">
185
+ <Auth2FA
186
+ status={defaultStatus}
187
+ backupCodes={lowCodes}
188
+ onRegenerateBackupCodes={async () => {
189
+ console.log('Regenerate backup codes')
190
+ await new Promise((resolve) => setTimeout(resolve, 1000))
191
+ return [
192
+ { code: 'FRESH-1111', used: false },
193
+ { code: 'FRESH-2222', used: false },
194
+ { code: 'FRESH-3333', used: false },
195
+ { code: 'FRESH-4444', used: false },
196
+ { code: 'FRESH-5555', used: false },
197
+ { code: 'FRESH-6666', used: false },
198
+ { code: 'FRESH-7777', used: false },
199
+ { code: 'FRESH-8888', used: false },
200
+ ]
201
+ }}
202
+ />
203
+ </div>
204
+ )
205
+ },
206
+ }
207
+
208
+ export const Interactive: Story = {
209
+ render: () => {
210
+ const [status, setStatus] = React.useState<TwoFactorStatus>({ enabled: false })
211
+ const [backupCodes, setBackupCodes] = React.useState<BackupCode[]>([])
212
+
213
+ const handleEnable = async (method: TwoFactorMethod, code: string): Promise<boolean> => {
214
+ await new Promise((resolve) => setTimeout(resolve, 1500))
215
+ if (code === '123456') {
216
+ setStatus({ enabled: true, method, lastVerified: new Date() })
217
+ setBackupCodes([
218
+ { code: 'DEMO-1111', used: false },
219
+ { code: 'DEMO-2222', used: false },
220
+ { code: 'DEMO-3333', used: false },
221
+ { code: 'DEMO-4444', used: false },
222
+ { code: 'DEMO-5555', used: false },
223
+ { code: 'DEMO-6666', used: false },
224
+ { code: 'DEMO-7777', used: false },
225
+ { code: 'DEMO-8888', used: false },
226
+ ])
227
+ return true
228
+ }
229
+ return false
230
+ }
231
+
232
+ const handleDisable = async (code: string): Promise<boolean> => {
233
+ await new Promise((resolve) => setTimeout(resolve, 1500))
234
+ if (code === '123456') {
235
+ setStatus({ enabled: false })
236
+ setBackupCodes([])
237
+ return true
238
+ }
239
+ return false
240
+ }
241
+
242
+ return (
243
+ <div className="p-6 max-w-2xl">
244
+ <div className="mb-4 p-4 bg-muted rounded-lg text-sm">
245
+ <p className="font-medium">Test Instructions:</p>
246
+ <p className="text-muted-foreground mt-1">
247
+ Use code <code className="bg-background px-1 rounded">123456</code> to enable/disable 2FA
248
+ </p>
249
+ </div>
250
+ <Auth2FA
251
+ status={status}
252
+ availableMethods={['authenticator', 'sms', 'email']}
253
+ qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Demo:user@demo.com?secret=DEMOSECRETKEY123&issuer=Demo"
254
+ secretKey="DEMOSECRETKEY123"
255
+ backupCodes={backupCodes}
256
+ onEnable={handleEnable}
257
+ onDisable={handleDisable}
258
+ onSendCode={async (method) => {
259
+ alert(`Code sent via ${method}! (Demo: use 123456)`)
260
+ return true
261
+ }}
262
+ onRegenerateBackupCodes={async () => {
263
+ const newCodes = Array.from({ length: 8 }, (_, i) => ({
264
+ code: `REGEN-${1000 + i}`,
265
+ used: false,
266
+ }))
267
+ setBackupCodes(newCodes)
268
+ return newCodes
269
+ }}
270
+ />
271
+ </div>
272
+ )
273
+ },
274
+ }
275
+
276
+ export const SecuritySettings: Story = {
277
+ render: () => (
278
+ <div className="min-h-screen bg-muted/30">
279
+ <header className="bg-background border-b px-6 py-4">
280
+ <div className="flex items-center justify-between max-w-4xl mx-auto">
281
+ <div>
282
+ <h1 className="font-bold text-xl">Security Settings</h1>
283
+ <p className="text-sm text-muted-foreground">Manage your account security</p>
284
+ </div>
285
+ <nav className="flex gap-4 text-sm">
286
+ <a href="#" className="text-muted-foreground hover:text-foreground">Profile</a>
287
+ <a href="#" className="text-foreground font-medium">Security</a>
288
+ <a href="#" className="text-muted-foreground hover:text-foreground">Notifications</a>
289
+ </nav>
290
+ </div>
291
+ </header>
292
+ <main className="p-6 max-w-4xl mx-auto">
293
+ <Auth2FA
294
+ status={defaultStatus}
295
+ backupCodes={defaultBackupCodes}
296
+ onDisable={async (code) => {
297
+ await new Promise((resolve) => setTimeout(resolve, 1000))
298
+ return code === '123456'
299
+ }}
300
+ onRegenerateBackupCodes={async () => {
301
+ await new Promise((resolve) => setTimeout(resolve, 1000))
302
+ return defaultBackupCodes.map((c, i) => ({ ...c, code: `NEW-${i + 1000}`, used: false }))
303
+ }}
304
+ />
305
+ </main>
306
+ </div>
307
+ ),
308
+ }
@@ -0,0 +1,398 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaCalendarView, defaultCalendarEvents } from './index'
3
+ import type { CalendarEvent, CalendarViewMode } from './index'
4
+ import * as React from 'react'
5
+
6
+ const meta: Meta<typeof WakaCalendarView> = {
7
+ title: 'Blocks/CalendarView',
8
+ component: WakaCalendarView,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'A calendar component with month, week, day, and agenda views. Supports events with colors, locations, attendees, and recurrence.',
15
+ },
16
+ },
17
+ },
18
+ tags: ['autodocs'],
19
+ argTypes: {
20
+ viewMode: {
21
+ control: 'select',
22
+ options: ['month', 'week', 'day', 'agenda'],
23
+ },
24
+ showHeader: {
25
+ control: 'boolean',
26
+ },
27
+ showViewSelector: {
28
+ control: 'boolean',
29
+ },
30
+ showAddButton: {
31
+ control: 'boolean',
32
+ },
33
+ weekStartsOn: {
34
+ control: 'select',
35
+ options: [0, 1],
36
+ },
37
+ },
38
+ }
39
+
40
+ export default meta
41
+ type Story = StoryObj<typeof WakaCalendarView>
42
+
43
+ export const Default: Story = {
44
+ render: () => (
45
+ <div className="p-6">
46
+ <WakaCalendarView
47
+ events={defaultCalendarEvents}
48
+ onEventClick={(event) => console.log('Event clicked:', event)}
49
+ onDayClick={(date) => console.log('Day clicked:', date)}
50
+ />
51
+ </div>
52
+ ),
53
+ }
54
+
55
+ export const MonthView: Story = {
56
+ render: () => (
57
+ <div className="p-6">
58
+ <WakaCalendarView
59
+ events={defaultCalendarEvents}
60
+ viewMode="month"
61
+ onViewModeChange={(mode) => console.log('View mode:', mode)}
62
+ onEventClick={(event) => console.log('Event:', event)}
63
+ />
64
+ </div>
65
+ ),
66
+ }
67
+
68
+ export const AgendaView: Story = {
69
+ render: () => (
70
+ <div className="p-6">
71
+ <WakaCalendarView
72
+ events={defaultCalendarEvents}
73
+ viewMode="agenda"
74
+ onEventClick={(event) => console.log('Event:', event)}
75
+ />
76
+ </div>
77
+ ),
78
+ }
79
+
80
+ export const ManyEvents: Story = {
81
+ render: () => {
82
+ const now = new Date()
83
+ const manyEvents: CalendarEvent[] = [
84
+ // Today's events
85
+ {
86
+ id: '1',
87
+ title: 'Standup',
88
+ start: new Date(now.setHours(9, 0, 0, 0)),
89
+ end: new Date(now.setHours(9, 30, 0, 0)),
90
+ color: 'blue',
91
+ },
92
+ {
93
+ id: '2',
94
+ title: 'Design Review',
95
+ start: new Date(now.setHours(10, 0, 0, 0)),
96
+ end: new Date(now.setHours(11, 0, 0, 0)),
97
+ color: 'purple',
98
+ },
99
+ {
100
+ id: '3',
101
+ title: 'Lunch',
102
+ start: new Date(now.setHours(12, 0, 0, 0)),
103
+ end: new Date(now.setHours(13, 0, 0, 0)),
104
+ color: 'green',
105
+ },
106
+ {
107
+ id: '4',
108
+ title: 'Sprint Planning',
109
+ start: new Date(now.setHours(14, 0, 0, 0)),
110
+ end: new Date(now.setHours(15, 30, 0, 0)),
111
+ color: 'orange',
112
+ },
113
+ {
114
+ id: '5',
115
+ title: '1:1 with Manager',
116
+ start: new Date(now.setHours(16, 0, 0, 0)),
117
+ end: new Date(now.setHours(16, 30, 0, 0)),
118
+ color: 'cyan',
119
+ },
120
+ // Tomorrow
121
+ {
122
+ id: '6',
123
+ title: 'Team Workshop',
124
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24),
125
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 + 1000 * 60 * 60 * 4),
126
+ color: 'red',
127
+ allDay: true,
128
+ },
129
+ // More events
130
+ ...Array.from({ length: 10 }, (_, i) => ({
131
+ id: `event-${i + 10}`,
132
+ title: `Meeting ${i + 1}`,
133
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24 * (i + 2)),
134
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 * (i + 2) + 1000 * 60 * 60),
135
+ color: (['blue', 'green', 'purple', 'orange', 'cyan', 'pink', 'yellow', 'red'] as const)[i % 8],
136
+ })),
137
+ ]
138
+
139
+ return (
140
+ <div className="p-6">
141
+ <WakaCalendarView
142
+ events={manyEvents}
143
+ onEventClick={(event) => console.log('Event:', event)}
144
+ />
145
+ </div>
146
+ )
147
+ },
148
+ }
149
+
150
+ export const WithLocations: Story = {
151
+ render: () => {
152
+ const eventsWithLocations: CalendarEvent[] = [
153
+ {
154
+ id: '1',
155
+ title: 'Team Meeting',
156
+ start: new Date(),
157
+ end: new Date(Date.now() + 1000 * 60 * 60),
158
+ color: 'blue',
159
+ location: 'Conference Room A',
160
+ },
161
+ {
162
+ id: '2',
163
+ title: 'Client Call',
164
+ start: new Date(Date.now() + 1000 * 60 * 60 * 2),
165
+ end: new Date(Date.now() + 1000 * 60 * 60 * 3),
166
+ color: 'green',
167
+ location: 'Zoom Meeting',
168
+ },
169
+ {
170
+ id: '3',
171
+ title: 'Lunch Meeting',
172
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24),
173
+ end: new Date(Date.now() + 1000 * 60 * 60 * 25),
174
+ color: 'orange',
175
+ location: 'Restaurant Le Gourmet, 123 Main St',
176
+ },
177
+ ]
178
+
179
+ return (
180
+ <div className="p-6">
181
+ <WakaCalendarView
182
+ events={eventsWithLocations}
183
+ viewMode="agenda"
184
+ onEventClick={(event) => console.log('Event:', event)}
185
+ />
186
+ </div>
187
+ )
188
+ },
189
+ }
190
+
191
+ export const AllDayEvents: Story = {
192
+ render: () => {
193
+ const allDayEvents: CalendarEvent[] = [
194
+ {
195
+ id: '1',
196
+ title: 'Company Holiday',
197
+ start: new Date(),
198
+ end: new Date(),
199
+ allDay: true,
200
+ color: 'red',
201
+ },
202
+ {
203
+ id: '2',
204
+ title: 'Conference Week',
205
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3),
206
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
207
+ allDay: true,
208
+ color: 'purple',
209
+ },
210
+ {
211
+ id: '3',
212
+ title: 'Team Offsite',
213
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 10),
214
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 12),
215
+ allDay: true,
216
+ color: 'green',
217
+ },
218
+ ]
219
+
220
+ return (
221
+ <div className="p-6">
222
+ <WakaCalendarView
223
+ events={allDayEvents}
224
+ onEventClick={(event) => console.log('Event:', event)}
225
+ />
226
+ </div>
227
+ )
228
+ },
229
+ }
230
+
231
+ export const WeekStartsSunday: Story = {
232
+ render: () => (
233
+ <div className="p-6">
234
+ <WakaCalendarView
235
+ events={defaultCalendarEvents}
236
+ weekStartsOn={0}
237
+ locale="en-US"
238
+ onEventClick={(event) => console.log('Event:', event)}
239
+ />
240
+ </div>
241
+ ),
242
+ }
243
+
244
+ export const NoHeader: Story = {
245
+ render: () => (
246
+ <div className="p-6">
247
+ <WakaCalendarView
248
+ events={defaultCalendarEvents}
249
+ showHeader={false}
250
+ />
251
+ </div>
252
+ ),
253
+ }
254
+
255
+ export const ReadOnly: Story = {
256
+ render: () => (
257
+ <div className="p-6">
258
+ <WakaCalendarView
259
+ events={defaultCalendarEvents}
260
+ showAddButton={false}
261
+ onEventClick={(event) => console.log('View event:', event)}
262
+ />
263
+ </div>
264
+ ),
265
+ }
266
+
267
+ export const Interactive: Story = {
268
+ render: () => {
269
+ const [events, setEvents] = React.useState<CalendarEvent[]>(defaultCalendarEvents)
270
+ const [viewMode, setViewMode] = React.useState<CalendarViewMode>('month')
271
+ const [currentDate, setCurrentDate] = React.useState(new Date())
272
+
273
+ return (
274
+ <div className="p-6">
275
+ <div className="mb-4 text-sm text-muted-foreground">
276
+ Current view: {viewMode} | Date: {currentDate.toLocaleDateString()}
277
+ </div>
278
+ <WakaCalendarView
279
+ events={events}
280
+ currentDate={currentDate}
281
+ onDateChange={setCurrentDate}
282
+ viewMode={viewMode}
283
+ onViewModeChange={setViewMode}
284
+ onEventClick={(event) => {
285
+ console.log('Event clicked:', event)
286
+ alert(`Event: ${event.title}`)
287
+ }}
288
+ onDayClick={(date) => {
289
+ console.log('Day clicked:', date)
290
+ }}
291
+ onEventCreate={(event) => {
292
+ console.log('Create event:', event)
293
+ }}
294
+ />
295
+ </div>
296
+ )
297
+ },
298
+ }
299
+
300
+ export const ColorfulEvents: Story = {
301
+ render: () => {
302
+ const colorfulEvents: CalendarEvent[] = [
303
+ { id: '1', title: 'Blue Event', start: new Date(), end: new Date(Date.now() + 1000 * 60 * 60), color: 'blue' },
304
+ { id: '2', title: 'Green Event', start: new Date(Date.now() + 1000 * 60 * 60 * 3), end: new Date(Date.now() + 1000 * 60 * 60 * 4), color: 'green' },
305
+ { id: '3', title: 'Red Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24), end: new Date(Date.now() + 1000 * 60 * 60 * 25), color: 'red' },
306
+ { id: '4', title: 'Yellow Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 60 * 60), color: 'yellow' },
307
+ { id: '5', title: 'Purple Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 2), color: 'purple' },
308
+ { id: '6', title: 'Pink Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60), color: 'pink' },
309
+ { id: '7', title: 'Orange Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 5), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 5 + 1000 * 60 * 60 * 3), color: 'orange' },
310
+ { id: '8', title: 'Cyan Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 6), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 6 + 1000 * 60 * 60), color: 'cyan' },
311
+ ]
312
+
313
+ return (
314
+ <div className="p-6">
315
+ <WakaCalendarView
316
+ events={colorfulEvents}
317
+ onEventClick={(event) => console.log('Event:', event)}
318
+ />
319
+ </div>
320
+ )
321
+ },
322
+ }
323
+
324
+ export const EmptyCalendar: Story = {
325
+ render: () => (
326
+ <div className="p-6">
327
+ <WakaCalendarView
328
+ events={[]}
329
+ viewMode="agenda"
330
+ />
331
+ </div>
332
+ ),
333
+ }
334
+
335
+ export const TeamCalendar: Story = {
336
+ render: () => {
337
+ const teamEvents: CalendarEvent[] = [
338
+ {
339
+ id: '1',
340
+ title: 'Daily Standup',
341
+ description: 'Team sync meeting',
342
+ start: new Date(new Date().setHours(9, 0, 0, 0)),
343
+ end: new Date(new Date().setHours(9, 15, 0, 0)),
344
+ color: 'blue',
345
+ location: 'Zoom',
346
+ attendees: [
347
+ { id: '1', name: 'Alice' },
348
+ { id: '2', name: 'Bob' },
349
+ { id: '3', name: 'Carol' },
350
+ ],
351
+ },
352
+ {
353
+ id: '2',
354
+ title: 'Sprint Review',
355
+ description: 'Demo completed work',
356
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4),
357
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60),
358
+ color: 'green',
359
+ location: 'Conference Room B',
360
+ attendees: [
361
+ { id: '1', name: 'Alice' },
362
+ { id: '2', name: 'Bob' },
363
+ { id: '3', name: 'Carol' },
364
+ { id: '4', name: 'David' },
365
+ { id: '5', name: 'Eve' },
366
+ ],
367
+ },
368
+ {
369
+ id: '3',
370
+ title: 'Retrospective',
371
+ start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60 * 2),
372
+ end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60 * 3),
373
+ color: 'purple',
374
+ location: 'Conference Room B',
375
+ },
376
+ ]
377
+
378
+ return (
379
+ <div className="min-h-screen bg-muted/30">
380
+ <header className="bg-background border-b px-6 py-4">
381
+ <div className="flex items-center justify-between">
382
+ <div>
383
+ <h1 className="font-bold text-xl">Team Calendar</h1>
384
+ <p className="text-sm text-muted-foreground">Engineering Team</p>
385
+ </div>
386
+ </div>
387
+ </header>
388
+ <main className="p-6">
389
+ <WakaCalendarView
390
+ events={teamEvents}
391
+ onEventClick={(event) => console.log('Event:', event)}
392
+ onEventCreate={(event) => console.log('Create:', event)}
393
+ />
394
+ </main>
395
+ </div>
396
+ )
397
+ },
398
+ }