@wakastellar/ui 2.1.2 → 2.3.2

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 (123) hide show
  1. package/dist/blocks/apm-overview/index.d.ts +58 -0
  2. package/dist/blocks/cicd-builder/index.d.ts +47 -0
  3. package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
  4. package/dist/blocks/container-orchestrator/index.d.ts +63 -0
  5. package/dist/blocks/database-admin/index.d.ts +84 -0
  6. package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
  7. package/dist/blocks/incident-manager/index.d.ts +44 -0
  8. package/dist/blocks/index.d.ts +10 -0
  9. package/dist/blocks/infrastructure-map/index.d.ts +32 -0
  10. package/dist/blocks/on-call-schedule/index.d.ts +43 -0
  11. package/dist/blocks/release-notes/index.d.ts +49 -0
  12. package/dist/components/index.d.ts +34 -0
  13. package/dist/components/waka-ad-banner/index.d.ts +36 -0
  14. package/dist/components/waka-ad-fallback/index.d.ts +33 -0
  15. package/dist/components/waka-ad-inline/index.d.ts +15 -0
  16. package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
  17. package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
  18. package/dist/components/waka-ad-provider/index.d.ts +103 -0
  19. package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
  20. package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
  21. package/dist/components/waka-alert-panel/index.d.ts +45 -0
  22. package/dist/components/waka-artifact-list/index.d.ts +32 -0
  23. package/dist/components/waka-build-matrix/index.d.ts +36 -0
  24. package/dist/components/waka-config-comparator/index.d.ts +37 -0
  25. package/dist/components/waka-container-list/index.d.ts +51 -0
  26. package/dist/components/waka-content-recommendation/index.d.ts +23 -0
  27. package/dist/components/waka-database-card/index.d.ts +46 -0
  28. package/dist/components/waka-dependency-tree/index.d.ts +38 -0
  29. package/dist/components/waka-env-var-editor/index.d.ts +30 -0
  30. package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
  31. package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
  32. package/dist/components/waka-log-viewer/index.d.ts +38 -0
  33. package/dist/components/waka-migration-list/index.d.ts +36 -0
  34. package/dist/components/waka-outstream-video/index.d.ts +24 -0
  35. package/dist/components/waka-pod-card/index.d.ts +73 -0
  36. package/dist/components/waka-query-explain/index.d.ts +48 -0
  37. package/dist/components/waka-secret-card/index.d.ts +43 -0
  38. package/dist/components/waka-security-scan-result/index.d.ts +45 -0
  39. package/dist/components/waka-service-graph/index.d.ts +44 -0
  40. package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
  41. package/dist/components/waka-sponsored-card/index.d.ts +25 -0
  42. package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
  43. package/dist/components/waka-test-report/index.d.ts +60 -0
  44. package/dist/components/waka-trace-viewer/index.d.ts +36 -0
  45. package/dist/components/waka-video-ad/index.d.ts +32 -0
  46. package/dist/components/waka-video-overlay/index.d.ts +26 -0
  47. package/dist/index.cjs.js +251 -200
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.es.js +47315 -35823
  50. package/dist/utils/security.d.ts +96 -0
  51. package/package.json +4 -4
  52. package/src/blocks/apm-overview/index.tsx +672 -0
  53. package/src/blocks/cicd-builder/index.tsx +738 -0
  54. package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
  55. package/src/blocks/container-orchestrator/index.tsx +729 -0
  56. package/src/blocks/database-admin/index.tsx +679 -0
  57. package/src/blocks/gitops-sync-status/index.tsx +557 -0
  58. package/src/blocks/incident-manager/index.tsx +586 -0
  59. package/src/blocks/index.ts +119 -0
  60. package/src/blocks/infrastructure-map/index.tsx +638 -0
  61. package/src/blocks/on-call-schedule/index.tsx +615 -0
  62. package/src/blocks/release-notes/index.tsx +643 -0
  63. package/src/blocks/sidebar/index.tsx +6 -6
  64. package/src/components/DataTable/templates/index.tsx +3 -2
  65. package/src/components/index.ts +283 -0
  66. package/src/components/waka-3d-pie-chart/index.tsx +11 -11
  67. package/src/components/waka-achievement-unlock/index.tsx +16 -16
  68. package/src/components/waka-ad-banner/index.tsx +275 -0
  69. package/src/components/waka-ad-fallback/index.tsx +181 -0
  70. package/src/components/waka-ad-inline/index.tsx +103 -0
  71. package/src/components/waka-ad-interstitial/index.tsx +278 -0
  72. package/src/components/waka-ad-placeholder/index.tsx +84 -0
  73. package/src/components/waka-ad-provider/index.tsx +329 -0
  74. package/src/components/waka-ad-sidebar/index.tsx +113 -0
  75. package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
  76. package/src/components/waka-alert-panel/index.tsx +493 -0
  77. package/src/components/waka-artifact-list/index.tsx +416 -0
  78. package/src/components/waka-badge-showcase/index.tsx +12 -11
  79. package/src/components/waka-build-matrix/index.tsx +396 -0
  80. package/src/components/waka-command-bar/index.tsx +2 -1
  81. package/src/components/waka-config-comparator/index.tsx +416 -0
  82. package/src/components/waka-container-list/index.tsx +475 -0
  83. package/src/components/waka-content-recommendation/index.tsx +294 -0
  84. package/src/components/waka-cost-breakdown/index.tsx +10 -10
  85. package/src/components/waka-database-card/index.tsx +473 -0
  86. package/src/components/waka-dependency-tree/index.tsx +542 -0
  87. package/src/components/waka-env-var-editor/index.tsx +417 -0
  88. package/src/components/waka-feature-flag-row/index.tsx +386 -0
  89. package/src/components/waka-funnel-chart/index.tsx +8 -8
  90. package/src/components/waka-health-pulse/index.tsx +6 -6
  91. package/src/components/waka-kubernetes-overview/index.tsx +536 -0
  92. package/src/components/waka-leaderboard/index.tsx +9 -9
  93. package/src/components/waka-log-viewer/index.tsx +386 -0
  94. package/src/components/waka-loot-box/index.tsx +20 -20
  95. package/src/components/waka-migration-list/index.tsx +487 -0
  96. package/src/components/waka-outstream-video/index.tsx +240 -0
  97. package/src/components/waka-player-card/index.tsx +5 -5
  98. package/src/components/waka-pod-card/index.tsx +528 -0
  99. package/src/components/waka-query-explain/index.tsx +657 -0
  100. package/src/components/waka-quota-bar/index.tsx +4 -4
  101. package/src/components/waka-radar-score/index.tsx +10 -10
  102. package/src/components/waka-scratch-card/index.tsx +5 -4
  103. package/src/components/waka-secret-card/index.tsx +371 -0
  104. package/src/components/waka-security-scan-result/index.tsx +473 -0
  105. package/src/components/waka-server-rack/index.tsx +28 -27
  106. package/src/components/waka-service-graph/index.tsx +445 -0
  107. package/src/components/waka-sponsored-badge/index.tsx +97 -0
  108. package/src/components/waka-sponsored-card/index.tsx +275 -0
  109. package/src/components/waka-sponsored-feed/index.tsx +127 -0
  110. package/src/components/waka-spotlight/index.tsx +2 -1
  111. package/src/components/waka-success-explosion/index.tsx +4 -4
  112. package/src/components/waka-test-report/index.tsx +469 -0
  113. package/src/components/waka-trace-viewer/index.tsx +490 -0
  114. package/src/components/waka-video-ad/index.tsx +406 -0
  115. package/src/components/waka-video-overlay/index.tsx +257 -0
  116. package/src/components/waka-xp-bar/index.tsx +13 -13
  117. package/src/styles/base.css +16 -0
  118. package/src/styles/tailwind.preset.js +12 -0
  119. package/src/styles/themes/forest.css +16 -0
  120. package/src/styles/themes/monochrome.css +16 -0
  121. package/src/styles/themes/perpetuity.css +16 -0
  122. package/src/styles/themes/sunset.css +16 -0
  123. package/src/styles/themes/twilight.css +16 -0
