@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,530 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaVersusCard, useVersusCard } from './index'
3
+ import type { Competitor, Challenge, ChallengeStatus } from './index'
4
+ import * as React from 'react'
5
+ import { Target, Zap, Trophy } from 'lucide-react'
6
+
7
+ const defaultCompetitorLeft: Competitor = {
8
+ id: 'player1',
9
+ name: 'DragonSlayer',
10
+ avatar: 'https://i.pravatar.cc/100?img=1',
11
+ score: 1250,
12
+ rank: 'Diamond III',
13
+ winStreak: 5,
14
+ isCurrentUser: true,
15
+ }
16
+
17
+ const defaultCompetitorRight: Competitor = {
18
+ id: 'player2',
19
+ name: 'ShadowMage',
20
+ avatar: 'https://i.pravatar.cc/100?img=2',
21
+ score: 1180,
22
+ rank: 'Diamond II',
23
+ winStreak: 3,
24
+ }
25
+
26
+ const defaultChallenge: Challenge = {
27
+ type: 'score',
28
+ name: 'Weekly Showdown',
29
+ description: 'First to 2000 points wins!',
30
+ targetScore: 2000,
31
+ startTime: new Date(),
32
+ endTime: new Date(Date.now() + 2 * 60 * 60 * 1000),
33
+ rewards: [
34
+ { type: 'xp', value: 500 },
35
+ { type: 'badge', value: 'Champion' },
36
+ ],
37
+ }
38
+
39
+ const meta: Meta<typeof WakaVersusCard> = {
40
+ title: 'Components/Gamification/WakaVersusCard',
41
+ component: WakaVersusCard,
42
+ parameters: {
43
+ layout: 'centered',
44
+ docs: {
45
+ description: {
46
+ component: 'A versus matchup card with real-time score tracking, animated updates, challenge types, time remaining, and winner celebration effects.',
47
+ },
48
+ },
49
+ },
50
+ tags: ['autodocs'],
51
+ argTypes: {
52
+ size: {
53
+ control: 'select',
54
+ options: ['sm', 'default', 'lg'],
55
+ description: 'Card size variant',
56
+ },
57
+ status: {
58
+ control: 'select',
59
+ options: ['upcoming', 'live', 'finished'],
60
+ description: 'Challenge status',
61
+ },
62
+ showLivePulse: {
63
+ control: 'boolean',
64
+ description: 'Show live pulse animation',
65
+ },
66
+ },
67
+ }
68
+
69
+ export default meta
70
+ type Story = StoryObj<typeof WakaVersusCard>
71
+
72
+ export const Default: Story = {
73
+ args: {
74
+ competitorLeft: defaultCompetitorLeft,
75
+ competitorRight: defaultCompetitorRight,
76
+ challenge: defaultChallenge,
77
+ status: 'live',
78
+ },
79
+ render: (args) => (
80
+ <div className="w-[500px]">
81
+ <WakaVersusCard {...args} />
82
+ </div>
83
+ ),
84
+ }
85
+
86
+ export const ChallengeStatuses: Story = {
87
+ render: () => {
88
+ const statuses: ChallengeStatus[] = ['upcoming', 'live', 'finished']
89
+
90
+ return (
91
+ <div className="space-y-6 w-[500px]">
92
+ {statuses.map((status) => (
93
+ <div key={status}>
94
+ <p className="text-sm text-muted-foreground mb-2 capitalize">{status}</p>
95
+ <WakaVersusCard
96
+ competitorLeft={defaultCompetitorLeft}
97
+ competitorRight={defaultCompetitorRight}
98
+ challenge={{
99
+ ...defaultChallenge,
100
+ startTime: status === 'upcoming' ? new Date(Date.now() + 60 * 60 * 1000) : new Date(),
101
+ }}
102
+ status={status}
103
+ winnerId={status === 'finished' ? 'player1' : undefined}
104
+ />
105
+ </div>
106
+ ))}
107
+ </div>
108
+ )
109
+ },
110
+ }
111
+
112
+ export const ChallengeTypes: Story = {
113
+ render: () => {
114
+ const types: Array<{ type: Challenge['type']; name: string; description: string }> = [
115
+ { type: 'score', name: 'Score Battle', description: 'Highest score wins' },
116
+ { type: 'time', name: 'Time Trial', description: 'Fastest completion' },
117
+ { type: 'streak', name: 'Streak Challenge', description: 'Longest streak wins' },
118
+ { type: 'collection', name: 'Collection Race', description: 'Most items collected' },
119
+ { type: 'custom', name: 'Custom Battle', description: 'Special rules apply' },
120
+ ]
121
+
122
+ return (
123
+ <div className="space-y-4 w-[500px]">
124
+ {types.map(({ type, name, description }) => (
125
+ <WakaVersusCard
126
+ key={type}
127
+ competitorLeft={{ ...defaultCompetitorLeft, score: Math.floor(Math.random() * 500) + 100 }}
128
+ competitorRight={{ ...defaultCompetitorRight, score: Math.floor(Math.random() * 500) + 100 }}
129
+ challenge={{ ...defaultChallenge, type, name, description }}
130
+ status="live"
131
+ />
132
+ ))}
133
+ </div>
134
+ )
135
+ },
136
+ }
137
+
138
+ export const Sizes: Story = {
139
+ render: () => (
140
+ <div className="space-y-8">
141
+ <div>
142
+ <p className="text-sm text-muted-foreground mb-2">Small</p>
143
+ <div className="w-[400px]">
144
+ <WakaVersusCard
145
+ competitorLeft={defaultCompetitorLeft}
146
+ competitorRight={defaultCompetitorRight}
147
+ challenge={defaultChallenge}
148
+ status="live"
149
+ size="sm"
150
+ />
151
+ </div>
152
+ </div>
153
+ <div>
154
+ <p className="text-sm text-muted-foreground mb-2">Default</p>
155
+ <div className="w-[500px]">
156
+ <WakaVersusCard
157
+ competitorLeft={defaultCompetitorLeft}
158
+ competitorRight={defaultCompetitorRight}
159
+ challenge={defaultChallenge}
160
+ status="live"
161
+ size="default"
162
+ />
163
+ </div>
164
+ </div>
165
+ <div>
166
+ <p className="text-sm text-muted-foreground mb-2">Large</p>
167
+ <div className="w-[600px]">
168
+ <WakaVersusCard
169
+ competitorLeft={defaultCompetitorLeft}
170
+ competitorRight={defaultCompetitorRight}
171
+ challenge={defaultChallenge}
172
+ status="live"
173
+ size="lg"
174
+ />
175
+ </div>
176
+ </div>
177
+ </div>
178
+ ),
179
+ }
180
+
181
+ export const WithStats: Story = {
182
+ render: () => {
183
+ const leftWithStats: Competitor = {
184
+ ...defaultCompetitorLeft,
185
+ stats: [
186
+ { name: 'Wins', value: 42, icon: <Trophy className="h-3 w-3 text-yellow-500" /> },
187
+ { name: 'Streak', value: 5, icon: <Zap className="h-3 w-3 text-orange-500" /> },
188
+ ],
189
+ }
190
+
191
+ const rightWithStats: Competitor = {
192
+ ...defaultCompetitorRight,
193
+ stats: [
194
+ { name: 'Wins', value: 38, icon: <Trophy className="h-3 w-3 text-yellow-500" /> },
195
+ { name: 'Streak', value: 3, icon: <Zap className="h-3 w-3 text-orange-500" /> },
196
+ ],
197
+ }
198
+
199
+ return (
200
+ <div className="w-[550px]">
201
+ <WakaVersusCard
202
+ competitorLeft={leftWithStats}
203
+ competitorRight={rightWithStats}
204
+ challenge={defaultChallenge}
205
+ status="live"
206
+ />
207
+ </div>
208
+ )
209
+ },
210
+ }
211
+
212
+ export const WithTargetScore: Story = {
213
+ render: () => {
214
+ const challengeWithTarget: Challenge = {
215
+ ...defaultChallenge,
216
+ name: 'Race to 1000',
217
+ description: 'First to reach 1000 points wins!',
218
+ targetScore: 1000,
219
+ }
220
+
221
+ return (
222
+ <div className="w-[500px]">
223
+ <WakaVersusCard
224
+ competitorLeft={{ ...defaultCompetitorLeft, score: 720 }}
225
+ competitorRight={{ ...defaultCompetitorRight, score: 680 }}
226
+ challenge={challengeWithTarget}
227
+ status="live"
228
+ />
229
+ </div>
230
+ )
231
+ },
232
+ }
233
+
234
+ export const Finished: Story = {
235
+ render: () => (
236
+ <div className="w-[500px]">
237
+ <WakaVersusCard
238
+ competitorLeft={{ ...defaultCompetitorLeft, score: 2150 }}
239
+ competitorRight={{ ...defaultCompetitorRight, score: 1980 }}
240
+ challenge={defaultChallenge}
241
+ status="finished"
242
+ winnerId="player1"
243
+ />
244
+ </div>
245
+ ),
246
+ }
247
+
248
+ export const CloseMatch: Story = {
249
+ render: () => (
250
+ <div className="w-[500px]">
251
+ <p className="text-sm text-muted-foreground mb-4 text-center">
252
+ Score difference: 5 points - intense battle!
253
+ </p>
254
+ <WakaVersusCard
255
+ competitorLeft={{ ...defaultCompetitorLeft, score: 1495 }}
256
+ competitorRight={{ ...defaultCompetitorRight, score: 1490 }}
257
+ challenge={defaultChallenge}
258
+ status="live"
259
+ />
260
+ </div>
261
+ ),
262
+ }
263
+
264
+ export const Interactive: Story = {
265
+ render: () => {
266
+ const {
267
+ competitorLeft,
268
+ competitorRight,
269
+ status,
270
+ winnerId,
271
+ incrementScore,
272
+ start,
273
+ finish,
274
+ reset,
275
+ isLive,
276
+ scoreDifference,
277
+ } = useVersusCard({
278
+ competitorLeft: { ...defaultCompetitorLeft, score: 0 },
279
+ competitorRight: { ...defaultCompetitorRight, score: 0 },
280
+ challenge: { ...defaultChallenge, targetScore: 100 },
281
+ initialStatus: 'upcoming',
282
+ onWinner: (id) => console.log('Winner:', id),
283
+ })
284
+
285
+ return (
286
+ <div className="space-y-6 w-[500px]">
287
+ <WakaVersusCard
288
+ competitorLeft={competitorLeft}
289
+ competitorRight={competitorRight}
290
+ challenge={{ ...defaultChallenge, targetScore: 100 }}
291
+ status={status}
292
+ winnerId={winnerId}
293
+ />
294
+
295
+ <div className="p-4 rounded-lg border">
296
+ <h4 className="font-medium mb-3">Controls</h4>
297
+ <div className="grid grid-cols-2 gap-2 text-sm mb-4">
298
+ <div>Status: <span className="font-medium capitalize">{status}</span></div>
299
+ <div>Diff: <span className="font-medium">{scoreDifference}</span></div>
300
+ </div>
301
+
302
+ <div className="flex flex-wrap gap-2">
303
+ {status === 'upcoming' && (
304
+ <button
305
+ onClick={start}
306
+ className="px-3 py-1.5 text-xs rounded bg-green-500 text-white"
307
+ >
308
+ Start Challenge
309
+ </button>
310
+ )}
311
+ {isLive && (
312
+ <>
313
+ <button
314
+ onClick={() => incrementScore(competitorLeft.id, 10)}
315
+ className="px-3 py-1.5 text-xs rounded bg-blue-500 text-white"
316
+ >
317
+ +10 Left
318
+ </button>
319
+ <button
320
+ onClick={() => incrementScore(competitorRight.id, 10)}
321
+ className="px-3 py-1.5 text-xs rounded bg-purple-500 text-white"
322
+ >
323
+ +10 Right
324
+ </button>
325
+ <button
326
+ onClick={finish}
327
+ className="px-3 py-1.5 text-xs rounded bg-red-500 text-white"
328
+ >
329
+ End Match
330
+ </button>
331
+ </>
332
+ )}
333
+ <button
334
+ onClick={reset}
335
+ className="px-3 py-1.5 text-xs rounded border hover:bg-muted"
336
+ >
337
+ Reset
338
+ </button>
339
+ </div>
340
+ </div>
341
+ </div>
342
+ )
343
+ },
344
+ }
345
+
346
+ export const WithHook: Story = {
347
+ render: () => {
348
+ const {
349
+ competitorLeft,
350
+ competitorRight,
351
+ status,
352
+ winnerId,
353
+ incrementScore,
354
+ updateStatus,
355
+ leftIsWinning,
356
+ rightIsWinning,
357
+ isTied,
358
+ } = useVersusCard({
359
+ competitorLeft: { ...defaultCompetitorLeft, score: 500 },
360
+ competitorRight: { ...defaultCompetitorRight, score: 500 },
361
+ challenge: defaultChallenge,
362
+ initialStatus: 'live',
363
+ })
364
+
365
+ // Simulate score updates
366
+ React.useEffect(() => {
367
+ if (status !== 'live') return
368
+
369
+ const interval = setInterval(() => {
370
+ const random = Math.random()
371
+ if (random < 0.5) {
372
+ incrementScore(competitorLeft.id, Math.floor(Math.random() * 20) + 5)
373
+ } else {
374
+ incrementScore(competitorRight.id, Math.floor(Math.random() * 20) + 5)
375
+ }
376
+ }, 2000)
377
+
378
+ return () => clearInterval(interval)
379
+ }, [status, competitorLeft.id, competitorRight.id, incrementScore])
380
+
381
+ return (
382
+ <div className="space-y-6 w-[500px]">
383
+ <WakaVersusCard
384
+ competitorLeft={competitorLeft}
385
+ competitorRight={competitorRight}
386
+ challenge={defaultChallenge}
387
+ status={status}
388
+ winnerId={winnerId}
389
+ />
390
+
391
+ <div className="p-4 rounded-lg border text-sm">
392
+ <div className="grid grid-cols-3 gap-2 text-center">
393
+ <div className={leftIsWinning ? 'text-green-500 font-medium' : ''}>
394
+ Left {leftIsWinning && '(Winning)'}
395
+ </div>
396
+ <div className={isTied ? 'text-yellow-500 font-medium' : ''}>
397
+ {isTied ? 'Tied!' : 'vs'}
398
+ </div>
399
+ <div className={rightIsWinning ? 'text-green-500 font-medium' : ''}>
400
+ Right {rightIsWinning && '(Winning)'}
401
+ </div>
402
+ </div>
403
+ <p className="text-muted-foreground text-center mt-2">
404
+ Scores update automatically every 2 seconds
405
+ </p>
406
+ <div className="flex justify-center mt-3">
407
+ <button
408
+ onClick={() => updateStatus('finished')}
409
+ className="px-3 py-1.5 text-xs rounded bg-red-500 text-white"
410
+ >
411
+ End Match
412
+ </button>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ )
417
+ },
418
+ }
419
+
420
+ export const NoAvatars: Story = {
421
+ render: () => (
422
+ <div className="w-[500px]">
423
+ <WakaVersusCard
424
+ competitorLeft={{ ...defaultCompetitorLeft, avatar: undefined }}
425
+ competitorRight={{ ...defaultCompetitorRight, avatar: undefined }}
426
+ challenge={defaultChallenge}
427
+ status="live"
428
+ />
429
+ </div>
430
+ ),
431
+ }
432
+
433
+ export const Upcoming: Story = {
434
+ render: () => (
435
+ <div className="w-[500px]">
436
+ <WakaVersusCard
437
+ competitorLeft={{ ...defaultCompetitorLeft, score: 0 }}
438
+ competitorRight={{ ...defaultCompetitorRight, score: 0 }}
439
+ challenge={{
440
+ ...defaultChallenge,
441
+ startTime: new Date(Date.now() + 2 * 60 * 60 * 1000),
442
+ }}
443
+ status="upcoming"
444
+ />
445
+ </div>
446
+ ),
447
+ }
448
+
449
+ export const MultipleRewards: Story = {
450
+ render: () => {
451
+ const challengeWithRewards: Challenge = {
452
+ ...defaultChallenge,
453
+ name: 'Grand Championship',
454
+ rewards: [
455
+ { type: 'xp', value: 1000 },
456
+ { type: 'badge', value: 'Champion' },
457
+ { type: 'currency', value: 500 },
458
+ { type: 'item', value: 'Rare Sword' },
459
+ ],
460
+ }
461
+
462
+ return (
463
+ <div className="w-[550px]">
464
+ <WakaVersusCard
465
+ competitorLeft={defaultCompetitorLeft}
466
+ competitorRight={defaultCompetitorRight}
467
+ challenge={challengeWithRewards}
468
+ status="live"
469
+ />
470
+ </div>
471
+ )
472
+ },
473
+ }
474
+
475
+ export const TournamentMatch: Story = {
476
+ render: () => (
477
+ <div className="p-6 rounded-xl bg-gradient-to-br from-violet-900/20 to-purple-900/20 border border-violet-500/30">
478
+ <div className="text-center mb-6">
479
+ <div className="text-xs text-violet-400 uppercase tracking-wider mb-1">Quarter Finals</div>
480
+ <h2 className="text-xl font-bold">Spring Championship 2024</h2>
481
+ <p className="text-sm text-muted-foreground">Match 3 of 4</p>
482
+ </div>
483
+
484
+ <div className="w-[550px] mx-auto">
485
+ <WakaVersusCard
486
+ competitorLeft={{
487
+ ...defaultCompetitorLeft,
488
+ rank: 'Seed #1',
489
+ winStreak: 8,
490
+ }}
491
+ competitorRight={{
492
+ ...defaultCompetitorRight,
493
+ rank: 'Seed #4',
494
+ winStreak: 2,
495
+ }}
496
+ challenge={{
497
+ type: 'score',
498
+ name: 'Best of 3',
499
+ description: 'First to 2 wins advances',
500
+ targetScore: 2,
501
+ endTime: new Date(Date.now() + 30 * 60 * 1000),
502
+ rewards: [
503
+ { type: 'xp', value: 2000 },
504
+ { type: 'badge', value: 'Finalist' },
505
+ ],
506
+ }}
507
+ status="live"
508
+ size="lg"
509
+ />
510
+ </div>
511
+ </div>
512
+ ),
513
+ }
514
+
515
+ export const NoLivePulse: Story = {
516
+ render: () => (
517
+ <div className="w-[500px]">
518
+ <p className="text-sm text-muted-foreground mb-4">
519
+ Live match without pulse animation
520
+ </p>
521
+ <WakaVersusCard
522
+ competitorLeft={defaultCompetitorLeft}
523
+ competitorRight={defaultCompetitorRight}
524
+ challenge={defaultChallenge}
525
+ status="live"
526
+ showLivePulse={false}
527
+ />
528
+ </div>
529
+ ),
530
+ }