@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,353 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaAchievementUnlock, useAchievements } from './index'
3
+ import type { Achievement } from './index'
4
+ import * as React from 'react'
5
+ import { Trophy, Star, Zap, Target, Flame, Crown, Gem, Award } from 'lucide-react'
6
+
7
+ const sampleAchievement: Achievement = {
8
+ id: 'first-win',
9
+ name: 'First Victory',
10
+ description: 'Win your first game',
11
+ icon: <Trophy className="h-8 w-8" />,
12
+ rarity: 'common',
13
+ unlockedAt: new Date(),
14
+ xpReward: 100,
15
+ }
16
+
17
+ const achievements: Achievement[] = [
18
+ {
19
+ id: 'first-win',
20
+ name: 'First Victory',
21
+ description: 'Win your first game',
22
+ icon: <Trophy className="h-8 w-8" />,
23
+ rarity: 'common',
24
+ xpReward: 100,
25
+ },
26
+ {
27
+ id: 'streak-7',
28
+ name: 'Week Warrior',
29
+ description: 'Maintain a 7-day streak',
30
+ icon: <Flame className="h-8 w-8" />,
31
+ rarity: 'rare',
32
+ xpReward: 250,
33
+ },
34
+ {
35
+ id: 'level-25',
36
+ name: 'Quarter Century',
37
+ description: 'Reach level 25',
38
+ icon: <Star className="h-8 w-8" />,
39
+ rarity: 'epic',
40
+ xpReward: 500,
41
+ },
42
+ {
43
+ id: 'legendary-item',
44
+ name: 'Legendary Collector',
45
+ description: 'Obtain your first legendary item',
46
+ icon: <Crown className="h-8 w-8" />,
47
+ rarity: 'legendary',
48
+ xpReward: 1000,
49
+ },
50
+ ]
51
+
52
+ const meta: Meta<typeof WakaAchievementUnlock> = {
53
+ title: 'Components/Gamification/WakaAchievementUnlock',
54
+ component: WakaAchievementUnlock,
55
+ parameters: {
56
+ layout: 'centered',
57
+ docs: {
58
+ description: {
59
+ component: 'An achievement unlock modal with particle effects, glow animations, and celebration effects.',
60
+ },
61
+ },
62
+ },
63
+ tags: ['autodocs'],
64
+ argTypes: {
65
+ variant: {
66
+ control: 'select',
67
+ options: ['modal', 'toast', 'inline'],
68
+ description: 'Display variant',
69
+ },
70
+ animated: {
71
+ control: 'boolean',
72
+ description: 'Enable animations',
73
+ },
74
+ showConfetti: {
75
+ control: 'boolean',
76
+ description: 'Show confetti on unlock',
77
+ },
78
+ autoClose: {
79
+ control: 'boolean',
80
+ description: 'Auto close after delay',
81
+ },
82
+ autoCloseDelay: {
83
+ control: { type: 'number', min: 1000, max: 10000, step: 500 },
84
+ description: 'Auto close delay in ms',
85
+ },
86
+ },
87
+ }
88
+
89
+ export default meta
90
+ type Story = StoryObj<typeof WakaAchievementUnlock>
91
+
92
+ export const Default: Story = {
93
+ args: {
94
+ achievement: sampleAchievement,
95
+ isOpen: true,
96
+ variant: 'modal',
97
+ animated: true,
98
+ showConfetti: true,
99
+ autoClose: false,
100
+ },
101
+ render: (args) => <WakaAchievementUnlock {...args} onClose={() => {}} />,
102
+ }
103
+
104
+ export const Rarities: Story = {
105
+ render: () => (
106
+ <div className="grid grid-cols-2 gap-4">
107
+ {achievements.map((achievement) => (
108
+ <WakaAchievementUnlock
109
+ key={achievement.id}
110
+ achievement={{ ...achievement, unlockedAt: new Date() }}
111
+ isOpen={true}
112
+ onClose={() => {}}
113
+ variant="inline"
114
+ />
115
+ ))}
116
+ </div>
117
+ ),
118
+ }
119
+
120
+ export const Variants: Story = {
121
+ render: () => {
122
+ const [variant, setVariant] = React.useState<'modal' | 'toast' | 'inline'>('inline')
123
+ const [isOpen, setIsOpen] = React.useState(true)
124
+
125
+ return (
126
+ <div className="space-y-4">
127
+ <div className="flex gap-2 mb-4">
128
+ <button
129
+ onClick={() => { setVariant('modal'); setIsOpen(true) }}
130
+ className={`px-3 py-1.5 text-sm rounded ${variant === 'modal' ? 'bg-primary text-primary-foreground' : 'border hover:bg-muted'}`}
131
+ >
132
+ Modal
133
+ </button>
134
+ <button
135
+ onClick={() => { setVariant('toast'); setIsOpen(true) }}
136
+ className={`px-3 py-1.5 text-sm rounded ${variant === 'toast' ? 'bg-primary text-primary-foreground' : 'border hover:bg-muted'}`}
137
+ >
138
+ Toast
139
+ </button>
140
+ <button
141
+ onClick={() => { setVariant('inline'); setIsOpen(true) }}
142
+ className={`px-3 py-1.5 text-sm rounded ${variant === 'inline' ? 'bg-primary text-primary-foreground' : 'border hover:bg-muted'}`}
143
+ >
144
+ Inline
145
+ </button>
146
+ </div>
147
+
148
+ <WakaAchievementUnlock
149
+ achievement={achievements[1]}
150
+ isOpen={isOpen}
151
+ onClose={() => setIsOpen(false)}
152
+ variant={variant}
153
+ />
154
+ </div>
155
+ )
156
+ },
157
+ }
158
+
159
+ export const Interactive: Story = {
160
+ render: () => {
161
+ const [isOpen, setIsOpen] = React.useState(false)
162
+ const [currentAchievement, setCurrentAchievement] = React.useState<Achievement | null>(null)
163
+
164
+ const unlock = (achievement: Achievement) => {
165
+ setCurrentAchievement({ ...achievement, unlockedAt: new Date() })
166
+ setIsOpen(true)
167
+ }
168
+
169
+ return (
170
+ <div className="space-y-6">
171
+ <div className="grid grid-cols-2 gap-3">
172
+ {achievements.map((achievement) => (
173
+ <button
174
+ key={achievement.id}
175
+ onClick={() => unlock(achievement)}
176
+ className="p-4 rounded-lg border hover:border-primary/50 hover:bg-muted/50 text-left transition-all"
177
+ >
178
+ <div className="flex items-center gap-3">
179
+ <div className="text-muted-foreground">{achievement.icon}</div>
180
+ <div>
181
+ <p className="font-medium">{achievement.name}</p>
182
+ <p className="text-xs text-muted-foreground">{achievement.rarity}</p>
183
+ </div>
184
+ </div>
185
+ </button>
186
+ ))}
187
+ </div>
188
+
189
+ {currentAchievement && (
190
+ <WakaAchievementUnlock
191
+ achievement={currentAchievement}
192
+ isOpen={isOpen}
193
+ onClose={() => setIsOpen(false)}
194
+ showConfetti
195
+ />
196
+ )}
197
+ </div>
198
+ )
199
+ },
200
+ }
201
+
202
+ export const WithHook: Story = {
203
+ render: () => {
204
+ const {
205
+ achievements: userAchievements,
206
+ unlock,
207
+ isUnlocked,
208
+ getByRarity,
209
+ totalXP,
210
+ pendingUnlock,
211
+ dismissPending,
212
+ } = useAchievements({
213
+ allAchievements: achievements,
214
+ })
215
+
216
+ return (
217
+ <div className="space-y-6 w-[400px]">
218
+ <div className="p-4 rounded-lg border">
219
+ <div className="flex justify-between items-center mb-4">
220
+ <h3 className="font-semibold">Achievements</h3>
221
+ <span className="text-sm text-muted-foreground">
222
+ {userAchievements.length}/{achievements.length} • {totalXP} XP
223
+ </span>
224
+ </div>
225
+
226
+ <div className="space-y-2">
227
+ {achievements.map((achievement) => (
228
+ <div
229
+ key={achievement.id}
230
+ className={`flex items-center gap-3 p-2 rounded-lg ${
231
+ isUnlocked(achievement.id) ? 'bg-green-500/10' : 'bg-muted/30'
232
+ }`}
233
+ >
234
+ <div className={isUnlocked(achievement.id) ? '' : 'opacity-30'}>
235
+ {achievement.icon}
236
+ </div>
237
+ <div className="flex-1">
238
+ <p className="text-sm font-medium">{achievement.name}</p>
239
+ <p className="text-xs text-muted-foreground">{achievement.rarity}</p>
240
+ </div>
241
+ {!isUnlocked(achievement.id) && (
242
+ <button
243
+ onClick={() => unlock(achievement.id)}
244
+ className="px-2 py-1 text-xs rounded bg-primary text-primary-foreground"
245
+ >
246
+ Unlock
247
+ </button>
248
+ )}
249
+ </div>
250
+ ))}
251
+ </div>
252
+ </div>
253
+
254
+ {pendingUnlock && (
255
+ <WakaAchievementUnlock
256
+ achievement={pendingUnlock}
257
+ isOpen={true}
258
+ onClose={dismissPending}
259
+ showConfetti
260
+ />
261
+ )}
262
+ </div>
263
+ )
264
+ },
265
+ }
266
+
267
+ export const ToastNotification: Story = {
268
+ render: () => {
269
+ const [queue, setQueue] = React.useState<Achievement[]>([])
270
+
271
+ const addToQueue = () => {
272
+ const randomAchievement = achievements[Math.floor(Math.random() * achievements.length)]
273
+ setQueue((prev) => [...prev, { ...randomAchievement, id: Date.now().toString(), unlockedAt: new Date() }])
274
+ }
275
+
276
+ const removeFromQueue = (id: string) => {
277
+ setQueue((prev) => prev.filter((a) => a.id !== id))
278
+ }
279
+
280
+ return (
281
+ <div className="space-y-4">
282
+ <button
283
+ onClick={addToQueue}
284
+ className="px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium"
285
+ >
286
+ Unlock Random Achievement
287
+ </button>
288
+
289
+ <div className="fixed top-4 right-4 space-y-2 z-50">
290
+ {queue.map((achievement) => (
291
+ <WakaAchievementUnlock
292
+ key={achievement.id}
293
+ achievement={achievement}
294
+ isOpen={true}
295
+ onClose={() => removeFromQueue(achievement.id)}
296
+ variant="toast"
297
+ autoClose
298
+ autoCloseDelay={5000}
299
+ />
300
+ ))}
301
+ </div>
302
+ </div>
303
+ )
304
+ },
305
+ }
306
+
307
+ export const AchievementShowcase: Story = {
308
+ render: () => {
309
+ const unlockedAchievements: Achievement[] = achievements.map((a) => ({
310
+ ...a,
311
+ unlockedAt: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000),
312
+ }))
313
+
314
+ return (
315
+ <div className="w-[500px] p-6 rounded-xl border bg-card">
316
+ <h2 className="text-xl font-bold mb-6">Achievement Showcase</h2>
317
+
318
+ <div className="grid grid-cols-2 gap-3">
319
+ {unlockedAchievements.map((achievement) => (
320
+ <WakaAchievementUnlock
321
+ key={achievement.id}
322
+ achievement={achievement}
323
+ isOpen={true}
324
+ onClose={() => {}}
325
+ variant="inline"
326
+ animated={false}
327
+ />
328
+ ))}
329
+ </div>
330
+
331
+ <div className="mt-6 pt-4 border-t">
332
+ <div className="flex justify-between text-sm">
333
+ <span className="text-muted-foreground">Total XP Earned</span>
334
+ <span className="font-bold text-amber-500">1,850 XP</span>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ )
339
+ },
340
+ }
341
+
342
+ export const NoAnimation: Story = {
343
+ render: () => (
344
+ <WakaAchievementUnlock
345
+ achievement={sampleAchievement}
346
+ isOpen={true}
347
+ onClose={() => {}}
348
+ animated={false}
349
+ showConfetti={false}
350
+ variant="inline"
351
+ />
352
+ ),
353
+ }
@@ -0,0 +1,258 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaArtifactList, defaultArtifacts } from './index'
3
+ import type { Artifact } from './index'
4
+ import * as React from 'react'
5
+
6
+ const manyArtifacts: Artifact[] = [
7
+ ...defaultArtifacts,
8
+ {
9
+ id: '7',
10
+ name: 'build-windows-x64.zip',
11
+ type: 'archive',
12
+ size: 48 * 1024 * 1024,
13
+ createdAt: new Date(Date.now() - 2 * 3600000),
14
+ expiresAt: new Date(Date.now() + 7 * 24 * 3600000),
15
+ downloadUrl: '#',
16
+ sha256: 'd4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a1b2c3',
17
+ },
18
+ {
19
+ id: '8',
20
+ name: 'lint-report.json',
21
+ type: 'report',
22
+ size: 128 * 1024,
23
+ createdAt: new Date(Date.now() - 2 * 3600000),
24
+ downloadUrl: '#',
25
+ },
26
+ {
27
+ id: '9',
28
+ name: 'security-scan.sarif',
29
+ type: 'report',
30
+ size: 512 * 1024,
31
+ createdAt: new Date(Date.now() - 2 * 3600000),
32
+ downloadUrl: '#',
33
+ },
34
+ {
35
+ id: '10',
36
+ name: 'test-output.log',
37
+ type: 'log',
38
+ size: 2.5 * 1024 * 1024,
39
+ createdAt: new Date(Date.now() - 2 * 3600000),
40
+ downloadUrl: '#',
41
+ },
42
+ {
43
+ id: '11',
44
+ name: 'app-binary',
45
+ type: 'binary',
46
+ size: 15 * 1024 * 1024,
47
+ createdAt: new Date(Date.now() - 2 * 3600000),
48
+ downloadUrl: '#',
49
+ sha256: 'e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a1b2c3d4',
50
+ },
51
+ {
52
+ id: '12',
53
+ name: 'screenshots.zip',
54
+ type: 'image',
55
+ size: 8 * 1024 * 1024,
56
+ createdAt: new Date(Date.now() - 2 * 3600000),
57
+ downloadUrl: '#',
58
+ },
59
+ ]
60
+
61
+ const expiringSoonArtifacts: Artifact[] = [
62
+ {
63
+ id: '1',
64
+ name: 'build-urgent.tar.gz',
65
+ type: 'archive',
66
+ size: 50 * 1024 * 1024,
67
+ createdAt: new Date(Date.now() - 6 * 24 * 3600000),
68
+ expiresAt: new Date(Date.now() + 6 * 3600000),
69
+ downloadUrl: '#',
70
+ },
71
+ {
72
+ id: '2',
73
+ name: 'logs-expiring.txt',
74
+ type: 'log',
75
+ size: 1 * 1024 * 1024,
76
+ createdAt: new Date(Date.now() - 6 * 24 * 3600000),
77
+ expiresAt: new Date(Date.now() + 12 * 3600000),
78
+ downloadUrl: '#',
79
+ },
80
+ {
81
+ id: '3',
82
+ name: 'stable-artifact.zip',
83
+ type: 'archive',
84
+ size: 30 * 1024 * 1024,
85
+ createdAt: new Date(Date.now() - 1 * 24 * 3600000),
86
+ expiresAt: new Date(Date.now() + 30 * 24 * 3600000),
87
+ downloadUrl: '#',
88
+ },
89
+ ]
90
+
91
+ const meta: Meta<typeof WakaArtifactList> = {
92
+ title: 'Components/DevOps/WakaArtifactList',
93
+ component: WakaArtifactList,
94
+ parameters: {
95
+ layout: 'centered',
96
+ docs: {
97
+ description: {
98
+ component: 'A build artifact list with type filtering, size display, expiration warnings, SHA256 checksums, and download/delete actions.',
99
+ },
100
+ },
101
+ },
102
+ tags: ['autodocs'],
103
+ argTypes: {
104
+ showExpiration: {
105
+ control: 'boolean',
106
+ description: 'Show expiration dates',
107
+ },
108
+ allowDelete: {
109
+ control: 'boolean',
110
+ description: 'Allow artifact deletion',
111
+ },
112
+ },
113
+ }
114
+
115
+ export default meta
116
+ type Story = StoryObj<typeof WakaArtifactList>
117
+
118
+ export const Default: Story = {
119
+ args: {
120
+ artifacts: defaultArtifacts,
121
+ },
122
+ render: (args) => (
123
+ <div className="w-[800px] h-[500px]">
124
+ <WakaArtifactList
125
+ {...args}
126
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
127
+ onView={(artifact) => console.log('View:', artifact.name)}
128
+ />
129
+ </div>
130
+ ),
131
+ }
132
+
133
+ export const ManyArtifacts: Story = {
134
+ render: () => (
135
+ <div className="w-[800px] h-[600px]">
136
+ <WakaArtifactList
137
+ artifacts={manyArtifacts}
138
+ title="Build Artifacts"
139
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
140
+ onView={(artifact) => console.log('View:', artifact.name)}
141
+ />
142
+ </div>
143
+ ),
144
+ }
145
+
146
+ export const WithDelete: Story = {
147
+ render: () => {
148
+ const [artifacts, setArtifacts] = React.useState(defaultArtifacts)
149
+
150
+ const handleDelete = (artifact: Artifact) => {
151
+ if (confirm(`Delete ${artifact.name}?`)) {
152
+ setArtifacts((prev) => prev.filter((a) => a.id !== artifact.id))
153
+ }
154
+ }
155
+
156
+ return (
157
+ <div className="w-[800px] h-[500px]">
158
+ <WakaArtifactList
159
+ artifacts={artifacts}
160
+ allowDelete
161
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
162
+ onDelete={handleDelete}
163
+ onView={(artifact) => console.log('View:', artifact.name)}
164
+ />
165
+ </div>
166
+ )
167
+ },
168
+ }
169
+
170
+ export const ExpiringSoon: Story = {
171
+ render: () => (
172
+ <div className="w-[800px] h-[400px]">
173
+ <p className="text-sm text-muted-foreground mb-4">
174
+ Some artifacts are expiring within 24 hours (highlighted in yellow)
175
+ </p>
176
+ <WakaArtifactList
177
+ artifacts={expiringSoonArtifacts}
178
+ title="Expiring Artifacts"
179
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
180
+ onView={(artifact) => console.log('View:', artifact.name)}
181
+ />
182
+ </div>
183
+ ),
184
+ }
185
+
186
+ export const NoExpiration: Story = {
187
+ render: () => (
188
+ <div className="w-[800px] h-[500px]">
189
+ <WakaArtifactList
190
+ artifacts={defaultArtifacts}
191
+ showExpiration={false}
192
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
193
+ onView={(artifact) => console.log('View:', artifact.name)}
194
+ />
195
+ </div>
196
+ ),
197
+ }
198
+
199
+ export const ArchivesOnly: Story = {
200
+ render: () => (
201
+ <div className="w-[800px] h-[400px]">
202
+ <WakaArtifactList
203
+ artifacts={defaultArtifacts.filter((a) => a.type === 'archive')}
204
+ title="Release Archives"
205
+ onDownload={(artifact) => console.log('Download:', artifact.name)}
206
+ onView={(artifact) => console.log('View:', artifact.name)}
207
+ />
208
+ </div>
209
+ ),
210
+ }
211
+
212
+ export const Empty: Story = {
213
+ render: () => (
214
+ <div className="w-[800px] h-[400px]">
215
+ <WakaArtifactList
216
+ artifacts={[]}
217
+ title="No Artifacts"
218
+ />
219
+ </div>
220
+ ),
221
+ }
222
+
223
+ export const CIBuildArtifacts: Story = {
224
+ render: () => (
225
+ <div className="p-6 rounded-xl border bg-card">
226
+ <div className="flex items-center justify-between mb-6">
227
+ <div>
228
+ <h2 className="text-xl font-bold">Build #1234 Artifacts</h2>
229
+ <p className="text-sm text-muted-foreground">
230
+ Branch: main • Commit: abc123f
231
+ </p>
232
+ </div>
233
+ <div className="flex items-center gap-2 text-sm">
234
+ <span className="text-muted-foreground">Total size:</span>
235
+ <span className="font-medium">339.9 MB</span>
236
+ </div>
237
+ </div>
238
+
239
+ <div className="w-[750px]">
240
+ <WakaArtifactList
241
+ artifacts={defaultArtifacts}
242
+ allowDelete
243
+ onDownload={(artifact) => {
244
+ console.log('Downloading:', artifact.name)
245
+ alert(`Downloading ${artifact.name}...`)
246
+ }}
247
+ onDelete={(artifact) => {
248
+ console.log('Delete:', artifact.name)
249
+ }}
250
+ onView={(artifact) => {
251
+ console.log('View:', artifact.name)
252
+ alert(`Viewing details for ${artifact.name}`)
253
+ }}
254
+ />
255
+ </div>
256
+ </div>
257
+ ),
258
+ }