@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,388 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaScratchCard, useScratchCard } from './index'
|
|
3
|
+
import type { Prize, PrizeRarity } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { Gift, Star, Crown, Gem, Coins, Zap, Trophy } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
const samplePrize: Prize = {
|
|
8
|
+
id: '1',
|
|
9
|
+
name: 'Grand Prize',
|
|
10
|
+
description: 'You won the jackpot!',
|
|
11
|
+
rarity: 'legendary',
|
|
12
|
+
icon: <Crown className="h-12 w-12" />,
|
|
13
|
+
value: '$1,000',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const prizesByRarity: Record<PrizeRarity, Prize> = {
|
|
17
|
+
common: {
|
|
18
|
+
id: 'common',
|
|
19
|
+
name: 'Participation Reward',
|
|
20
|
+
description: 'Thanks for playing!',
|
|
21
|
+
rarity: 'common',
|
|
22
|
+
icon: <Star className="h-10 w-10" />,
|
|
23
|
+
value: '10 Points',
|
|
24
|
+
},
|
|
25
|
+
rare: {
|
|
26
|
+
id: 'rare',
|
|
27
|
+
name: 'Silver Prize',
|
|
28
|
+
description: 'Nice scratch!',
|
|
29
|
+
rarity: 'rare',
|
|
30
|
+
icon: <Coins className="h-10 w-10" />,
|
|
31
|
+
value: '50 Coins',
|
|
32
|
+
},
|
|
33
|
+
epic: {
|
|
34
|
+
id: 'epic',
|
|
35
|
+
name: 'Gold Prize',
|
|
36
|
+
description: 'Amazing luck!',
|
|
37
|
+
rarity: 'epic',
|
|
38
|
+
icon: <Gem className="h-10 w-10" />,
|
|
39
|
+
value: '200 Gems',
|
|
40
|
+
},
|
|
41
|
+
legendary: samplePrize,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const meta: Meta<typeof WakaScratchCard> = {
|
|
45
|
+
title: 'Components/Gamification/WakaScratchCard',
|
|
46
|
+
component: WakaScratchCard,
|
|
47
|
+
parameters: {
|
|
48
|
+
layout: 'centered',
|
|
49
|
+
docs: {
|
|
50
|
+
description: {
|
|
51
|
+
component: 'An interactive scratch card with canvas-based scratching, reveal effects, confetti celebration, and customizable overlay patterns.',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
tags: ['autodocs'],
|
|
56
|
+
argTypes: {
|
|
57
|
+
width: {
|
|
58
|
+
control: { type: 'number', min: 200, max: 500, step: 50 },
|
|
59
|
+
description: 'Card width in pixels',
|
|
60
|
+
},
|
|
61
|
+
height: {
|
|
62
|
+
control: { type: 'number', min: 150, max: 400, step: 50 },
|
|
63
|
+
description: 'Card height in pixels',
|
|
64
|
+
},
|
|
65
|
+
revealThreshold: {
|
|
66
|
+
control: { type: 'range', min: 0.3, max: 0.9, step: 0.1 },
|
|
67
|
+
description: 'Percentage of scratching needed to reveal (0-1)',
|
|
68
|
+
},
|
|
69
|
+
brushSize: {
|
|
70
|
+
control: { type: 'number', min: 20, max: 80, step: 10 },
|
|
71
|
+
description: 'Scratch brush size',
|
|
72
|
+
},
|
|
73
|
+
overlayPattern: {
|
|
74
|
+
control: 'select',
|
|
75
|
+
options: ['solid', 'stripes', 'dots', 'gradient'],
|
|
76
|
+
description: 'Overlay pattern style',
|
|
77
|
+
},
|
|
78
|
+
showProgress: {
|
|
79
|
+
control: 'boolean',
|
|
80
|
+
description: 'Show scratch progress indicator',
|
|
81
|
+
},
|
|
82
|
+
disabled: {
|
|
83
|
+
control: 'boolean',
|
|
84
|
+
description: 'Disable scratching',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default meta
|
|
90
|
+
type Story = StoryObj<typeof WakaScratchCard>
|
|
91
|
+
|
|
92
|
+
export const Default: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
prize: samplePrize,
|
|
95
|
+
width: 300,
|
|
96
|
+
height: 200,
|
|
97
|
+
revealThreshold: 0.5,
|
|
98
|
+
brushSize: 40,
|
|
99
|
+
overlayPattern: 'solid',
|
|
100
|
+
},
|
|
101
|
+
render: (args) => <WakaScratchCard {...args} />,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const Rarities: Story = {
|
|
105
|
+
render: () => (
|
|
106
|
+
<div className="grid grid-cols-2 gap-6">
|
|
107
|
+
{(['common', 'rare', 'epic', 'legendary'] as PrizeRarity[]).map((rarity) => (
|
|
108
|
+
<div key={rarity} className="text-center">
|
|
109
|
+
<p className="text-sm text-muted-foreground mb-2 capitalize">{rarity}</p>
|
|
110
|
+
<WakaScratchCard prize={prizesByRarity[rarity]} width={250} height={180} />
|
|
111
|
+
</div>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const OverlayPatterns: Story = {
|
|
118
|
+
render: () => (
|
|
119
|
+
<div className="grid grid-cols-2 gap-6">
|
|
120
|
+
<div className="text-center">
|
|
121
|
+
<p className="text-sm text-muted-foreground mb-2">Gradient (Default)</p>
|
|
122
|
+
<WakaScratchCard
|
|
123
|
+
prize={prizesByRarity.epic}
|
|
124
|
+
overlayPattern="gradient"
|
|
125
|
+
width={250}
|
|
126
|
+
height={180}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="text-center">
|
|
130
|
+
<p className="text-sm text-muted-foreground mb-2">Solid</p>
|
|
131
|
+
<WakaScratchCard
|
|
132
|
+
prize={prizesByRarity.rare}
|
|
133
|
+
overlayPattern="solid"
|
|
134
|
+
overlayColor="#4f46e5"
|
|
135
|
+
width={250}
|
|
136
|
+
height={180}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<div className="text-center">
|
|
140
|
+
<p className="text-sm text-muted-foreground mb-2">Stripes</p>
|
|
141
|
+
<WakaScratchCard
|
|
142
|
+
prize={prizesByRarity.common}
|
|
143
|
+
overlayPattern="stripes"
|
|
144
|
+
overlayColor="#22c55e"
|
|
145
|
+
width={250}
|
|
146
|
+
height={180}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="text-center">
|
|
150
|
+
<p className="text-sm text-muted-foreground mb-2">Dots</p>
|
|
151
|
+
<WakaScratchCard
|
|
152
|
+
prize={prizesByRarity.legendary}
|
|
153
|
+
overlayPattern="dots"
|
|
154
|
+
overlayColor="#f59e0b"
|
|
155
|
+
width={250}
|
|
156
|
+
height={180}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const Sizes: Story = {
|
|
164
|
+
render: () => (
|
|
165
|
+
<div className="flex flex-wrap gap-6 items-start">
|
|
166
|
+
<div className="text-center">
|
|
167
|
+
<p className="text-sm text-muted-foreground mb-2">Small</p>
|
|
168
|
+
<WakaScratchCard prize={prizesByRarity.common} width={200} height={150} />
|
|
169
|
+
</div>
|
|
170
|
+
<div className="text-center">
|
|
171
|
+
<p className="text-sm text-muted-foreground mb-2">Medium (Default)</p>
|
|
172
|
+
<WakaScratchCard prize={prizesByRarity.rare} width={300} height={200} />
|
|
173
|
+
</div>
|
|
174
|
+
<div className="text-center">
|
|
175
|
+
<p className="text-sm text-muted-foreground mb-2">Large</p>
|
|
176
|
+
<WakaScratchCard prize={prizesByRarity.epic} width={400} height={280} />
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const WithProgress: Story = {
|
|
183
|
+
render: () => (
|
|
184
|
+
<div className="space-y-4">
|
|
185
|
+
<p className="text-sm text-muted-foreground text-center">
|
|
186
|
+
Scratch to see the progress indicator
|
|
187
|
+
</p>
|
|
188
|
+
<WakaScratchCard prize={samplePrize} showProgress />
|
|
189
|
+
</div>
|
|
190
|
+
),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const CustomRevealThreshold: Story = {
|
|
194
|
+
render: () => (
|
|
195
|
+
<div className="flex gap-6">
|
|
196
|
+
<div className="text-center">
|
|
197
|
+
<p className="text-sm text-muted-foreground mb-2">30% threshold</p>
|
|
198
|
+
<WakaScratchCard
|
|
199
|
+
prize={prizesByRarity.rare}
|
|
200
|
+
revealThreshold={0.3}
|
|
201
|
+
showProgress
|
|
202
|
+
width={250}
|
|
203
|
+
height={180}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="text-center">
|
|
207
|
+
<p className="text-sm text-muted-foreground mb-2">80% threshold</p>
|
|
208
|
+
<WakaScratchCard
|
|
209
|
+
prize={prizesByRarity.epic}
|
|
210
|
+
revealThreshold={0.8}
|
|
211
|
+
showProgress
|
|
212
|
+
width={250}
|
|
213
|
+
height={180}
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
),
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const Interactive: Story = {
|
|
221
|
+
render: () => {
|
|
222
|
+
const [revealed, setRevealed] = React.useState(false)
|
|
223
|
+
const [percentage, setPercentage] = React.useState(0)
|
|
224
|
+
const [revealedPrize, setRevealedPrize] = React.useState<Prize | null>(null)
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div className="space-y-6 w-[350px]">
|
|
228
|
+
<WakaScratchCard
|
|
229
|
+
prize={samplePrize}
|
|
230
|
+
onScratch={(pct) => setPercentage(pct)}
|
|
231
|
+
onReveal={(prize) => {
|
|
232
|
+
setRevealed(true)
|
|
233
|
+
setRevealedPrize(prize)
|
|
234
|
+
}}
|
|
235
|
+
onReset={() => {
|
|
236
|
+
setRevealed(false)
|
|
237
|
+
setPercentage(0)
|
|
238
|
+
setRevealedPrize(null)
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
241
|
+
|
|
242
|
+
<div className="p-4 rounded-lg border bg-muted/30">
|
|
243
|
+
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
244
|
+
<div>Scratched: {percentage.toFixed(0)}%</div>
|
|
245
|
+
<div>Revealed: {revealed ? 'Yes' : 'No'}</div>
|
|
246
|
+
</div>
|
|
247
|
+
{revealedPrize && (
|
|
248
|
+
<div className="mt-2 pt-2 border-t text-sm">
|
|
249
|
+
<span className="text-muted-foreground">Prize: </span>
|
|
250
|
+
<span className="font-medium">{revealedPrize.name}</span>
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
)
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export const WithHook: Story = {
|
|
260
|
+
render: () => {
|
|
261
|
+
const scratchCard = useScratchCard({
|
|
262
|
+
threshold: 60,
|
|
263
|
+
onReveal: () => console.log('Revealed!'),
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<div className="space-y-6 w-[350px]">
|
|
268
|
+
<WakaScratchCard
|
|
269
|
+
prize={prizesByRarity.legendary}
|
|
270
|
+
onScratch={(pct) => scratchCard.setPercentage(pct)}
|
|
271
|
+
onReveal={() => scratchCard.reveal()}
|
|
272
|
+
onReset={() => scratchCard.reset()}
|
|
273
|
+
/>
|
|
274
|
+
|
|
275
|
+
<div className="p-4 rounded-lg border">
|
|
276
|
+
<h4 className="font-medium mb-2">Hook State</h4>
|
|
277
|
+
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
278
|
+
<div>Percentage: {scratchCard.percentage.toFixed(0)}%</div>
|
|
279
|
+
<div>Revealed: {scratchCard.isRevealed ? 'Yes' : 'No'}</div>
|
|
280
|
+
<div>Scratching: {scratchCard.isScratching ? 'Yes' : 'No'}</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div className="mt-3 flex gap-2">
|
|
284
|
+
<button
|
|
285
|
+
onClick={() => scratchCard.reveal()}
|
|
286
|
+
disabled={scratchCard.isRevealed}
|
|
287
|
+
className="px-3 py-1.5 text-xs rounded bg-primary text-primary-foreground disabled:opacity-50"
|
|
288
|
+
>
|
|
289
|
+
Force Reveal
|
|
290
|
+
</button>
|
|
291
|
+
<button
|
|
292
|
+
onClick={() => scratchCard.reset()}
|
|
293
|
+
className="px-3 py-1.5 text-xs rounded border hover:bg-muted"
|
|
294
|
+
>
|
|
295
|
+
Reset
|
|
296
|
+
</button>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
)
|
|
301
|
+
},
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export const LotteryTicket: Story = {
|
|
305
|
+
render: () => (
|
|
306
|
+
<div className="p-6 rounded-xl border bg-gradient-to-br from-amber-50 to-orange-50 dark:from-amber-950/30 dark:to-orange-950/30">
|
|
307
|
+
<div className="text-center mb-4">
|
|
308
|
+
<h3 className="text-xl font-bold text-amber-600 dark:text-amber-400">Lucky Lottery</h3>
|
|
309
|
+
<p className="text-sm text-muted-foreground">Scratch to reveal your prize!</p>
|
|
310
|
+
</div>
|
|
311
|
+
<WakaScratchCard
|
|
312
|
+
prize={samplePrize}
|
|
313
|
+
overlayColor="#d97706"
|
|
314
|
+
overlayPattern="stripes"
|
|
315
|
+
showProgress
|
|
316
|
+
/>
|
|
317
|
+
<p className="text-xs text-center text-muted-foreground mt-4">
|
|
318
|
+
Ticket #8472-9123 • Valid until Dec 31, 2024
|
|
319
|
+
</p>
|
|
320
|
+
</div>
|
|
321
|
+
),
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export const PromoCard: Story = {
|
|
325
|
+
render: () => {
|
|
326
|
+
const promoPrize: Prize = {
|
|
327
|
+
id: 'promo',
|
|
328
|
+
name: '50% OFF',
|
|
329
|
+
description: 'On your next purchase',
|
|
330
|
+
rarity: 'epic',
|
|
331
|
+
icon: <Gift className="h-12 w-12" />,
|
|
332
|
+
value: 'SAVE50',
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<div className="p-6 rounded-xl border bg-gradient-to-br from-violet-500/10 to-purple-500/10">
|
|
337
|
+
<div className="text-center mb-4">
|
|
338
|
+
<h3 className="text-lg font-bold">Special Offer</h3>
|
|
339
|
+
<p className="text-sm text-muted-foreground">Scratch to reveal your discount!</p>
|
|
340
|
+
</div>
|
|
341
|
+
<WakaScratchCard
|
|
342
|
+
prize={promoPrize}
|
|
343
|
+
overlayColor="#8b5cf6"
|
|
344
|
+
overlayPattern="dots"
|
|
345
|
+
width={280}
|
|
346
|
+
height={180}
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
349
|
+
)
|
|
350
|
+
},
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export const CustomPrizeRenderer: Story = {
|
|
354
|
+
render: () => (
|
|
355
|
+
<WakaScratchCard
|
|
356
|
+
prize={prizesByRarity.legendary}
|
|
357
|
+
renderPrize={(prize, revealed) => (
|
|
358
|
+
<div
|
|
359
|
+
className={`h-full w-full flex flex-col items-center justify-center bg-gradient-to-br from-amber-900 to-orange-900 p-4 transition-all ${
|
|
360
|
+
revealed ? 'opacity-100' : 'opacity-0'
|
|
361
|
+
}`}
|
|
362
|
+
>
|
|
363
|
+
<Trophy className="h-16 w-16 text-amber-400 mb-3" />
|
|
364
|
+
<div className="text-2xl font-bold text-white">{prize.value}</div>
|
|
365
|
+
<div className="text-amber-300 text-center mt-2">
|
|
366
|
+
{prize.name}
|
|
367
|
+
</div>
|
|
368
|
+
{revealed && (
|
|
369
|
+
<button className="mt-4 px-4 py-2 bg-amber-500 text-white rounded-lg font-medium hover:bg-amber-400 transition-colors">
|
|
370
|
+
Claim Prize
|
|
371
|
+
</button>
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
375
|
+
/>
|
|
376
|
+
),
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export const Disabled: Story = {
|
|
380
|
+
render: () => (
|
|
381
|
+
<div className="space-y-4">
|
|
382
|
+
<p className="text-sm text-muted-foreground text-center">
|
|
383
|
+
Disabled state - cannot scratch
|
|
384
|
+
</p>
|
|
385
|
+
<WakaScratchCard prize={samplePrize} disabled />
|
|
386
|
+
</div>
|
|
387
|
+
),
|
|
388
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaSecretCard, defaultSecret } from './index'
|
|
3
|
+
import type { Secret } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
const expiredSecret: Secret = {
|
|
7
|
+
id: '2',
|
|
8
|
+
name: 'Expired Certificate',
|
|
9
|
+
description: 'SSL certificate that has expired',
|
|
10
|
+
type: 'certificate',
|
|
11
|
+
value: '-----BEGIN CERTIFICATE-----...',
|
|
12
|
+
createdAt: new Date(Date.now() - 365 * 24 * 3600000),
|
|
13
|
+
updatedAt: new Date(Date.now() - 30 * 24 * 3600000),
|
|
14
|
+
expiresAt: new Date(Date.now() - 7 * 24 * 3600000),
|
|
15
|
+
tags: ['ssl', 'critical'],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const expiringSoonSecret: Secret = {
|
|
19
|
+
id: '3',
|
|
20
|
+
name: 'Database Password',
|
|
21
|
+
description: 'Password for production database',
|
|
22
|
+
type: 'password',
|
|
23
|
+
value: 'super-secret-password-123',
|
|
24
|
+
createdAt: new Date(Date.now() - 180 * 24 * 3600000),
|
|
25
|
+
updatedAt: new Date(Date.now() - 5 * 24 * 3600000),
|
|
26
|
+
expiresAt: new Date(Date.now() + 3 * 24 * 3600000),
|
|
27
|
+
rotatedAt: new Date(Date.now() - 5 * 24 * 3600000),
|
|
28
|
+
rotationPeriod: 90,
|
|
29
|
+
lastAccessedAt: new Date(Date.now() - 12 * 3600000),
|
|
30
|
+
lastAccessedBy: 'postgres-service',
|
|
31
|
+
tags: ['database', 'production'],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const needsRotationSecret: Secret = {
|
|
35
|
+
id: '4',
|
|
36
|
+
name: 'AWS Secret Key',
|
|
37
|
+
description: 'AWS access credentials',
|
|
38
|
+
type: 'api-key',
|
|
39
|
+
value: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
40
|
+
createdAt: new Date(Date.now() - 120 * 24 * 3600000),
|
|
41
|
+
updatedAt: new Date(Date.now() - 45 * 24 * 3600000),
|
|
42
|
+
rotatedAt: new Date(Date.now() - 45 * 24 * 3600000),
|
|
43
|
+
rotationPeriod: 30,
|
|
44
|
+
lastAccessedAt: new Date(Date.now() - 1 * 3600000),
|
|
45
|
+
lastAccessedBy: 's3-backup-job',
|
|
46
|
+
tags: ['aws', 'cloud'],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const tokenSecret: Secret = {
|
|
50
|
+
id: '5',
|
|
51
|
+
name: 'GitHub Personal Access Token',
|
|
52
|
+
type: 'token',
|
|
53
|
+
value: 'ghp_1234567890abcdefghijklmnopqrstuvwxyz',
|
|
54
|
+
createdAt: new Date(Date.now() - 60 * 24 * 3600000),
|
|
55
|
+
updatedAt: new Date(Date.now() - 60 * 24 * 3600000),
|
|
56
|
+
tags: ['github', 'ci-cd'],
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const simpleSecret: Secret = {
|
|
60
|
+
id: '6',
|
|
61
|
+
name: 'Simple API Key',
|
|
62
|
+
type: 'api-key',
|
|
63
|
+
createdAt: new Date(Date.now() - 30 * 24 * 3600000),
|
|
64
|
+
updatedAt: new Date(Date.now() - 30 * 24 * 3600000),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const meta: Meta<typeof WakaSecretCard> = {
|
|
68
|
+
title: 'Components/DevOps/WakaSecretCard',
|
|
69
|
+
component: WakaSecretCard,
|
|
70
|
+
parameters: {
|
|
71
|
+
layout: 'centered',
|
|
72
|
+
docs: {
|
|
73
|
+
description: {
|
|
74
|
+
component: 'A secret management card with expiration warnings, rotation indicators, access history, and secure value display.',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
tags: ['autodocs'],
|
|
79
|
+
argTypes: {
|
|
80
|
+
isValueVisible: {
|
|
81
|
+
control: 'boolean',
|
|
82
|
+
description: 'Whether the secret value is visible',
|
|
83
|
+
},
|
|
84
|
+
showHistory: {
|
|
85
|
+
control: 'boolean',
|
|
86
|
+
description: 'Show access history section',
|
|
87
|
+
},
|
|
88
|
+
compact: {
|
|
89
|
+
control: 'boolean',
|
|
90
|
+
description: 'Compact row mode',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default meta
|
|
96
|
+
type Story = StoryObj<typeof WakaSecretCard>
|
|
97
|
+
|
|
98
|
+
export const Default: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
secret: defaultSecret,
|
|
101
|
+
},
|
|
102
|
+
render: (args) => (
|
|
103
|
+
<div className="w-[500px]">
|
|
104
|
+
<WakaSecretCard
|
|
105
|
+
{...args}
|
|
106
|
+
onRotate={() => console.log('Rotate')}
|
|
107
|
+
onDelete={() => console.log('Delete')}
|
|
108
|
+
onCopy={() => console.log('Copy')}
|
|
109
|
+
onView={() => console.log('View')}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const WithAccessHistory: Story = {
|
|
116
|
+
render: () => (
|
|
117
|
+
<div className="w-[500px]">
|
|
118
|
+
<WakaSecretCard
|
|
119
|
+
secret={defaultSecret}
|
|
120
|
+
showHistory
|
|
121
|
+
onRotate={() => console.log('Rotate')}
|
|
122
|
+
onDelete={() => console.log('Delete')}
|
|
123
|
+
onCopy={() => console.log('Copy')}
|
|
124
|
+
onView={() => console.log('View')}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const Expired: Story = {
|
|
131
|
+
render: () => (
|
|
132
|
+
<div className="w-[500px]">
|
|
133
|
+
<WakaSecretCard
|
|
134
|
+
secret={expiredSecret}
|
|
135
|
+
onRotate={() => console.log('Rotate')}
|
|
136
|
+
onDelete={() => console.log('Delete')}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const ExpiringSoon: Story = {
|
|
143
|
+
render: () => (
|
|
144
|
+
<div className="w-[500px]">
|
|
145
|
+
<WakaSecretCard
|
|
146
|
+
secret={expiringSoonSecret}
|
|
147
|
+
onRotate={() => console.log('Rotate')}
|
|
148
|
+
onDelete={() => console.log('Delete')}
|
|
149
|
+
onCopy={() => console.log('Copy')}
|
|
150
|
+
onView={() => console.log('View')}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const NeedsRotation: Story = {
|
|
157
|
+
render: () => (
|
|
158
|
+
<div className="w-[500px]">
|
|
159
|
+
<WakaSecretCard
|
|
160
|
+
secret={needsRotationSecret}
|
|
161
|
+
onRotate={() => console.log('Rotate')}
|
|
162
|
+
onDelete={() => console.log('Delete')}
|
|
163
|
+
onCopy={() => console.log('Copy')}
|
|
164
|
+
onView={() => console.log('View')}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const AllTypes: Story = {
|
|
171
|
+
render: () => {
|
|
172
|
+
const types: Secret['type'][] = ['api-key', 'password', 'certificate', 'token', 'other']
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<div className="space-y-4 w-[500px]">
|
|
176
|
+
{types.map((type) => (
|
|
177
|
+
<WakaSecretCard
|
|
178
|
+
key={type}
|
|
179
|
+
secret={{
|
|
180
|
+
id: type,
|
|
181
|
+
name: `${type.replace('-', ' ')} Secret`,
|
|
182
|
+
type,
|
|
183
|
+
createdAt: new Date(),
|
|
184
|
+
updatedAt: new Date(),
|
|
185
|
+
}}
|
|
186
|
+
compact
|
|
187
|
+
/>
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const ValueVisible: Story = {
|
|
195
|
+
render: () => {
|
|
196
|
+
const [visible, setVisible] = React.useState(false)
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div className="w-[500px]">
|
|
200
|
+
<WakaSecretCard
|
|
201
|
+
secret={defaultSecret}
|
|
202
|
+
isValueVisible={visible}
|
|
203
|
+
onView={() => setVisible(!visible)}
|
|
204
|
+
onCopy={() => {
|
|
205
|
+
navigator.clipboard?.writeText(defaultSecret.value || '')
|
|
206
|
+
alert('Copied to clipboard!')
|
|
207
|
+
}}
|
|
208
|
+
onRotate={() => console.log('Rotate')}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
)
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const Compact: Story = {
|
|
216
|
+
render: () => (
|
|
217
|
+
<div className="space-y-2 w-[500px]">
|
|
218
|
+
<WakaSecretCard
|
|
219
|
+
secret={defaultSecret}
|
|
220
|
+
compact
|
|
221
|
+
onCopy={() => console.log('Copy')}
|
|
222
|
+
onView={() => console.log('View')}
|
|
223
|
+
onRotate={() => console.log('Rotate')}
|
|
224
|
+
/>
|
|
225
|
+
<WakaSecretCard
|
|
226
|
+
secret={expiredSecret}
|
|
227
|
+
compact
|
|
228
|
+
onCopy={() => console.log('Copy')}
|
|
229
|
+
onRotate={() => console.log('Rotate')}
|
|
230
|
+
/>
|
|
231
|
+
<WakaSecretCard
|
|
232
|
+
secret={expiringSoonSecret}
|
|
233
|
+
compact
|
|
234
|
+
onCopy={() => console.log('Copy')}
|
|
235
|
+
onRotate={() => console.log('Rotate')}
|
|
236
|
+
/>
|
|
237
|
+
<WakaSecretCard
|
|
238
|
+
secret={needsRotationSecret}
|
|
239
|
+
compact
|
|
240
|
+
onCopy={() => console.log('Copy')}
|
|
241
|
+
onRotate={() => console.log('Rotate')}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
),
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const NoActions: Story = {
|
|
248
|
+
render: () => (
|
|
249
|
+
<div className="w-[500px]">
|
|
250
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
251
|
+
Read-only view without action buttons
|
|
252
|
+
</p>
|
|
253
|
+
<WakaSecretCard secret={tokenSecret} />
|
|
254
|
+
</div>
|
|
255
|
+
),
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export const MinimalSecret: Story = {
|
|
259
|
+
render: () => (
|
|
260
|
+
<div className="w-[500px]">
|
|
261
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
262
|
+
Secret with minimal information
|
|
263
|
+
</p>
|
|
264
|
+
<WakaSecretCard
|
|
265
|
+
secret={simpleSecret}
|
|
266
|
+
onRotate={() => console.log('Rotate')}
|
|
267
|
+
onDelete={() => console.log('Delete')}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
),
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export const SecretVault: Story = {
|
|
274
|
+
render: () => {
|
|
275
|
+
const secrets: Secret[] = [
|
|
276
|
+
defaultSecret,
|
|
277
|
+
expiredSecret,
|
|
278
|
+
expiringSoonSecret,
|
|
279
|
+
needsRotationSecret,
|
|
280
|
+
tokenSecret,
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<div className="p-6 rounded-xl border bg-card">
|
|
285
|
+
<div className="flex items-center justify-between mb-6">
|
|
286
|
+
<div>
|
|
287
|
+
<h2 className="text-xl font-bold">Secret Vault</h2>
|
|
288
|
+
<p className="text-sm text-muted-foreground">Production Environment</p>
|
|
289
|
+
</div>
|
|
290
|
+
<div className="flex items-center gap-2 text-sm">
|
|
291
|
+
<span className="text-red-500 font-medium">1 expired</span>
|
|
292
|
+
<span className="text-muted-foreground">•</span>
|
|
293
|
+
<span className="text-yellow-500 font-medium">1 expiring soon</span>
|
|
294
|
+
<span className="text-muted-foreground">•</span>
|
|
295
|
+
<span className="text-orange-500 font-medium">1 needs rotation</span>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<div className="space-y-2 w-[500px]">
|
|
300
|
+
{secrets.map((secret) => (
|
|
301
|
+
<WakaSecretCard
|
|
302
|
+
key={secret.id}
|
|
303
|
+
secret={secret}
|
|
304
|
+
compact
|
|
305
|
+
onCopy={() => console.log('Copy:', secret.name)}
|
|
306
|
+
onView={() => console.log('View:', secret.name)}
|
|
307
|
+
onRotate={() => console.log('Rotate:', secret.name)}
|
|
308
|
+
/>
|
|
309
|
+
))}
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
},
|
|
314
|
+
}
|