@@ -171,14 +171,14 @@ export function WakaRadarScore({
171
171
  .join(" ") + " Z"
172
172
  }
173
173
 
174
- // Default colors for datasets
174
+ // Default colors for datasets - using CSS variables for theme support
175
175
  const defaultColors = [
176
- "#3b82f6", // blue
177
- "#ef4444", // red
178
- "#22c55e", // green
179
- "#f59e0b", // amber
180
- "#8b5cf6", // violet
181
- "#ec4899", // pink
176
+ "hsl(var(--chart-1))",
177
+ "hsl(var(--chart-2))",
178
+ "hsl(var(--chart-3))",
179
+ "hsl(var(--chart-4))",
180
+ "hsl(var(--chart-5))",
181
+ "hsl(var(--primary))",
182
182
  ]
183
183
 
184
184
  const getDatasetColor = (index: number, dataset: RadarDataSet) => {
@@ -443,7 +443,7 @@ export interface WakaRadarScoreSimpleProps
443
443
 
444
444
  export function WakaRadarScoreSimple({
445
445
  data,
446
- color = "#3b82f6",
446
+ color = "hsl(var(--chart-1))",
447
447
  fillOpacity = 0.2,
448
448
  ...props
449
449
  }: WakaRadarScoreSimpleProps) {
@@ -494,14 +494,14 @@ export function WakaRadarScoreCompare({
494
494
  id: "a",
495
495
  label: dataA.label,
496
496
  data: dataA.data,
497
- color: dataA.color || "#3b82f6",
497
+ color: dataA.color || "hsl(var(--chart-1))",
498
498
  fillOpacity: 0.15,
499
499
  },
500
500
  {
501
501
  id: "b",
502
502
  label: dataB.label,
503
503
  data: dataB.data,
504
- color: dataB.color || "#ef4444",
504
+ color: dataB.color || "hsl(var(--chart-2))",
505
505
  fillOpacity: 0.15,
506
506
  },
507
507
  ]
@@ -100,6 +100,7 @@ export interface UseScratchCardReturn {
100
100
  // Rarity Configuration
101
101
  // ============================================================================
102
102
 
103
+ // Rarity configuration using CSS variables for theme support
103
104
  const rarityConfig: Record<PrizeRarity, {
104
105
  gradient: string
105
106
  glowColor: string
@@ -111,7 +112,7 @@ const rarityConfig: Record<PrizeRarity, {
111
112
  }> = {
112
113
  common: {
113
114
  gradient: "from-slate-400 to-slate-600",
114
- glowColor: "#94a3b8",
115
+ glowColor: "hsl(var(--muted-foreground))",
115
116
  borderColor: "border-slate-400",
116
117
  bgColor: "bg-slate-100",
117
118
  textColor: "text-slate-600",
@@ -120,7 +121,7 @@ const rarityConfig: Record<PrizeRarity, {
120
121
  },
121
122
  rare: {
122
123
  gradient: "from-blue-400 to-blue-600",
123
- glowColor: "#60a5fa",
124
+ glowColor: "hsl(var(--info))",
124
125
  borderColor: "border-blue-400",
125
126
  bgColor: "bg-blue-100",
126
127
  textColor: "text-blue-600",
@@ -129,7 +130,7 @@ const rarityConfig: Record<PrizeRarity, {
129
130
  },
130
131
  epic: {
131
132
  gradient: "from-purple-400 to-purple-600",
132
- glowColor: "#a855f7",
133
+ glowColor: "hsl(var(--chart-3))",
133
134
  borderColor: "border-purple-400",
134
135
  bgColor: "bg-purple-100",
135
136
  textColor: "text-purple-600",
@@ -138,7 +139,7 @@ const rarityConfig: Record<PrizeRarity, {
138
139
  },
139
140
  legendary: {
140
141
  gradient: "from-amber-400 via-orange-500 to-red-500",
141
- glowColor: "#fbbf24",
142
+ glowColor: "hsl(var(--warning))",
142
143
  borderColor: "border-amber-400",
143
144
  bgColor: "bg-amber-100",
144
145
  textColor: "text-amber-600",
@@ -0,0 +1,371 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils/cn"
5
+ import { Button } from "../button"
6
+ import { Badge } from "../badge"
7
+ import { Card, CardContent, CardHeader, CardTitle } from "../card"
8
+ import {
9
+ Tooltip,
10
+ TooltipContent,
11
+ TooltipProvider,
12
+ TooltipTrigger,
13
+ } from "../tooltip"
14
+ import {
15
+ Key,
16
+ Copy,
17
+ CheckCircle2,
18
+ RotateCcw,
19
+ Trash2,
20
+ Clock,
21
+ Eye,
22
+ EyeOff,
23
+ AlertTriangle,
24
+ Shield,
25
+ User,
26
+ Calendar,
27
+ History,
28
+ } from "lucide-react"
29
+
30
+ export interface SecretAccess {
31
+ user: string
32
+ action: "read" | "write" | "rotate"
33
+ timestamp: Date
34
+ }
35
+
36
+ export interface Secret {
37
+ id: string
38
+ name: string
39
+ description?: string
40
+ type: "api-key" | "password" | "certificate" | "token" | "other"
41
+ value?: string // Usually not exposed
42
+ createdAt: Date
43
+ updatedAt: Date
44
+ expiresAt?: Date
45
+ rotatedAt?: Date
46
+ rotationPeriod?: number // in days
47
+ lastAccessedAt?: Date
48
+ lastAccessedBy?: string
49
+ accessHistory?: SecretAccess[]
50
+ tags?: string[]
51
+ }
52
+
53
+ export interface WakaSecretCardProps {
54
+ /** Secret data */
55
+ secret: Secret
56
+ /** Callback when rotating secret */
57
+ onRotate?: () => void
58
+ /** Callback when deleting secret */
59
+ onDelete?: () => void
60
+ /** Callback when copying secret */
61
+ onCopy?: () => void
62
+ /** Callback when viewing secret value */
63
+ onView?: () => void
64
+ /** Whether the secret value is visible */
65
+ isValueVisible?: boolean
66
+ /** Show access history */
67
+ showHistory?: boolean
68
+ /** Compact mode */
69
+ compact?: boolean
70
+ /** Custom class name */
71
+ className?: string
72
+ }
73
+
74
+ const typeConfig: Record<Secret["type"], { icon: React.ElementType; label: string; color: string }> = {
75
+ "api-key": { icon: Key, label: "API Key", color: "text-blue-500" },
76
+ password: { icon: Shield, label: "Password", color: "text-purple-500" },
77
+ certificate: { icon: Shield, label: "Certificate", color: "text-green-500" },
78
+ token: { icon: Key, label: "Token", color: "text-orange-500" },
79
+ other: { icon: Key, label: "Secret", color: "text-gray-500" },
80
+ }
81
+
82
+ function formatDate(date: Date): string {
83
+ return date.toLocaleDateString("fr-FR", {
84
+ day: "2-digit",
85
+ month: "short",
86
+ year: "numeric",
87
+ })
88
+ }
89
+
90
+ function formatRelativeTime(date: Date): string {
91
+ const diff = Date.now() - date.getTime()
92
+ const minutes = Math.floor(diff / 60000)
93
+ const hours = Math.floor(minutes / 60)
94
+ const days = Math.floor(hours / 24)
95
+
96
+ if (days > 30) return formatDate(date)
97
+ if (days > 0) return `${days}d ago`
98
+ if (hours > 0) return `${hours}h ago`
99
+ return `${minutes}m ago`
100
+ }
101
+
102
+ function isExpiringSoon(date?: Date): boolean {
103
+ if (!date) return false
104
+ const diff = date.getTime() - Date.now()
105
+ return diff > 0 && diff < 7 * 24 * 60 * 60 * 1000 // Less than 7 days
106
+ }
107
+
108
+ function isExpired(date?: Date): boolean {
109
+ if (!date) return false
110
+ return date.getTime() < Date.now()
111
+ }
112
+
113
+ function needsRotation(secret: Secret): boolean {
114
+ if (!secret.rotationPeriod || !secret.rotatedAt) return false
115
+ const daysSinceRotation = Math.floor(
116
+ (Date.now() - secret.rotatedAt.getTime()) / (24 * 60 * 60 * 1000)
117
+ )
118
+ return daysSinceRotation >= secret.rotationPeriod
119
+ }
120
+
121
+ export function WakaSecretCard({
122
+ secret,
123
+ onRotate,
124
+ onDelete,
125
+ onCopy,
126
+ onView,
127
+ isValueVisible = false,
128
+ showHistory = false,
129
+ compact = false,
130
+ className,
131
+ }: WakaSecretCardProps) {
132
+ const [copied, setCopied] = React.useState(false)
133
+ const typeConf = typeConfig[secret.type]
134
+ const TypeIcon = typeConf.icon
135
+ const expired = isExpired(secret.expiresAt)
136
+ const expiringSoon = isExpiringSoon(secret.expiresAt)
137
+ const needsRotate = needsRotation(secret)
138
+
139
+ const handleCopy = () => {
140
+ onCopy?.()
141
+ setCopied(true)
142
+ setTimeout(() => setCopied(false), 2000)
143
+ }
144
+
145
+ if (compact) {
146
+ return (
147
+ <div
148
+ className={cn(
149
+ "flex items-center gap-3 p-3 border rounded-lg hover:bg-muted/30 transition-colors",
150
+ expired && "border-red-500/50 bg-red-500/5",
151
+ expiringSoon && !expired && "border-yellow-500/50 bg-yellow-500/5",
152
+ needsRotate && "border-orange-500/50",
153
+ className
154
+ )}
155
+ >
156
+ <TypeIcon className={cn("h-5 w-5 shrink-0", typeConf.color)} />
157
+
158
+ <div className="flex-1 min-w-0">
159
+ <div className="font-medium truncate">{secret.name}</div>
160
+ <div className="text-xs text-muted-foreground flex items-center gap-2">
161
+ <span>{typeConf.label}</span>
162
+ {secret.lastAccessedAt && (
163
+ <span>Last used {formatRelativeTime(secret.lastAccessedAt)}</span>
164
+ )}
165
+ </div>
166
+ </div>
167
+
168
+ {expired && (
169
+ <Badge className="bg-red-500 text-xs">Expired</Badge>
170
+ )}
171
+ {expiringSoon && !expired && (
172
+ <Badge className="bg-yellow-500 text-xs">Expiring Soon</Badge>
173
+ )}
174
+ {needsRotate && (
175
+ <Badge variant="outline" className="text-orange-500 text-xs">
176
+ <RotateCcw className="h-3 w-3 mr-1" />
177
+ Rotate
178
+ </Badge>
179
+ )}
180
+
181
+ <div className="flex items-center gap-1">
182
+ {onView && (
183
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={onView}>
184
+ {isValueVisible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
185
+ </Button>
186
+ )}
187
+ {onCopy && (
188
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={handleCopy}>
189
+ {copied ? <CheckCircle2 className="h-4 w-4 text-green-500" /> : <Copy className="h-4 w-4" />}
190
+ </Button>
191
+ )}
192
+ {onRotate && (
193
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={onRotate}>
194
+ <RotateCcw className="h-4 w-4" />
195
+ </Button>
196
+ )}
197
+ </div>
198
+ </div>
199
+ )
200
+ }
201
+
202
+ return (
203
+ <Card className={cn(
204
+ expired && "border-red-500/50",
205
+ expiringSoon && !expired && "border-yellow-500/50",
206
+ className
207
+ )}>
208
+ <CardHeader className="pb-3">
209
+ <div className="flex items-start justify-between">
210
+ <div className="flex items-center gap-3">
211
+ <div className={cn(
212
+ "w-10 h-10 rounded-lg flex items-center justify-center",
213
+ "bg-muted"
214
+ )}>
215
+ <TypeIcon className={cn("h-5 w-5", typeConf.color)} />
216
+ </div>
217
+ <div>
218
+ <CardTitle className="text-base">{secret.name}</CardTitle>
219
+ <div className="flex items-center gap-2 mt-1">
220
+ <Badge variant="outline" className="text-xs">
221
+ {typeConf.label}
222
+ </Badge>
223
+ {secret.tags?.map((tag) => (
224
+ <Badge key={tag} variant="secondary" className="text-xs">
225
+ {tag}
226
+ </Badge>
227
+ ))}
228
+ </div>
229
+ </div>
230
+ </div>
231
+
232
+ <div className="flex items-center gap-1">
233
+ {expired && (
234
+ <Badge className="bg-red-500">
235
+ <AlertTriangle className="h-3 w-3 mr-1" />
236
+ Expired
237
+ </Badge>
238
+ )}
239
+ {expiringSoon && !expired && (
240
+ <Badge className="bg-yellow-500">
241
+ <Clock className="h-3 w-3 mr-1" />
242
+ Expires {formatDate(secret.expiresAt!)}
243
+ </Badge>
244
+ )}
245
+ {needsRotate && (
246
+ <Badge variant="outline" className="text-orange-500">
247
+ <RotateCcw className="h-3 w-3 mr-1" />
248
+ Needs Rotation
249
+ </Badge>
250
+ )}
251
+ </div>
252
+ </div>
253
+ </CardHeader>
254
+
255
+ <CardContent className="space-y-4">
256
+ {secret.description && (
257
+ <p className="text-sm text-muted-foreground">{secret.description}</p>
258
+ )}
259
+
260
+ {/* Secret value preview */}
261
+ {secret.value && (
262
+ <div className="flex items-center gap-2 p-2 bg-muted/50 rounded-lg font-mono text-sm">
263
+ <span className="flex-1 truncate">
264
+ {isValueVisible ? secret.value : "•".repeat(24)}
265
+ </span>
266
+ {onView && (
267
+ <Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0" onClick={onView}>
268
+ {isValueVisible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
269
+ </Button>
270
+ )}
271
+ {onCopy && (
272
+ <Button variant="ghost" size="sm" className="h-7 w-7 p-0 shrink-0" onClick={handleCopy}>
273
+ {copied ? <CheckCircle2 className="h-4 w-4 text-green-500" /> : <Copy className="h-4 w-4" />}
274
+ </Button>
275
+ )}
276
+ </div>
277
+ )}
278
+
279
+ {/* Metadata */}
280
+ <div className="grid grid-cols-2 gap-4 text-sm">
281
+ <div className="flex items-center gap-2 text-muted-foreground">
282
+ <Calendar className="h-4 w-4" />
283
+ <span>Created {formatDate(secret.createdAt)}</span>
284
+ </div>
285
+ <div className="flex items-center gap-2 text-muted-foreground">
286
+ <Clock className="h-4 w-4" />
287
+ <span>Updated {formatRelativeTime(secret.updatedAt)}</span>
288
+ </div>
289
+ {secret.rotatedAt && (
290
+ <div className="flex items-center gap-2 text-muted-foreground">
291
+ <RotateCcw className="h-4 w-4" />
292
+ <span>Rotated {formatRelativeTime(secret.rotatedAt)}</span>
293
+ </div>
294
+ )}
295
+ {secret.lastAccessedAt && (
296
+ <div className="flex items-center gap-2 text-muted-foreground">
297
+ <User className="h-4 w-4" />
298
+ <span>
299
+ Used {formatRelativeTime(secret.lastAccessedAt)}
300
+ {secret.lastAccessedBy && ` by ${secret.lastAccessedBy}`}
301
+ </span>
302
+ </div>
303
+ )}
304
+ </div>
305
+
306
+ {/* Access history */}
307
+ {showHistory && secret.accessHistory && secret.accessHistory.length > 0 && (
308
+ <div className="border-t pt-4">
309
+ <div className="flex items-center gap-2 mb-2 text-sm font-medium">
310
+ <History className="h-4 w-4" />
311
+ Recent Access
312
+ </div>
313
+ <div className="space-y-1">
314
+ {secret.accessHistory.slice(0, 5).map((access, i) => (
315
+ <div key={i} className="flex items-center justify-between text-xs text-muted-foreground">
316
+ <span>
317
+ {access.user} - {access.action}
318
+ </span>
319
+ <span>{formatRelativeTime(access.timestamp)}</span>
320
+ </div>
321
+ ))}
322
+ </div>
323
+ </div>
324
+ )}
325
+
326
+ {/* Actions */}
327
+ <div className="flex items-center gap-2 pt-2 border-t">
328
+ {onRotate && (
329
+ <Button variant="outline" size="sm" onClick={onRotate}>
330
+ <RotateCcw className="h-4 w-4 mr-2" />
331
+ Rotate
332
+ </Button>
333
+ )}
334
+ {onDelete && (
335
+ <Button
336
+ variant="outline"
337
+ size="sm"
338
+ className="text-destructive hover:text-destructive"
339
+ onClick={onDelete}
340
+ >
341
+ <Trash2 className="h-4 w-4 mr-2" />
342
+ Delete
343
+ </Button>
344
+ )}
345
+ </div>
346
+ </CardContent>
347
+ </Card>
348
+ )
349
+ }
350
+
351
+ // Default sample secret for demo
352
+ export const defaultSecret: Secret = {
353
+ id: "1",
354
+ name: "Production API Key",
355
+ description: "Main API key for production services",
356
+ type: "api-key",
357
+ value: "sk_live_1234567890abcdefghijklmnopqrstuvwxyz",
358
+ createdAt: new Date(Date.now() - 90 * 24 * 3600000),
359
+ updatedAt: new Date(Date.now() - 7 * 24 * 3600000),
360
+ rotatedAt: new Date(Date.now() - 30 * 24 * 3600000),
361
+ rotationPeriod: 30,
362
+ expiresAt: new Date(Date.now() + 23 * 24 * 3600000),
363
+ lastAccessedAt: new Date(Date.now() - 2 * 3600000),
364
+ lastAccessedBy: "api-gateway",
365
+ tags: ["production", "critical"],
366
+ accessHistory: [
367
+ { user: "api-gateway", action: "read", timestamp: new Date(Date.now() - 2 * 3600000) },
368
+ { user: "john.doe", action: "rotate", timestamp: new Date(Date.now() - 30 * 24 * 3600000) },
369
+ { user: "api-gateway", action: "read", timestamp: new Date(Date.now() - 24 * 3600000) },
370
+ ],
371
+ }