@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.
- package/dist/blocks/dashboard/index.d.ts +4 -1
- package/dist/blocks/empty-states/index.d.ts +4 -1
- package/dist/blocks/error-pages/index.d.ts +4 -1
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/landing/index.d.ts +4 -1
- package/dist/blocks/pricing/index.d.ts +5 -1
- package/dist/blocks/sidebar/index.d.ts +5 -1
- package/dist/index.cjs.js +130 -130
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +7905 -7856
- package/dist/stories/Button.d.ts +14 -0
- package/dist/stories/Button.stories.d.ts +8 -0
- package/dist/stories/Header.d.ts +11 -0
- package/dist/stories/Header.stories.d.ts +6 -0
- package/dist/stories/Page.d.ts +2 -0
- package/dist/stories/Page.stories.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/link.d.ts +23 -0
- package/package.json +11 -3
- package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
- package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
- package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
- package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
- package/src/blocks/chat/Chat.stories.tsx +466 -0
- package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
- package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
- package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
- package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
- package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
- package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
- package/src/blocks/dashboard/index.tsx +68 -27
- package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
- package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
- package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
- package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
- package/src/blocks/empty-states/index.tsx +26 -8
- package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
- package/src/blocks/error-pages/index.tsx +26 -8
- package/src/blocks/faq/FAQ.stories.tsx +416 -0
- package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
- package/src/blocks/footer/Footer.stories.tsx +328 -0
- package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
- package/src/blocks/header/WakaHeader.stories.tsx +455 -0
- package/src/blocks/headtab/Headtab.stories.tsx +369 -0
- package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
- package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
- package/src/blocks/index.ts +1 -1
- package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
- package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
- package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
- package/src/blocks/landing/index.tsx +125 -66
- package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
- package/src/blocks/layout/Layout.stories.tsx +373 -0
- package/src/blocks/login/Login.stories.tsx +443 -0
- package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
- package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
- package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
- package/src/blocks/pricing/index.tsx +38 -4
- package/src/blocks/profile/Profile.stories.tsx +371 -0
- package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
- package/src/blocks/settings/Settings.stories.tsx +520 -0
- package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
- package/src/blocks/sidebar/index.tsx +49 -20
- package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
- package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
- package/src/blocks/wizard/Wizard.stories.tsx +683 -0
- package/src/components/accordion/Accordion.stories.tsx +93 -0
- package/src/components/alert/Alert.stories.tsx +95 -0
- package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
- package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
- package/src/components/avatar/Avatar.stories.tsx +104 -0
- package/src/components/button/Button.stories.tsx +12 -1
- package/src/components/calendar/Calendar.stories.tsx +102 -0
- package/src/components/card/Card.stories.tsx +125 -0
- package/src/components/checkbox/Checkbox.stories.tsx +100 -0
- package/src/components/code/Code.stories.tsx +402 -0
- package/src/components/collapsible/Collapsible.stories.tsx +123 -0
- package/src/components/command/Command.stories.tsx +207 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
- package/src/components/dialog/Dialog.stories.tsx +157 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
- package/src/components/form/Form.stories.tsx +413 -0
- package/src/components/hover-card/HoverCard.stories.tsx +148 -0
- package/src/components/input-otp/InputOTP.stories.tsx +255 -0
- package/src/components/label/Label.stories.tsx +68 -0
- package/src/components/menubar/Menubar.stories.tsx +278 -0
- package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
- package/src/components/popover/Popover.stories.tsx +199 -0
- package/src/components/progress/Progress.stories.tsx +104 -0
- package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
- package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
- package/src/components/select/Select.stories.tsx +146 -0
- package/src/components/separator/Separator.stories.tsx +117 -0
- package/src/components/sheet/Sheet.stories.tsx +195 -0
- package/src/components/skeleton/Skeleton.stories.tsx +114 -0
- package/src/components/slider/Slider.stories.tsx +157 -0
- package/src/components/switch/Switch.stories.tsx +114 -0
- package/src/components/table/Table.stories.tsx +153 -0
- package/src/components/tabs/Tabs.stories.tsx +155 -0
- package/src/components/textarea/Textarea.stories.tsx +116 -0
- package/src/components/toast/Toast.stories.tsx +160 -0
- package/src/components/toggle/Toggle.stories.tsx +125 -0
- package/src/components/tooltip/Tooltip.stories.tsx +133 -0
- package/src/components/typography/Typography.stories.tsx +207 -0
- package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
- package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
- package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
- package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
- package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
- package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
- package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
- package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
- package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
- package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
- package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
- package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
- package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
- package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
- package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
- package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
- package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
- package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
- package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
- package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
- package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
- package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
- package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
- package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
- package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
- package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
- package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
- package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
- package/src/components/waka-image/WakaImage.stories.tsx +255 -0
- package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
- package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
- package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
- package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
- package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
- package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
- package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
- package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
- package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
- package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
- package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
- package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
- package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
- package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
- package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
- package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
- package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
- package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
- package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
- package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
- package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
- package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
- package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
- package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
- package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
- package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
- package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
- package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
- package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
- package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
- package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
- package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
- package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
- package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
- package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
- package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
- package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
- package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
- package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
- package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
- package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
- package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
- package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
- package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
- package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
- package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
- package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
- package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
- package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
- package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import {
|
|
3
|
+
Auth2FA,
|
|
4
|
+
defaultBackupCodes,
|
|
5
|
+
defaultStatus,
|
|
6
|
+
} from './index'
|
|
7
|
+
import type { TwoFactorStatus, TwoFactorMethod, BackupCode } from './index'
|
|
8
|
+
import * as React from 'react'
|
|
9
|
+
|
|
10
|
+
const meta: Meta<typeof Auth2FA> = {
|
|
11
|
+
title: 'Blocks/Auth2FA',
|
|
12
|
+
component: Auth2FA,
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
'A two-factor authentication setup wizard supporting authenticator apps, SMS, email, and hardware keys with backup codes management and security tips.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
tags: ['autodocs'],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default meta
|
|
26
|
+
type Story = StoryObj<typeof Auth2FA>
|
|
27
|
+
|
|
28
|
+
export const Default: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<div className="p-6 max-w-2xl">
|
|
31
|
+
<Auth2FA
|
|
32
|
+
status={{ enabled: false }}
|
|
33
|
+
availableMethods={['authenticator', 'sms', 'email']}
|
|
34
|
+
qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
|
35
|
+
secretKey="JBSWY3DPEHPK3PXP"
|
|
36
|
+
onEnable={async (method, code) => {
|
|
37
|
+
console.log('Enable 2FA:', method, code)
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
39
|
+
return code === '123456'
|
|
40
|
+
}}
|
|
41
|
+
onSendCode={async (method) => {
|
|
42
|
+
console.log('Send code via:', method)
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
44
|
+
return true
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const Enabled: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div className="p-6 max-w-2xl">
|
|
54
|
+
<Auth2FA
|
|
55
|
+
status={defaultStatus}
|
|
56
|
+
backupCodes={defaultBackupCodes}
|
|
57
|
+
onDisable={async (code) => {
|
|
58
|
+
console.log('Disable 2FA with code:', code)
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
60
|
+
return code === '123456'
|
|
61
|
+
}}
|
|
62
|
+
onRegenerateBackupCodes={async () => {
|
|
63
|
+
console.log('Regenerate backup codes')
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
65
|
+
return [
|
|
66
|
+
{ code: 'NEW1-1234', used: false },
|
|
67
|
+
{ code: 'NEW2-5678', used: false },
|
|
68
|
+
{ code: 'NEW3-9012', used: false },
|
|
69
|
+
{ code: 'NEW4-3456', used: false },
|
|
70
|
+
{ code: 'NEW5-7890', used: false },
|
|
71
|
+
{ code: 'NEW6-1234', used: false },
|
|
72
|
+
{ code: 'NEW7-5678', used: false },
|
|
73
|
+
{ code: 'NEW8-9012', used: false },
|
|
74
|
+
]
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const EnabledWithSMS: Story = {
|
|
82
|
+
render: () => {
|
|
83
|
+
const smsStatus: TwoFactorStatus = {
|
|
84
|
+
enabled: true,
|
|
85
|
+
method: 'sms',
|
|
86
|
+
phone: '+1 (555) ***-**89',
|
|
87
|
+
lastVerified: new Date(),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div className="p-6 max-w-2xl">
|
|
92
|
+
<Auth2FA
|
|
93
|
+
status={smsStatus}
|
|
94
|
+
backupCodes={defaultBackupCodes}
|
|
95
|
+
onDisable={async (code) => {
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
97
|
+
return code === '123456'
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const EnabledWithEmail: Story = {
|
|
106
|
+
render: () => {
|
|
107
|
+
const emailStatus: TwoFactorStatus = {
|
|
108
|
+
enabled: true,
|
|
109
|
+
method: 'email',
|
|
110
|
+
email: 'u***@example.com',
|
|
111
|
+
lastVerified: new Date(),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div className="p-6 max-w-2xl">
|
|
116
|
+
<Auth2FA
|
|
117
|
+
status={emailStatus}
|
|
118
|
+
backupCodes={defaultBackupCodes}
|
|
119
|
+
onDisable={async (code) => {
|
|
120
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
121
|
+
return code === '123456'
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const AllMethods: Story = {
|
|
130
|
+
render: () => (
|
|
131
|
+
<div className="p-6 max-w-2xl">
|
|
132
|
+
<Auth2FA
|
|
133
|
+
status={{ enabled: false }}
|
|
134
|
+
availableMethods={['authenticator', 'sms', 'email', 'hardware']}
|
|
135
|
+
qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
|
136
|
+
secretKey="JBSWY3DPEHPK3PXP"
|
|
137
|
+
onEnable={async (method, code) => {
|
|
138
|
+
console.log('Enable 2FA:', method, code)
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
140
|
+
return true
|
|
141
|
+
}}
|
|
142
|
+
onSendCode={async (method) => {
|
|
143
|
+
console.log('Send code via:', method)
|
|
144
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
145
|
+
return true
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const AuthenticatorOnly: Story = {
|
|
153
|
+
render: () => (
|
|
154
|
+
<div className="p-6 max-w-2xl">
|
|
155
|
+
<Auth2FA
|
|
156
|
+
status={{ enabled: false }}
|
|
157
|
+
availableMethods={['authenticator']}
|
|
158
|
+
qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/SecureApp:admin@secureapp.com?secret=ABCDEFGHIJKLMNOP&issuer=SecureApp"
|
|
159
|
+
secretKey="ABCDEFGHIJKLMNOP"
|
|
160
|
+
onEnable={async (method, code) => {
|
|
161
|
+
console.log('Enable authenticator:', code)
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
163
|
+
return code.length === 6
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const LowBackupCodes: Story = {
|
|
171
|
+
render: () => {
|
|
172
|
+
const lowCodes: BackupCode[] = [
|
|
173
|
+
{ code: 'LAST-1234', used: false },
|
|
174
|
+
{ code: 'LAST-5678', used: false },
|
|
175
|
+
{ code: 'USED-0001', used: true },
|
|
176
|
+
{ code: 'USED-0002', used: true },
|
|
177
|
+
{ code: 'USED-0003', used: true },
|
|
178
|
+
{ code: 'USED-0004', used: true },
|
|
179
|
+
{ code: 'USED-0005', used: true },
|
|
180
|
+
{ code: 'USED-0006', used: true },
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className="p-6 max-w-2xl">
|
|
185
|
+
<Auth2FA
|
|
186
|
+
status={defaultStatus}
|
|
187
|
+
backupCodes={lowCodes}
|
|
188
|
+
onRegenerateBackupCodes={async () => {
|
|
189
|
+
console.log('Regenerate backup codes')
|
|
190
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
191
|
+
return [
|
|
192
|
+
{ code: 'FRESH-1111', used: false },
|
|
193
|
+
{ code: 'FRESH-2222', used: false },
|
|
194
|
+
{ code: 'FRESH-3333', used: false },
|
|
195
|
+
{ code: 'FRESH-4444', used: false },
|
|
196
|
+
{ code: 'FRESH-5555', used: false },
|
|
197
|
+
{ code: 'FRESH-6666', used: false },
|
|
198
|
+
{ code: 'FRESH-7777', used: false },
|
|
199
|
+
{ code: 'FRESH-8888', used: false },
|
|
200
|
+
]
|
|
201
|
+
}}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export const Interactive: Story = {
|
|
209
|
+
render: () => {
|
|
210
|
+
const [status, setStatus] = React.useState<TwoFactorStatus>({ enabled: false })
|
|
211
|
+
const [backupCodes, setBackupCodes] = React.useState<BackupCode[]>([])
|
|
212
|
+
|
|
213
|
+
const handleEnable = async (method: TwoFactorMethod, code: string): Promise<boolean> => {
|
|
214
|
+
await new Promise((resolve) => setTimeout(resolve, 1500))
|
|
215
|
+
if (code === '123456') {
|
|
216
|
+
setStatus({ enabled: true, method, lastVerified: new Date() })
|
|
217
|
+
setBackupCodes([
|
|
218
|
+
{ code: 'DEMO-1111', used: false },
|
|
219
|
+
{ code: 'DEMO-2222', used: false },
|
|
220
|
+
{ code: 'DEMO-3333', used: false },
|
|
221
|
+
{ code: 'DEMO-4444', used: false },
|
|
222
|
+
{ code: 'DEMO-5555', used: false },
|
|
223
|
+
{ code: 'DEMO-6666', used: false },
|
|
224
|
+
{ code: 'DEMO-7777', used: false },
|
|
225
|
+
{ code: 'DEMO-8888', used: false },
|
|
226
|
+
])
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const handleDisable = async (code: string): Promise<boolean> => {
|
|
233
|
+
await new Promise((resolve) => setTimeout(resolve, 1500))
|
|
234
|
+
if (code === '123456') {
|
|
235
|
+
setStatus({ enabled: false })
|
|
236
|
+
setBackupCodes([])
|
|
237
|
+
return true
|
|
238
|
+
}
|
|
239
|
+
return false
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div className="p-6 max-w-2xl">
|
|
244
|
+
<div className="mb-4 p-4 bg-muted rounded-lg text-sm">
|
|
245
|
+
<p className="font-medium">Test Instructions:</p>
|
|
246
|
+
<p className="text-muted-foreground mt-1">
|
|
247
|
+
Use code <code className="bg-background px-1 rounded">123456</code> to enable/disable 2FA
|
|
248
|
+
</p>
|
|
249
|
+
</div>
|
|
250
|
+
<Auth2FA
|
|
251
|
+
status={status}
|
|
252
|
+
availableMethods={['authenticator', 'sms', 'email']}
|
|
253
|
+
qrCodeUrl="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=otpauth://totp/Demo:user@demo.com?secret=DEMOSECRETKEY123&issuer=Demo"
|
|
254
|
+
secretKey="DEMOSECRETKEY123"
|
|
255
|
+
backupCodes={backupCodes}
|
|
256
|
+
onEnable={handleEnable}
|
|
257
|
+
onDisable={handleDisable}
|
|
258
|
+
onSendCode={async (method) => {
|
|
259
|
+
alert(`Code sent via ${method}! (Demo: use 123456)`)
|
|
260
|
+
return true
|
|
261
|
+
}}
|
|
262
|
+
onRegenerateBackupCodes={async () => {
|
|
263
|
+
const newCodes = Array.from({ length: 8 }, (_, i) => ({
|
|
264
|
+
code: `REGEN-${1000 + i}`,
|
|
265
|
+
used: false,
|
|
266
|
+
}))
|
|
267
|
+
setBackupCodes(newCodes)
|
|
268
|
+
return newCodes
|
|
269
|
+
}}
|
|
270
|
+
/>
|
|
271
|
+
</div>
|
|
272
|
+
)
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export const SecuritySettings: Story = {
|
|
277
|
+
render: () => (
|
|
278
|
+
<div className="min-h-screen bg-muted/30">
|
|
279
|
+
<header className="bg-background border-b px-6 py-4">
|
|
280
|
+
<div className="flex items-center justify-between max-w-4xl mx-auto">
|
|
281
|
+
<div>
|
|
282
|
+
<h1 className="font-bold text-xl">Security Settings</h1>
|
|
283
|
+
<p className="text-sm text-muted-foreground">Manage your account security</p>
|
|
284
|
+
</div>
|
|
285
|
+
<nav className="flex gap-4 text-sm">
|
|
286
|
+
<a href="#" className="text-muted-foreground hover:text-foreground">Profile</a>
|
|
287
|
+
<a href="#" className="text-foreground font-medium">Security</a>
|
|
288
|
+
<a href="#" className="text-muted-foreground hover:text-foreground">Notifications</a>
|
|
289
|
+
</nav>
|
|
290
|
+
</div>
|
|
291
|
+
</header>
|
|
292
|
+
<main className="p-6 max-w-4xl mx-auto">
|
|
293
|
+
<Auth2FA
|
|
294
|
+
status={defaultStatus}
|
|
295
|
+
backupCodes={defaultBackupCodes}
|
|
296
|
+
onDisable={async (code) => {
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
298
|
+
return code === '123456'
|
|
299
|
+
}}
|
|
300
|
+
onRegenerateBackupCodes={async () => {
|
|
301
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
302
|
+
return defaultBackupCodes.map((c, i) => ({ ...c, code: `NEW-${i + 1000}`, used: false }))
|
|
303
|
+
}}
|
|
304
|
+
/>
|
|
305
|
+
</main>
|
|
306
|
+
</div>
|
|
307
|
+
),
|
|
308
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaCalendarView, defaultCalendarEvents } from './index'
|
|
3
|
+
import type { CalendarEvent, CalendarViewMode } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof WakaCalendarView> = {
|
|
7
|
+
title: 'Blocks/CalendarView',
|
|
8
|
+
component: WakaCalendarView,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'fullscreen',
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'A calendar component with month, week, day, and agenda views. Supports events with colors, locations, attendees, and recurrence.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
tags: ['autodocs'],
|
|
19
|
+
argTypes: {
|
|
20
|
+
viewMode: {
|
|
21
|
+
control: 'select',
|
|
22
|
+
options: ['month', 'week', 'day', 'agenda'],
|
|
23
|
+
},
|
|
24
|
+
showHeader: {
|
|
25
|
+
control: 'boolean',
|
|
26
|
+
},
|
|
27
|
+
showViewSelector: {
|
|
28
|
+
control: 'boolean',
|
|
29
|
+
},
|
|
30
|
+
showAddButton: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
},
|
|
33
|
+
weekStartsOn: {
|
|
34
|
+
control: 'select',
|
|
35
|
+
options: [0, 1],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default meta
|
|
41
|
+
type Story = StoryObj<typeof WakaCalendarView>
|
|
42
|
+
|
|
43
|
+
export const Default: Story = {
|
|
44
|
+
render: () => (
|
|
45
|
+
<div className="p-6">
|
|
46
|
+
<WakaCalendarView
|
|
47
|
+
events={defaultCalendarEvents}
|
|
48
|
+
onEventClick={(event) => console.log('Event clicked:', event)}
|
|
49
|
+
onDayClick={(date) => console.log('Day clicked:', date)}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const MonthView: Story = {
|
|
56
|
+
render: () => (
|
|
57
|
+
<div className="p-6">
|
|
58
|
+
<WakaCalendarView
|
|
59
|
+
events={defaultCalendarEvents}
|
|
60
|
+
viewMode="month"
|
|
61
|
+
onViewModeChange={(mode) => console.log('View mode:', mode)}
|
|
62
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const AgendaView: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<div className="p-6">
|
|
71
|
+
<WakaCalendarView
|
|
72
|
+
events={defaultCalendarEvents}
|
|
73
|
+
viewMode="agenda"
|
|
74
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const ManyEvents: Story = {
|
|
81
|
+
render: () => {
|
|
82
|
+
const now = new Date()
|
|
83
|
+
const manyEvents: CalendarEvent[] = [
|
|
84
|
+
// Today's events
|
|
85
|
+
{
|
|
86
|
+
id: '1',
|
|
87
|
+
title: 'Standup',
|
|
88
|
+
start: new Date(now.setHours(9, 0, 0, 0)),
|
|
89
|
+
end: new Date(now.setHours(9, 30, 0, 0)),
|
|
90
|
+
color: 'blue',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: '2',
|
|
94
|
+
title: 'Design Review',
|
|
95
|
+
start: new Date(now.setHours(10, 0, 0, 0)),
|
|
96
|
+
end: new Date(now.setHours(11, 0, 0, 0)),
|
|
97
|
+
color: 'purple',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: '3',
|
|
101
|
+
title: 'Lunch',
|
|
102
|
+
start: new Date(now.setHours(12, 0, 0, 0)),
|
|
103
|
+
end: new Date(now.setHours(13, 0, 0, 0)),
|
|
104
|
+
color: 'green',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: '4',
|
|
108
|
+
title: 'Sprint Planning',
|
|
109
|
+
start: new Date(now.setHours(14, 0, 0, 0)),
|
|
110
|
+
end: new Date(now.setHours(15, 30, 0, 0)),
|
|
111
|
+
color: 'orange',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: '5',
|
|
115
|
+
title: '1:1 with Manager',
|
|
116
|
+
start: new Date(now.setHours(16, 0, 0, 0)),
|
|
117
|
+
end: new Date(now.setHours(16, 30, 0, 0)),
|
|
118
|
+
color: 'cyan',
|
|
119
|
+
},
|
|
120
|
+
// Tomorrow
|
|
121
|
+
{
|
|
122
|
+
id: '6',
|
|
123
|
+
title: 'Team Workshop',
|
|
124
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24),
|
|
125
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 + 1000 * 60 * 60 * 4),
|
|
126
|
+
color: 'red',
|
|
127
|
+
allDay: true,
|
|
128
|
+
},
|
|
129
|
+
// More events
|
|
130
|
+
...Array.from({ length: 10 }, (_, i) => ({
|
|
131
|
+
id: `event-${i + 10}`,
|
|
132
|
+
title: `Meeting ${i + 1}`,
|
|
133
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24 * (i + 2)),
|
|
134
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * (i + 2) + 1000 * 60 * 60),
|
|
135
|
+
color: (['blue', 'green', 'purple', 'orange', 'cyan', 'pink', 'yellow', 'red'] as const)[i % 8],
|
|
136
|
+
})),
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div className="p-6">
|
|
141
|
+
<WakaCalendarView
|
|
142
|
+
events={manyEvents}
|
|
143
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const WithLocations: Story = {
|
|
151
|
+
render: () => {
|
|
152
|
+
const eventsWithLocations: CalendarEvent[] = [
|
|
153
|
+
{
|
|
154
|
+
id: '1',
|
|
155
|
+
title: 'Team Meeting',
|
|
156
|
+
start: new Date(),
|
|
157
|
+
end: new Date(Date.now() + 1000 * 60 * 60),
|
|
158
|
+
color: 'blue',
|
|
159
|
+
location: 'Conference Room A',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: '2',
|
|
163
|
+
title: 'Client Call',
|
|
164
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 2),
|
|
165
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 3),
|
|
166
|
+
color: 'green',
|
|
167
|
+
location: 'Zoom Meeting',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: '3',
|
|
171
|
+
title: 'Lunch Meeting',
|
|
172
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24),
|
|
173
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 25),
|
|
174
|
+
color: 'orange',
|
|
175
|
+
location: 'Restaurant Le Gourmet, 123 Main St',
|
|
176
|
+
},
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div className="p-6">
|
|
181
|
+
<WakaCalendarView
|
|
182
|
+
events={eventsWithLocations}
|
|
183
|
+
viewMode="agenda"
|
|
184
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
)
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export const AllDayEvents: Story = {
|
|
192
|
+
render: () => {
|
|
193
|
+
const allDayEvents: CalendarEvent[] = [
|
|
194
|
+
{
|
|
195
|
+
id: '1',
|
|
196
|
+
title: 'Company Holiday',
|
|
197
|
+
start: new Date(),
|
|
198
|
+
end: new Date(),
|
|
199
|
+
allDay: true,
|
|
200
|
+
color: 'red',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: '2',
|
|
204
|
+
title: 'Conference Week',
|
|
205
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3),
|
|
206
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
|
|
207
|
+
allDay: true,
|
|
208
|
+
color: 'purple',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: '3',
|
|
212
|
+
title: 'Team Offsite',
|
|
213
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 10),
|
|
214
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 12),
|
|
215
|
+
allDay: true,
|
|
216
|
+
color: 'green',
|
|
217
|
+
},
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div className="p-6">
|
|
222
|
+
<WakaCalendarView
|
|
223
|
+
events={allDayEvents}
|
|
224
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
227
|
+
)
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const WeekStartsSunday: Story = {
|
|
232
|
+
render: () => (
|
|
233
|
+
<div className="p-6">
|
|
234
|
+
<WakaCalendarView
|
|
235
|
+
events={defaultCalendarEvents}
|
|
236
|
+
weekStartsOn={0}
|
|
237
|
+
locale="en-US"
|
|
238
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export const NoHeader: Story = {
|
|
245
|
+
render: () => (
|
|
246
|
+
<div className="p-6">
|
|
247
|
+
<WakaCalendarView
|
|
248
|
+
events={defaultCalendarEvents}
|
|
249
|
+
showHeader={false}
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
),
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const ReadOnly: Story = {
|
|
256
|
+
render: () => (
|
|
257
|
+
<div className="p-6">
|
|
258
|
+
<WakaCalendarView
|
|
259
|
+
events={defaultCalendarEvents}
|
|
260
|
+
showAddButton={false}
|
|
261
|
+
onEventClick={(event) => console.log('View event:', event)}
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
),
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const Interactive: Story = {
|
|
268
|
+
render: () => {
|
|
269
|
+
const [events, setEvents] = React.useState<CalendarEvent[]>(defaultCalendarEvents)
|
|
270
|
+
const [viewMode, setViewMode] = React.useState<CalendarViewMode>('month')
|
|
271
|
+
const [currentDate, setCurrentDate] = React.useState(new Date())
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<div className="p-6">
|
|
275
|
+
<div className="mb-4 text-sm text-muted-foreground">
|
|
276
|
+
Current view: {viewMode} | Date: {currentDate.toLocaleDateString()}
|
|
277
|
+
</div>
|
|
278
|
+
<WakaCalendarView
|
|
279
|
+
events={events}
|
|
280
|
+
currentDate={currentDate}
|
|
281
|
+
onDateChange={setCurrentDate}
|
|
282
|
+
viewMode={viewMode}
|
|
283
|
+
onViewModeChange={setViewMode}
|
|
284
|
+
onEventClick={(event) => {
|
|
285
|
+
console.log('Event clicked:', event)
|
|
286
|
+
alert(`Event: ${event.title}`)
|
|
287
|
+
}}
|
|
288
|
+
onDayClick={(date) => {
|
|
289
|
+
console.log('Day clicked:', date)
|
|
290
|
+
}}
|
|
291
|
+
onEventCreate={(event) => {
|
|
292
|
+
console.log('Create event:', event)
|
|
293
|
+
}}
|
|
294
|
+
/>
|
|
295
|
+
</div>
|
|
296
|
+
)
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const ColorfulEvents: Story = {
|
|
301
|
+
render: () => {
|
|
302
|
+
const colorfulEvents: CalendarEvent[] = [
|
|
303
|
+
{ id: '1', title: 'Blue Event', start: new Date(), end: new Date(Date.now() + 1000 * 60 * 60), color: 'blue' },
|
|
304
|
+
{ id: '2', title: 'Green Event', start: new Date(Date.now() + 1000 * 60 * 60 * 3), end: new Date(Date.now() + 1000 * 60 * 60 * 4), color: 'green' },
|
|
305
|
+
{ id: '3', title: 'Red Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24), end: new Date(Date.now() + 1000 * 60 * 60 * 25), color: 'red' },
|
|
306
|
+
{ id: '4', title: 'Yellow Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 60 * 60), color: 'yellow' },
|
|
307
|
+
{ id: '5', title: 'Purple Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 2), color: 'purple' },
|
|
308
|
+
{ id: '6', title: 'Pink Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60), color: 'pink' },
|
|
309
|
+
{ id: '7', title: 'Orange Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 5), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 5 + 1000 * 60 * 60 * 3), color: 'orange' },
|
|
310
|
+
{ id: '8', title: 'Cyan Event', start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 6), end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 6 + 1000 * 60 * 60), color: 'cyan' },
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<div className="p-6">
|
|
315
|
+
<WakaCalendarView
|
|
316
|
+
events={colorfulEvents}
|
|
317
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
)
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export const EmptyCalendar: Story = {
|
|
325
|
+
render: () => (
|
|
326
|
+
<div className="p-6">
|
|
327
|
+
<WakaCalendarView
|
|
328
|
+
events={[]}
|
|
329
|
+
viewMode="agenda"
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
),
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const TeamCalendar: Story = {
|
|
336
|
+
render: () => {
|
|
337
|
+
const teamEvents: CalendarEvent[] = [
|
|
338
|
+
{
|
|
339
|
+
id: '1',
|
|
340
|
+
title: 'Daily Standup',
|
|
341
|
+
description: 'Team sync meeting',
|
|
342
|
+
start: new Date(new Date().setHours(9, 0, 0, 0)),
|
|
343
|
+
end: new Date(new Date().setHours(9, 15, 0, 0)),
|
|
344
|
+
color: 'blue',
|
|
345
|
+
location: 'Zoom',
|
|
346
|
+
attendees: [
|
|
347
|
+
{ id: '1', name: 'Alice' },
|
|
348
|
+
{ id: '2', name: 'Bob' },
|
|
349
|
+
{ id: '3', name: 'Carol' },
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: '2',
|
|
354
|
+
title: 'Sprint Review',
|
|
355
|
+
description: 'Demo completed work',
|
|
356
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4),
|
|
357
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60),
|
|
358
|
+
color: 'green',
|
|
359
|
+
location: 'Conference Room B',
|
|
360
|
+
attendees: [
|
|
361
|
+
{ id: '1', name: 'Alice' },
|
|
362
|
+
{ id: '2', name: 'Bob' },
|
|
363
|
+
{ id: '3', name: 'Carol' },
|
|
364
|
+
{ id: '4', name: 'David' },
|
|
365
|
+
{ id: '5', name: 'Eve' },
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: '3',
|
|
370
|
+
title: 'Retrospective',
|
|
371
|
+
start: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60 * 2),
|
|
372
|
+
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 4 + 1000 * 60 * 60 * 3),
|
|
373
|
+
color: 'purple',
|
|
374
|
+
location: 'Conference Room B',
|
|
375
|
+
},
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
return (
|
|
379
|
+
<div className="min-h-screen bg-muted/30">
|
|
380
|
+
<header className="bg-background border-b px-6 py-4">
|
|
381
|
+
<div className="flex items-center justify-between">
|
|
382
|
+
<div>
|
|
383
|
+
<h1 className="font-bold text-xl">Team Calendar</h1>
|
|
384
|
+
<p className="text-sm text-muted-foreground">Engineering Team</p>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
</header>
|
|
388
|
+
<main className="p-6">
|
|
389
|
+
<WakaCalendarView
|
|
390
|
+
events={teamEvents}
|
|
391
|
+
onEventClick={(event) => console.log('Event:', event)}
|
|
392
|
+
onEventCreate={(event) => console.log('Create:', event)}
|
|
393
|
+
/>
|
|
394
|
+
</main>
|
|
395
|
+
</div>
|
|
396
|
+
)
|
|
397
|
+
},
|
|
398
|
+
}
|