@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,381 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { OnCallScheduleBlock, defaultOnCallSchedule } from './index'
3
+ import type { OnCallSchedule, OnCallUser, OnCallShift, EscalationLevel } from './index'
4
+ import * as React from 'react'
5
+
6
+ const meta: Meta<typeof OnCallScheduleBlock> = {
7
+ title: 'Blocks/OnCallSchedule',
8
+ component: OnCallScheduleBlock,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'An on-call schedule component with weekly view, escalation policies, team members, current on-call status, and shift management.',
15
+ },
16
+ },
17
+ },
18
+ tags: ['autodocs'],
19
+ }
20
+
21
+ export default meta
22
+ type Story = StoryObj<typeof OnCallScheduleBlock>
23
+
24
+ export const Default: Story = {
25
+ render: () => (
26
+ <div className="p-6">
27
+ <OnCallScheduleBlock
28
+ schedule={defaultOnCallSchedule}
29
+ onEditSchedule={() => console.log('Edit schedule')}
30
+ onAddOverride={(date) => console.log('Add override:', date)}
31
+ onSwapShift={(shift) => console.log('Swap shift:', shift)}
32
+ onContactOnCall={(user) => console.log('Contact:', user)}
33
+ />
34
+ </div>
35
+ ),
36
+ }
37
+
38
+ export const CurrentlyOnCall: Story = {
39
+ render: () => {
40
+ // Ensure someone is currently on call
41
+ const now = new Date()
42
+ const currentShift: OnCallShift = {
43
+ id: 'current-shift',
44
+ user: defaultOnCallSchedule.users[0],
45
+ startTime: new Date(now.getTime() - 4 * 3600000), // Started 4 hours ago
46
+ endTime: new Date(now.getTime() + 8 * 3600000), // Ends in 8 hours
47
+ level: 1,
48
+ }
49
+
50
+ const scheduleWithCurrent: OnCallSchedule = {
51
+ ...defaultOnCallSchedule,
52
+ shifts: [currentShift, ...defaultOnCallSchedule.shifts],
53
+ }
54
+
55
+ return (
56
+ <div className="p-6">
57
+ <OnCallScheduleBlock
58
+ schedule={scheduleWithCurrent}
59
+ onContactOnCall={(user) => alert(`Calling ${user.name}...`)}
60
+ />
61
+ </div>
62
+ )
63
+ },
64
+ }
65
+
66
+ export const NoCurrentOnCall: Story = {
67
+ render: () => {
68
+ // All shifts in the future
69
+ const futureShifts: OnCallShift[] = defaultOnCallSchedule.shifts.map(shift => ({
70
+ ...shift,
71
+ startTime: new Date(Date.now() + 24 * 3600000),
72
+ endTime: new Date(Date.now() + 48 * 3600000),
73
+ }))
74
+
75
+ const scheduleNoOnCall: OnCallSchedule = {
76
+ ...defaultOnCallSchedule,
77
+ shifts: futureShifts,
78
+ }
79
+
80
+ return (
81
+ <div className="p-6">
82
+ <OnCallScheduleBlock schedule={scheduleNoOnCall} />
83
+ </div>
84
+ )
85
+ },
86
+ }
87
+
88
+ export const WithOverride: Story = {
89
+ render: () => {
90
+ const now = new Date()
91
+ const overrideShift: OnCallShift = {
92
+ id: 'override-shift',
93
+ user: defaultOnCallSchedule.users[2],
94
+ startTime: new Date(now.getTime() - 2 * 3600000),
95
+ endTime: new Date(now.getTime() + 10 * 3600000),
96
+ level: 1,
97
+ overrideReason: 'Covering for John who is on PTO',
98
+ }
99
+
100
+ const scheduleWithOverride: OnCallSchedule = {
101
+ ...defaultOnCallSchedule,
102
+ shifts: [overrideShift, ...defaultOnCallSchedule.shifts],
103
+ }
104
+
105
+ return (
106
+ <div className="p-6">
107
+ <OnCallScheduleBlock
108
+ schedule={scheduleWithOverride}
109
+ onContactOnCall={(user) => console.log('Contact:', user)}
110
+ />
111
+ </div>
112
+ )
113
+ },
114
+ }
115
+
116
+ export const DifferentRotations: Story = {
117
+ render: () => {
118
+ const weeklySchedule: OnCallSchedule = {
119
+ ...defaultOnCallSchedule,
120
+ name: 'Backend Team On-Call',
121
+ team: 'Backend Engineering',
122
+ rotation: 'weekly',
123
+ }
124
+
125
+ const dailySchedule: OnCallSchedule = {
126
+ ...defaultOnCallSchedule,
127
+ id: 'daily',
128
+ name: 'Frontend Team On-Call',
129
+ team: 'Frontend Engineering',
130
+ rotation: 'daily',
131
+ }
132
+
133
+ return (
134
+ <div className="p-6 space-y-8">
135
+ <OnCallScheduleBlock schedule={weeklySchedule} />
136
+ <OnCallScheduleBlock schedule={dailySchedule} />
137
+ </div>
138
+ )
139
+ },
140
+ }
141
+
142
+ export const MultipleEscalationLevels: Story = {
143
+ render: () => {
144
+ const now = new Date()
145
+ const multiLevelShifts: OnCallShift[] = [
146
+ {
147
+ id: 'level1-shift',
148
+ user: defaultOnCallSchedule.users[0],
149
+ startTime: new Date(now.getTime() - 4 * 3600000),
150
+ endTime: new Date(now.getTime() + 8 * 3600000),
151
+ level: 1,
152
+ },
153
+ {
154
+ id: 'level2-shift',
155
+ user: defaultOnCallSchedule.users[1],
156
+ startTime: new Date(now.getTime() - 4 * 3600000),
157
+ endTime: new Date(now.getTime() + 8 * 3600000),
158
+ level: 2,
159
+ },
160
+ {
161
+ id: 'level3-shift',
162
+ user: defaultOnCallSchedule.users[2],
163
+ startTime: new Date(now.getTime() - 4 * 3600000),
164
+ endTime: new Date(now.getTime() + 8 * 3600000),
165
+ level: 3,
166
+ },
167
+ ]
168
+
169
+ const multiLevelSchedule: OnCallSchedule = {
170
+ ...defaultOnCallSchedule,
171
+ shifts: [...multiLevelShifts, ...defaultOnCallSchedule.shifts],
172
+ }
173
+
174
+ return (
175
+ <div className="p-6">
176
+ <OnCallScheduleBlock
177
+ schedule={multiLevelSchedule}
178
+ onContactOnCall={(user) => console.log('Contact:', user)}
179
+ />
180
+ </div>
181
+ )
182
+ },
183
+ }
184
+
185
+ export const SmallTeam: Story = {
186
+ render: () => {
187
+ const smallTeam: OnCallUser[] = [
188
+ { id: '1', name: 'Alice', email: 'alice@example.com', phone: '+1 555-0101', status: 'available' },
189
+ { id: '2', name: 'Bob', email: 'bob@example.com', phone: '+1 555-0102', status: 'available' },
190
+ ]
191
+
192
+ const now = new Date()
193
+ const smallTeamShifts: OnCallShift[] = [
194
+ {
195
+ id: 's1',
196
+ user: smallTeam[0],
197
+ startTime: new Date(now.getTime() - 2 * 3600000),
198
+ endTime: new Date(now.getTime() + 22 * 3600000),
199
+ level: 1,
200
+ },
201
+ ]
202
+
203
+ const smallTeamSchedule: OnCallSchedule = {
204
+ id: 'small-team',
205
+ name: 'Startup On-Call',
206
+ team: 'Engineering',
207
+ rotation: 'daily',
208
+ escalationPolicy: {
209
+ level1Timeout: 10,
210
+ level2Timeout: 20,
211
+ level3Timeout: 30,
212
+ },
213
+ shifts: smallTeamShifts,
214
+ users: smallTeam,
215
+ }
216
+
217
+ return (
218
+ <div className="p-6">
219
+ <OnCallScheduleBlock schedule={smallTeamSchedule} />
220
+ </div>
221
+ )
222
+ },
223
+ }
224
+
225
+ export const LargeTeam: Story = {
226
+ render: () => {
227
+ const largeTeam: OnCallUser[] = Array.from({ length: 12 }, (_, i) => ({
228
+ id: `user-${i + 1}`,
229
+ name: `Team Member ${i + 1}`,
230
+ email: `member${i + 1}@example.com`,
231
+ phone: `+1 555-${String(i + 1).padStart(4, '0')}`,
232
+ status: (['available', 'busy', 'available', 'offline'] as const)[i % 4],
233
+ timezone: ['America/New_York', 'America/Los_Angeles', 'Europe/London', 'Asia/Tokyo'][i % 4],
234
+ }))
235
+
236
+ const largeTeamSchedule: OnCallSchedule = {
237
+ ...defaultOnCallSchedule,
238
+ name: 'Global Support Team',
239
+ team: 'Customer Support',
240
+ users: largeTeam,
241
+ }
242
+
243
+ return (
244
+ <div className="p-6">
245
+ <OnCallScheduleBlock schedule={largeTeamSchedule} />
246
+ </div>
247
+ )
248
+ },
249
+ }
250
+
251
+ export const DifferentUserStatuses: Story = {
252
+ render: () => {
253
+ const usersWithStatuses: OnCallUser[] = [
254
+ { id: '1', name: 'John (Available)', email: 'john@example.com', status: 'available' },
255
+ { id: '2', name: 'Jane (Busy)', email: 'jane@example.com', status: 'busy' },
256
+ { id: '3', name: 'Bob (DND)', email: 'bob@example.com', status: 'dnd' },
257
+ { id: '4', name: 'Alice (Offline)', email: 'alice@example.com', status: 'offline' },
258
+ ]
259
+
260
+ const statusSchedule: OnCallSchedule = {
261
+ ...defaultOnCallSchedule,
262
+ users: usersWithStatuses,
263
+ }
264
+
265
+ return (
266
+ <div className="p-6">
267
+ <OnCallScheduleBlock schedule={statusSchedule} />
268
+ </div>
269
+ )
270
+ },
271
+ }
272
+
273
+ export const CustomEscalationPolicy: Story = {
274
+ render: () => {
275
+ const criticalSchedule: OnCallSchedule = {
276
+ ...defaultOnCallSchedule,
277
+ name: 'Critical Infrastructure',
278
+ team: 'SRE',
279
+ escalationPolicy: {
280
+ level1Timeout: 5, // Very fast escalation
281
+ level2Timeout: 10,
282
+ level3Timeout: 15,
283
+ },
284
+ }
285
+
286
+ return (
287
+ <div className="p-6">
288
+ <OnCallScheduleBlock schedule={criticalSchedule} />
289
+ </div>
290
+ )
291
+ },
292
+ }
293
+
294
+ export const Interactive: Story = {
295
+ render: () => {
296
+ const [schedule, setSchedule] = React.useState(defaultOnCallSchedule)
297
+
298
+ const handleAddOverride = (date: Date) => {
299
+ alert(`Add override for ${date.toLocaleDateString()}`)
300
+ }
301
+
302
+ const handleSwapShift = (shift: OnCallShift) => {
303
+ alert(`Swap shift: ${shift.user.name}'s shift on ${shift.startTime.toLocaleDateString()}`)
304
+ }
305
+
306
+ const handleContact = (user: OnCallUser) => {
307
+ alert(`Contacting ${user.name}:\nEmail: ${user.email}\nPhone: ${user.phone || 'N/A'}`)
308
+ }
309
+
310
+ const handleEditSchedule = () => {
311
+ alert('Opening schedule editor...')
312
+ }
313
+
314
+ return (
315
+ <div className="p-6">
316
+ <OnCallScheduleBlock
317
+ schedule={schedule}
318
+ onEditSchedule={handleEditSchedule}
319
+ onAddOverride={handleAddOverride}
320
+ onSwapShift={handleSwapShift}
321
+ onContactOnCall={handleContact}
322
+ />
323
+ </div>
324
+ )
325
+ },
326
+ }
327
+
328
+ export const ReadOnly: Story = {
329
+ render: () => (
330
+ <div className="p-6">
331
+ <OnCallScheduleBlock schedule={defaultOnCallSchedule} />
332
+ </div>
333
+ ),
334
+ }
335
+
336
+ export const IncidentDashboard: Story = {
337
+ render: () => {
338
+ const now = new Date()
339
+ const currentOnCall: OnCallShift = {
340
+ id: 'current',
341
+ user: {
342
+ ...defaultOnCallSchedule.users[0],
343
+ status: 'available',
344
+ },
345
+ startTime: new Date(now.getTime() - 6 * 3600000),
346
+ endTime: new Date(now.getTime() + 6 * 3600000),
347
+ level: 1,
348
+ }
349
+
350
+ const incidentSchedule: OnCallSchedule = {
351
+ ...defaultOnCallSchedule,
352
+ shifts: [currentOnCall, ...defaultOnCallSchedule.shifts],
353
+ }
354
+
355
+ return (
356
+ <div className="min-h-screen bg-muted/30">
357
+ <header className="bg-background border-b px-6 py-4">
358
+ <div className="flex items-center justify-between">
359
+ <div>
360
+ <h1 className="font-bold text-xl">Incident Management</h1>
361
+ <p className="text-sm text-muted-foreground">On-Call & Escalation</p>
362
+ </div>
363
+ <div className="flex items-center gap-2">
364
+ <span className="text-sm text-muted-foreground">Current incident:</span>
365
+ <span className="px-2 py-1 bg-green-500/10 text-green-600 rounded text-sm font-medium">
366
+ No active incidents
367
+ </span>
368
+ </div>
369
+ </div>
370
+ </header>
371
+ <main className="p-6">
372
+ <OnCallScheduleBlock
373
+ schedule={incidentSchedule}
374
+ onContactOnCall={(user) => console.log('Contact:', user)}
375
+ onAddOverride={(date) => console.log('Override:', date)}
376
+ />
377
+ </main>
378
+ </div>
379
+ )
380
+ },
381
+ }
@@ -0,0 +1,316 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import {
3
+ PlayerProfile,
4
+ defaultPlayerStats,
5
+ defaultAchievements,
6
+ defaultBadges,
7
+ defaultMatchHistory,
8
+ } from './index'
9
+ import type { PlayerStats, Achievement, PlayerBadge, MatchHistory } from './index'
10
+ import * as React from 'react'
11
+ import { Trophy, Star, Flame, Crown, Medal, Zap, Target, Shield, Swords, Users, Gamepad2 } from 'lucide-react'
12
+
13
+ const meta: Meta<typeof PlayerProfile> = {
14
+ title: 'Blocks/PlayerProfile',
15
+ component: PlayerProfile,
16
+ parameters: {
17
+ layout: 'centered',
18
+ docs: {
19
+ description: {
20
+ component:
21
+ 'A gaming player profile page with XP progression, level display, stats, achievements with rarity tiers, badges, and match history tracking.',
22
+ },
23
+ },
24
+ },
25
+ tags: ['autodocs'],
26
+ }
27
+
28
+ export default meta
29
+ type Story = StoryObj<typeof PlayerProfile>
30
+
31
+ export const Default: Story = {
32
+ render: () => (
33
+ <div className="p-6 max-w-4xl">
34
+ <PlayerProfile
35
+ name="ShadowHunter"
36
+ title="Elite Warrior"
37
+ level={42}
38
+ currentXP={7500}
39
+ maxXP={10000}
40
+ totalXP={125000}
41
+ rank="Diamond II"
42
+ stats={defaultPlayerStats}
43
+ achievements={defaultAchievements}
44
+ badges={defaultBadges}
45
+ matchHistory={defaultMatchHistory}
46
+ streak={14}
47
+ playtime="245h"
48
+ friendsCount={89}
49
+ isOnline={true}
50
+ joinDate={new Date('2023-06-15')}
51
+ />
52
+ </div>
53
+ ),
54
+ }
55
+
56
+ export const NewPlayer: Story = {
57
+ render: () => {
58
+ const newPlayerStats: PlayerStats[] = [
59
+ { label: 'Matches Played', value: 12, icon: <Gamepad2 className="h-5 w-5" />, color: 'blue' },
60
+ { label: 'Wins', value: 5, icon: <Trophy className="h-5 w-5" />, color: 'green' },
61
+ { label: 'Win Rate', value: '41.7%', icon: <Target className="h-5 w-5" />, color: 'yellow' },
62
+ ]
63
+
64
+ const newPlayerAchievements: Achievement[] = [
65
+ {
66
+ id: 'first-match',
67
+ name: 'Welcome Aboard',
68
+ description: 'Play your first match',
69
+ rarity: 'common',
70
+ unlockedAt: new Date(),
71
+ icon: <Star className="h-6 w-6 text-yellow-500" />,
72
+ },
73
+ {
74
+ id: 'first-win',
75
+ name: 'First Victory',
76
+ description: 'Win your first match',
77
+ rarity: 'common',
78
+ progress: 1,
79
+ maxProgress: 1,
80
+ icon: <Trophy className="h-6 w-6 text-yellow-500" />,
81
+ },
82
+ {
83
+ id: 'ten-matches',
84
+ name: 'Getting Started',
85
+ description: 'Play 10 matches',
86
+ rarity: 'common',
87
+ progress: 12,
88
+ maxProgress: 10,
89
+ icon: <Gamepad2 className="h-6 w-6 text-blue-500" />,
90
+ },
91
+ ]
92
+
93
+ return (
94
+ <div className="p-6 max-w-4xl">
95
+ <PlayerProfile
96
+ name="NewPlayer123"
97
+ level={3}
98
+ currentXP={350}
99
+ maxXP={500}
100
+ totalXP={850}
101
+ stats={newPlayerStats}
102
+ achievements={newPlayerAchievements}
103
+ matchHistory={defaultMatchHistory.slice(0, 3)}
104
+ streak={2}
105
+ playtime="3h"
106
+ friendsCount={2}
107
+ isOnline={true}
108
+ joinDate={new Date()}
109
+ />
110
+ </div>
111
+ )
112
+ },
113
+ }
114
+
115
+ export const TopPlayer: Story = {
116
+ render: () => {
117
+ const topStats: PlayerStats[] = [
118
+ { label: 'Matches Played', value: '2,847', icon: <Gamepad2 className="h-5 w-5" />, color: 'blue' },
119
+ { label: 'Wins', value: '1,923', icon: <Trophy className="h-5 w-5" />, color: 'green' },
120
+ { label: 'Win Rate', value: '67.5%', icon: <Target className="h-5 w-5" />, color: 'yellow' },
121
+ { label: 'Best Score', value: '98,450', icon: <Star className="h-5 w-5" />, color: 'purple' },
122
+ { label: 'Kills', value: '45,230', icon: <Swords className="h-5 w-5" />, color: 'red' },
123
+ { label: 'MVP Awards', value: 342, icon: <Crown className="h-5 w-5" />, color: 'default' },
124
+ ]
125
+
126
+ const legendaryAchievements: Achievement[] = [
127
+ { id: '1', name: 'Legendary Champion', description: 'Reach the top 100 global ranking', rarity: 'legendary', unlockedAt: new Date('2024-01-15'), icon: <Crown className="h-6 w-6 text-amber-500" /> },
128
+ { id: '2', name: 'Master Strategist', description: 'Win 1000 matches', rarity: 'legendary', unlockedAt: new Date('2024-02-20'), icon: <Trophy className="h-6 w-6 text-amber-500" /> },
129
+ { id: '3', name: 'Eternal Flame', description: 'Maintain a 100-day streak', rarity: 'legendary', unlockedAt: new Date('2024-03-01'), icon: <Flame className="h-6 w-6 text-amber-500" /> },
130
+ { id: '4', name: 'Community Hero', description: 'Have 500 friends', rarity: 'epic', unlockedAt: new Date('2024-01-10'), icon: <Users className="h-6 w-6 text-purple-500" /> },
131
+ ]
132
+
133
+ const topBadges: PlayerBadge[] = [
134
+ { id: '1', name: 'Season 1 Champion', icon: <Crown className="h-4 w-4" />, color: '#f59e0b' },
135
+ { id: '2', name: 'Top 100', icon: <Medal className="h-4 w-4" />, color: '#ef4444' },
136
+ { id: '3', name: 'Pro Player', icon: <Zap className="h-4 w-4" />, color: '#8b5cf6' },
137
+ { id: '4', name: 'Content Creator', icon: <Star className="h-4 w-4" />, color: '#ec4899' },
138
+ { id: '5', name: 'Beta Veteran', icon: <Shield className="h-4 w-4" />, color: '#3b82f6' },
139
+ ]
140
+
141
+ return (
142
+ <div className="p-6 max-w-4xl">
143
+ <PlayerProfile
144
+ name="xXLegendXx"
145
+ title="Grand Champion"
146
+ level={99}
147
+ currentXP={9500}
148
+ maxXP={10000}
149
+ totalXP={2500000}
150
+ rank="Challenger"
151
+ rankIcon={<Crown className="h-5 w-5 text-amber-500" />}
152
+ stats={topStats}
153
+ achievements={legendaryAchievements}
154
+ badges={topBadges}
155
+ matchHistory={defaultMatchHistory}
156
+ streak={142}
157
+ playtime="2,450h"
158
+ friendsCount={523}
159
+ isOnline={true}
160
+ joinDate={new Date('2022-01-01')}
161
+ />
162
+ </div>
163
+ )
164
+ },
165
+ }
166
+
167
+ export const OfflinePlayer: Story = {
168
+ render: () => (
169
+ <div className="p-6 max-w-4xl">
170
+ <PlayerProfile
171
+ name="GhostRider"
172
+ title="Veteran Player"
173
+ level={56}
174
+ currentXP={2300}
175
+ maxXP={8000}
176
+ totalXP={180000}
177
+ rank="Platinum I"
178
+ stats={defaultPlayerStats}
179
+ achievements={defaultAchievements}
180
+ badges={defaultBadges}
181
+ matchHistory={defaultMatchHistory}
182
+ streak={0}
183
+ playtime="320h"
184
+ friendsCount={45}
185
+ isOnline={false}
186
+ joinDate={new Date('2023-03-20')}
187
+ />
188
+ </div>
189
+ ),
190
+ }
191
+
192
+ export const WithAvatar: Story = {
193
+ render: () => (
194
+ <div className="p-6 max-w-4xl">
195
+ <PlayerProfile
196
+ name="AcePlayer"
197
+ avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=AcePlayer"
198
+ title="Rising Star"
199
+ level={28}
200
+ currentXP={4200}
201
+ maxXP={6000}
202
+ totalXP={75000}
203
+ rank="Gold III"
204
+ stats={defaultPlayerStats}
205
+ achievements={defaultAchievements}
206
+ badges={defaultBadges}
207
+ matchHistory={defaultMatchHistory}
208
+ streak={7}
209
+ playtime="120h"
210
+ friendsCount={34}
211
+ isOnline={true}
212
+ joinDate={new Date('2023-09-10')}
213
+ />
214
+ </div>
215
+ ),
216
+ }
217
+
218
+ export const MinimalProfile: Story = {
219
+ render: () => (
220
+ <div className="p-6 max-w-4xl">
221
+ <PlayerProfile
222
+ name="CasualGamer"
223
+ level={15}
224
+ currentXP={1200}
225
+ maxXP={2000}
226
+ stats={[
227
+ { label: 'Matches', value: 45, icon: <Gamepad2 className="h-5 w-5" />, color: 'blue' },
228
+ { label: 'Wins', value: 18, icon: <Trophy className="h-5 w-5" />, color: 'green' },
229
+ ]}
230
+ isOnline={false}
231
+ />
232
+ </div>
233
+ ),
234
+ }
235
+
236
+ export const WinStreak: Story = {
237
+ render: () => {
238
+ const winStreakHistory: MatchHistory[] = [
239
+ { id: '1', result: 'win', score: '5-0', opponent: 'Team Alpha', mode: 'Ranked', date: new Date(Date.now() - 1 * 3600000), xpGained: 250 },
240
+ { id: '2', result: 'win', score: '3-1', opponent: 'Team Beta', mode: 'Ranked', date: new Date(Date.now() - 2 * 3600000), xpGained: 200 },
241
+ { id: '3', result: 'win', score: '4-2', opponent: 'Team Gamma', mode: 'Ranked', date: new Date(Date.now() - 3 * 3600000), xpGained: 225 },
242
+ { id: '4', result: 'win', score: '2-0', opponent: 'Team Delta', mode: 'Ranked', date: new Date(Date.now() - 4 * 3600000), xpGained: 175 },
243
+ { id: '5', result: 'win', score: '3-0', opponent: 'Team Epsilon', mode: 'Ranked', date: new Date(Date.now() - 5 * 3600000), xpGained: 200 },
244
+ ]
245
+
246
+ return (
247
+ <div className="p-6 max-w-4xl">
248
+ <PlayerProfile
249
+ name="OnFirePlayer"
250
+ title="Unstoppable"
251
+ level={67}
252
+ currentXP={8900}
253
+ maxXP={10000}
254
+ totalXP={450000}
255
+ rank="Master"
256
+ rankIcon={<Flame className="h-5 w-5 text-orange-500" />}
257
+ stats={defaultPlayerStats}
258
+ achievements={defaultAchievements}
259
+ badges={[
260
+ { id: '1', name: 'Hot Streak', icon: <Flame className="h-4 w-4" />, color: '#f97316' },
261
+ ...defaultBadges,
262
+ ]}
263
+ matchHistory={winStreakHistory}
264
+ streak={28}
265
+ playtime="500h"
266
+ friendsCount={156}
267
+ isOnline={true}
268
+ joinDate={new Date('2023-01-01')}
269
+ />
270
+ </div>
271
+ )
272
+ },
273
+ }
274
+
275
+ export const GameProfile: Story = {
276
+ render: () => (
277
+ <div className="min-h-screen bg-gradient-to-b from-background to-muted/30">
278
+ <header className="bg-background/80 backdrop-blur border-b px-6 py-4">
279
+ <div className="flex items-center justify-between max-w-5xl mx-auto">
280
+ <div className="flex items-center gap-3">
281
+ <Gamepad2 className="h-6 w-6 text-primary" />
282
+ <h1 className="font-bold text-xl">Battle Arena</h1>
283
+ </div>
284
+ <nav className="flex gap-4 text-sm">
285
+ <a href="#" className="text-foreground font-medium">Profile</a>
286
+ <a href="#" className="text-muted-foreground hover:text-foreground">Leaderboard</a>
287
+ <a href="#" className="text-muted-foreground hover:text-foreground">Store</a>
288
+ <a href="#" className="text-muted-foreground hover:text-foreground">Settings</a>
289
+ </nav>
290
+ </div>
291
+ </header>
292
+ <main className="p-6 max-w-5xl mx-auto">
293
+ <PlayerProfile
294
+ name="DragonSlayer"
295
+ avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=DragonSlayer"
296
+ title="Legendary Warrior"
297
+ level={88}
298
+ currentXP={7200}
299
+ maxXP={10000}
300
+ totalXP={1250000}
301
+ rank="Grandmaster"
302
+ rankIcon={<Crown className="h-5 w-5 text-amber-500" />}
303
+ stats={defaultPlayerStats}
304
+ achievements={defaultAchievements}
305
+ badges={defaultBadges}
306
+ matchHistory={defaultMatchHistory}
307
+ streak={45}
308
+ playtime="1,250h"
309
+ friendsCount={234}
310
+ isOnline={true}
311
+ joinDate={new Date('2022-06-01')}
312
+ />
313
+ </main>
314
+ </div>
315
+ ),
316
+ }