@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.
- package/dist/blocks/apm-overview/index.d.ts +58 -0
- package/dist/blocks/cicd-builder/index.d.ts +47 -0
- package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
- package/dist/blocks/container-orchestrator/index.d.ts +63 -0
- package/dist/blocks/database-admin/index.d.ts +84 -0
- package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
- package/dist/blocks/incident-manager/index.d.ts +44 -0
- package/dist/blocks/index.d.ts +10 -0
- package/dist/blocks/infrastructure-map/index.d.ts +32 -0
- package/dist/blocks/on-call-schedule/index.d.ts +43 -0
- package/dist/blocks/release-notes/index.d.ts +49 -0
- package/dist/components/index.d.ts +34 -0
- package/dist/components/waka-ad-banner/index.d.ts +36 -0
- package/dist/components/waka-ad-fallback/index.d.ts +33 -0
- package/dist/components/waka-ad-inline/index.d.ts +15 -0
- package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
- package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
- package/dist/components/waka-ad-provider/index.d.ts +103 -0
- package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
- package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
- package/dist/components/waka-alert-panel/index.d.ts +45 -0
- package/dist/components/waka-artifact-list/index.d.ts +32 -0
- package/dist/components/waka-build-matrix/index.d.ts +36 -0
- package/dist/components/waka-config-comparator/index.d.ts +37 -0
- package/dist/components/waka-container-list/index.d.ts +51 -0
- package/dist/components/waka-content-recommendation/index.d.ts +23 -0
- package/dist/components/waka-database-card/index.d.ts +46 -0
- package/dist/components/waka-dependency-tree/index.d.ts +38 -0
- package/dist/components/waka-env-var-editor/index.d.ts +30 -0
- package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
- package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
- package/dist/components/waka-log-viewer/index.d.ts +38 -0
- package/dist/components/waka-migration-list/index.d.ts +36 -0
- package/dist/components/waka-outstream-video/index.d.ts +24 -0
- package/dist/components/waka-pod-card/index.d.ts +73 -0
- package/dist/components/waka-query-explain/index.d.ts +48 -0
- package/dist/components/waka-secret-card/index.d.ts +43 -0
- package/dist/components/waka-security-scan-result/index.d.ts +45 -0
- package/dist/components/waka-service-graph/index.d.ts +44 -0
- package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
- package/dist/components/waka-sponsored-card/index.d.ts +25 -0
- package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
- package/dist/components/waka-test-report/index.d.ts +60 -0
- package/dist/components/waka-trace-viewer/index.d.ts +36 -0
- package/dist/components/waka-video-ad/index.d.ts +32 -0
- package/dist/components/waka-video-overlay/index.d.ts +26 -0
- package/dist/index.cjs.js +251 -200
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +47315 -35823
- package/dist/utils/security.d.ts +96 -0
- package/package.json +4 -4
- package/src/blocks/apm-overview/index.tsx +672 -0
- package/src/blocks/cicd-builder/index.tsx +738 -0
- package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
- package/src/blocks/container-orchestrator/index.tsx +729 -0
- package/src/blocks/database-admin/index.tsx +679 -0
- package/src/blocks/gitops-sync-status/index.tsx +557 -0
- package/src/blocks/incident-manager/index.tsx +586 -0
- package/src/blocks/index.ts +119 -0
- package/src/blocks/infrastructure-map/index.tsx +638 -0
- package/src/blocks/on-call-schedule/index.tsx +615 -0
- package/src/blocks/release-notes/index.tsx +643 -0
- package/src/blocks/sidebar/index.tsx +6 -6
- package/src/components/DataTable/templates/index.tsx +3 -2
- package/src/components/index.ts +283 -0
- package/src/components/waka-3d-pie-chart/index.tsx +11 -11
- package/src/components/waka-achievement-unlock/index.tsx +16 -16
- package/src/components/waka-ad-banner/index.tsx +275 -0
- package/src/components/waka-ad-fallback/index.tsx +181 -0
- package/src/components/waka-ad-inline/index.tsx +103 -0
- package/src/components/waka-ad-interstitial/index.tsx +278 -0
- package/src/components/waka-ad-placeholder/index.tsx +84 -0
- package/src/components/waka-ad-provider/index.tsx +329 -0
- package/src/components/waka-ad-sidebar/index.tsx +113 -0
- package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
- package/src/components/waka-alert-panel/index.tsx +493 -0
- package/src/components/waka-artifact-list/index.tsx +416 -0
- package/src/components/waka-badge-showcase/index.tsx +12 -11
- package/src/components/waka-build-matrix/index.tsx +396 -0
- package/src/components/waka-command-bar/index.tsx +2 -1
- package/src/components/waka-config-comparator/index.tsx +416 -0
- package/src/components/waka-container-list/index.tsx +475 -0
- package/src/components/waka-content-recommendation/index.tsx +294 -0
- package/src/components/waka-cost-breakdown/index.tsx +10 -10
- package/src/components/waka-database-card/index.tsx +473 -0
- package/src/components/waka-dependency-tree/index.tsx +542 -0
- package/src/components/waka-env-var-editor/index.tsx +417 -0
- package/src/components/waka-feature-flag-row/index.tsx +386 -0
- package/src/components/waka-funnel-chart/index.tsx +8 -8
- package/src/components/waka-health-pulse/index.tsx +6 -6
- package/src/components/waka-kubernetes-overview/index.tsx +536 -0
- package/src/components/waka-leaderboard/index.tsx +9 -9
- package/src/components/waka-log-viewer/index.tsx +386 -0
- package/src/components/waka-loot-box/index.tsx +20 -20
- package/src/components/waka-migration-list/index.tsx +487 -0
- package/src/components/waka-outstream-video/index.tsx +240 -0
- package/src/components/waka-player-card/index.tsx +5 -5
- package/src/components/waka-pod-card/index.tsx +528 -0
- package/src/components/waka-query-explain/index.tsx +657 -0
- package/src/components/waka-quota-bar/index.tsx +4 -4
- package/src/components/waka-radar-score/index.tsx +10 -10
- package/src/components/waka-scratch-card/index.tsx +5 -4
- package/src/components/waka-secret-card/index.tsx +371 -0
- package/src/components/waka-security-scan-result/index.tsx +473 -0
- package/src/components/waka-server-rack/index.tsx +28 -27
- package/src/components/waka-service-graph/index.tsx +445 -0
- package/src/components/waka-sponsored-badge/index.tsx +97 -0
- package/src/components/waka-sponsored-card/index.tsx +275 -0
- package/src/components/waka-sponsored-feed/index.tsx +127 -0
- package/src/components/waka-spotlight/index.tsx +2 -1
- package/src/components/waka-success-explosion/index.tsx +4 -4
- package/src/components/waka-test-report/index.tsx +469 -0
- package/src/components/waka-trace-viewer/index.tsx +490 -0
- package/src/components/waka-video-ad/index.tsx +406 -0
- package/src/components/waka-video-overlay/index.tsx +257 -0
- package/src/components/waka-xp-bar/index.tsx +13 -13
- package/src/styles/base.css +16 -0
- package/src/styles/tailwind.preset.js +12 -0
- package/src/styles/themes/forest.css +16 -0
- package/src/styles/themes/monochrome.css +16 -0
- package/src/styles/themes/perpetuity.css +16 -0
- package/src/styles/themes/sunset.css +16 -0
- package/src/styles/themes/twilight.css +16 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../utils/cn"
|
|
5
|
+
import { Badge } from "../badge"
|
|
6
|
+
import { Button } from "../button"
|
|
7
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../card"
|
|
8
|
+
import { Progress } from "../progress"
|
|
9
|
+
import {
|
|
10
|
+
Tooltip,
|
|
11
|
+
TooltipContent,
|
|
12
|
+
TooltipProvider,
|
|
13
|
+
TooltipTrigger,
|
|
14
|
+
} from "../tooltip"
|
|
15
|
+
import {
|
|
16
|
+
Database,
|
|
17
|
+
Activity,
|
|
18
|
+
Users,
|
|
19
|
+
Clock,
|
|
20
|
+
AlertTriangle,
|
|
21
|
+
RefreshCw,
|
|
22
|
+
Server,
|
|
23
|
+
HardDrive,
|
|
24
|
+
Zap,
|
|
25
|
+
TrendingUp,
|
|
26
|
+
TrendingDown,
|
|
27
|
+
Minus,
|
|
28
|
+
GitBranch,
|
|
29
|
+
CheckCircle2,
|
|
30
|
+
XCircle,
|
|
31
|
+
} from "lucide-react"
|
|
32
|
+
|
|
33
|
+
export type DatabaseType = "postgresql" | "mysql" | "mongodb" | "redis" | "elasticsearch" | "other"
|
|
34
|
+
export type DatabaseStatus = "healthy" | "warning" | "critical" | "offline" | "syncing"
|
|
35
|
+
export type ReplicationRole = "primary" | "replica" | "standalone"
|
|
36
|
+
|
|
37
|
+
export interface DatabaseMetrics {
|
|
38
|
+
connectionsActive: number
|
|
39
|
+
connectionsMax: number
|
|
40
|
+
queriesPerSecond: number
|
|
41
|
+
slowQueries: number
|
|
42
|
+
avgQueryTime: number // in ms
|
|
43
|
+
cacheHitRate?: number // percentage
|
|
44
|
+
storageUsed: number // in bytes
|
|
45
|
+
storageTotal: number // in bytes
|
|
46
|
+
replicationLag?: number // in ms
|
|
47
|
+
uptime?: number // in seconds
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DatabaseInfo {
|
|
51
|
+
id: string
|
|
52
|
+
name: string
|
|
53
|
+
type: DatabaseType
|
|
54
|
+
version?: string
|
|
55
|
+
host: string
|
|
56
|
+
port: number
|
|
57
|
+
status: DatabaseStatus
|
|
58
|
+
role: ReplicationRole
|
|
59
|
+
metrics: DatabaseMetrics
|
|
60
|
+
replicas?: number
|
|
61
|
+
lastBackup?: Date
|
|
62
|
+
tags?: string[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface WakaDatabaseCardProps {
|
|
66
|
+
/** Database info */
|
|
67
|
+
database: DatabaseInfo
|
|
68
|
+
/** Callback when clicking refresh */
|
|
69
|
+
onRefresh?: () => void
|
|
70
|
+
/** Callback when clicking on the database */
|
|
71
|
+
onClick?: () => void
|
|
72
|
+
/** Show detailed metrics */
|
|
73
|
+
detailed?: boolean
|
|
74
|
+
/** Compact mode */
|
|
75
|
+
compact?: boolean
|
|
76
|
+
/** Custom class name */
|
|
77
|
+
className?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const typeConfig: Record<DatabaseType, { label: string; color: string }> = {
|
|
81
|
+
postgresql: { label: "PostgreSQL", color: "text-blue-500" },
|
|
82
|
+
mysql: { label: "MySQL", color: "text-orange-500" },
|
|
83
|
+
mongodb: { label: "MongoDB", color: "text-green-500" },
|
|
84
|
+
redis: { label: "Redis", color: "text-red-500" },
|
|
85
|
+
elasticsearch: { label: "Elasticsearch", color: "text-yellow-500" },
|
|
86
|
+
other: { label: "Database", color: "text-gray-500" },
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const statusConfig: Record<DatabaseStatus, { label: string; color: string; bgColor: string }> = {
|
|
90
|
+
healthy: { label: "Healthy", color: "text-green-500", bgColor: "bg-green-500" },
|
|
91
|
+
warning: { label: "Warning", color: "text-yellow-500", bgColor: "bg-yellow-500" },
|
|
92
|
+
critical: { label: "Critical", color: "text-red-500", bgColor: "bg-red-500" },
|
|
93
|
+
offline: { label: "Offline", color: "text-gray-500", bgColor: "bg-gray-500" },
|
|
94
|
+
syncing: { label: "Syncing", color: "text-blue-500", bgColor: "bg-blue-500" },
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function formatBytes(bytes: number): string {
|
|
98
|
+
if (bytes === 0) return "0 B"
|
|
99
|
+
const k = 1024
|
|
100
|
+
const sizes = ["B", "KB", "MB", "GB", "TB"]
|
|
101
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
102
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatDuration(seconds: number): string {
|
|
106
|
+
if (seconds < 60) return `${seconds}s`
|
|
107
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`
|
|
108
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`
|
|
109
|
+
return `${Math.floor(seconds / 86400)}d`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function formatMs(ms: number): string {
|
|
113
|
+
if (ms < 1000) return `${ms.toFixed(0)}ms`
|
|
114
|
+
return `${(ms / 1000).toFixed(2)}s`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function TrendIndicator({ value, threshold = 0 }: { value: number; threshold?: number }) {
|
|
118
|
+
if (value > threshold) {
|
|
119
|
+
return <TrendingUp className="h-3 w-3 text-green-500" />
|
|
120
|
+
}
|
|
121
|
+
if (value < -threshold) {
|
|
122
|
+
return <TrendingDown className="h-3 w-3 text-red-500" />
|
|
123
|
+
}
|
|
124
|
+
return <Minus className="h-3 w-3 text-muted-foreground" />
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function WakaDatabaseCard({
|
|
128
|
+
database,
|
|
129
|
+
onRefresh,
|
|
130
|
+
onClick,
|
|
131
|
+
detailed = false,
|
|
132
|
+
compact = false,
|
|
133
|
+
className,
|
|
134
|
+
}: WakaDatabaseCardProps) {
|
|
135
|
+
const typeConf = typeConfig[database.type]
|
|
136
|
+
const statusConf = statusConfig[database.status]
|
|
137
|
+
const { metrics } = database
|
|
138
|
+
|
|
139
|
+
const connectionPercent = (metrics.connectionsActive / metrics.connectionsMax) * 100
|
|
140
|
+
const storagePercent = (metrics.storageUsed / metrics.storageTotal) * 100
|
|
141
|
+
|
|
142
|
+
if (compact) {
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
className={cn(
|
|
146
|
+
"flex items-center gap-3 p-3 border rounded-lg hover:bg-muted/30 transition-colors cursor-pointer",
|
|
147
|
+
database.status === "critical" && "border-red-500/50",
|
|
148
|
+
database.status === "warning" && "border-yellow-500/50",
|
|
149
|
+
className
|
|
150
|
+
)}
|
|
151
|
+
onClick={onClick}
|
|
152
|
+
>
|
|
153
|
+
<div className="relative">
|
|
154
|
+
<Database className={cn("h-5 w-5", typeConf.color)} />
|
|
155
|
+
<div className={cn(
|
|
156
|
+
"absolute -bottom-0.5 -right-0.5 w-2 h-2 rounded-full border border-background",
|
|
157
|
+
statusConf.bgColor
|
|
158
|
+
)} />
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<div className="flex-1 min-w-0">
|
|
162
|
+
<div className="font-medium truncate">{database.name}</div>
|
|
163
|
+
<div className="text-xs text-muted-foreground flex items-center gap-2">
|
|
164
|
+
<span>{typeConf.label}</span>
|
|
165
|
+
{database.version && <span>v{database.version}</span>}
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div className="flex items-center gap-3 text-xs">
|
|
170
|
+
<div className="flex items-center gap-1">
|
|
171
|
+
<Users className="h-3 w-3 text-muted-foreground" />
|
|
172
|
+
<span>{metrics.connectionsActive}/{metrics.connectionsMax}</span>
|
|
173
|
+
</div>
|
|
174
|
+
<div className="flex items-center gap-1">
|
|
175
|
+
<Zap className="h-3 w-3 text-muted-foreground" />
|
|
176
|
+
<span>{metrics.queriesPerSecond}/s</span>
|
|
177
|
+
</div>
|
|
178
|
+
{metrics.slowQueries > 0 && (
|
|
179
|
+
<Badge variant="outline" className="text-yellow-500">
|
|
180
|
+
{metrics.slowQueries} slow
|
|
181
|
+
</Badge>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<Badge className={cn("text-white", statusConf.bgColor)}>
|
|
186
|
+
{statusConf.label}
|
|
187
|
+
</Badge>
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Card className={cn(
|
|
194
|
+
database.status === "critical" && "border-red-500/50",
|
|
195
|
+
database.status === "warning" && "border-yellow-500/50",
|
|
196
|
+
className
|
|
197
|
+
)}>
|
|
198
|
+
<CardHeader className="pb-3">
|
|
199
|
+
<div className="flex items-start justify-between">
|
|
200
|
+
<div className="flex items-center gap-3">
|
|
201
|
+
<div className={cn(
|
|
202
|
+
"w-10 h-10 rounded-lg flex items-center justify-center",
|
|
203
|
+
"bg-muted"
|
|
204
|
+
)}>
|
|
205
|
+
<Database className={cn("h-5 w-5", typeConf.color)} />
|
|
206
|
+
</div>
|
|
207
|
+
<div>
|
|
208
|
+
<CardTitle className="text-base flex items-center gap-2">
|
|
209
|
+
{database.name}
|
|
210
|
+
<Badge className={cn("text-white text-xs", statusConf.bgColor)}>
|
|
211
|
+
{statusConf.label}
|
|
212
|
+
</Badge>
|
|
213
|
+
</CardTitle>
|
|
214
|
+
<div className="flex items-center gap-2 mt-1 text-xs text-muted-foreground">
|
|
215
|
+
<span>{typeConf.label}</span>
|
|
216
|
+
{database.version && <span>v{database.version}</span>}
|
|
217
|
+
<span>•</span>
|
|
218
|
+
<span>{database.host}:{database.port}</span>
|
|
219
|
+
{database.role !== "standalone" && (
|
|
220
|
+
<>
|
|
221
|
+
<span>•</span>
|
|
222
|
+
<Badge variant="outline" className="text-xs">
|
|
223
|
+
<GitBranch className="h-3 w-3 mr-1" />
|
|
224
|
+
{database.role}
|
|
225
|
+
</Badge>
|
|
226
|
+
</>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{onRefresh && (
|
|
233
|
+
<Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={onRefresh}>
|
|
234
|
+
<RefreshCw className="h-4 w-4" />
|
|
235
|
+
</Button>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
</CardHeader>
|
|
239
|
+
|
|
240
|
+
<CardContent className="space-y-4">
|
|
241
|
+
{/* Key metrics row */}
|
|
242
|
+
<div className="grid grid-cols-4 gap-3">
|
|
243
|
+
<div className="text-center p-2 bg-muted/50 rounded-lg">
|
|
244
|
+
<div className="flex items-center justify-center gap-1 text-xs text-muted-foreground mb-1">
|
|
245
|
+
<Users className="h-3 w-3" />
|
|
246
|
+
Connections
|
|
247
|
+
</div>
|
|
248
|
+
<div className="text-lg font-semibold">{metrics.connectionsActive}</div>
|
|
249
|
+
<div className="text-xs text-muted-foreground">/ {metrics.connectionsMax}</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div className="text-center p-2 bg-muted/50 rounded-lg">
|
|
253
|
+
<div className="flex items-center justify-center gap-1 text-xs text-muted-foreground mb-1">
|
|
254
|
+
<Zap className="h-3 w-3" />
|
|
255
|
+
Queries/sec
|
|
256
|
+
</div>
|
|
257
|
+
<div className="text-lg font-semibold">{metrics.queriesPerSecond}</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<div className="text-center p-2 bg-muted/50 rounded-lg">
|
|
261
|
+
<div className="flex items-center justify-center gap-1 text-xs text-muted-foreground mb-1">
|
|
262
|
+
<Clock className="h-3 w-3" />
|
|
263
|
+
Avg Latency
|
|
264
|
+
</div>
|
|
265
|
+
<div className="text-lg font-semibold">{formatMs(metrics.avgQueryTime)}</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<div className="text-center p-2 bg-muted/50 rounded-lg">
|
|
269
|
+
<div className="flex items-center justify-center gap-1 text-xs text-muted-foreground mb-1">
|
|
270
|
+
<AlertTriangle className="h-3 w-3" />
|
|
271
|
+
Slow Queries
|
|
272
|
+
</div>
|
|
273
|
+
<div className={cn(
|
|
274
|
+
"text-lg font-semibold",
|
|
275
|
+
metrics.slowQueries > 10 && "text-red-500",
|
|
276
|
+
metrics.slowQueries > 0 && metrics.slowQueries <= 10 && "text-yellow-500"
|
|
277
|
+
)}>
|
|
278
|
+
{metrics.slowQueries}
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
{/* Connection usage */}
|
|
284
|
+
<div>
|
|
285
|
+
<div className="flex items-center justify-between text-sm mb-1">
|
|
286
|
+
<span className="flex items-center gap-1 text-muted-foreground">
|
|
287
|
+
<Users className="h-4 w-4" />
|
|
288
|
+
Connections
|
|
289
|
+
</span>
|
|
290
|
+
<span className={cn(
|
|
291
|
+
connectionPercent > 80 && "text-red-500",
|
|
292
|
+
connectionPercent > 60 && connectionPercent <= 80 && "text-yellow-500"
|
|
293
|
+
)}>
|
|
294
|
+
{connectionPercent.toFixed(0)}%
|
|
295
|
+
</span>
|
|
296
|
+
</div>
|
|
297
|
+
<Progress
|
|
298
|
+
value={connectionPercent}
|
|
299
|
+
className={cn(
|
|
300
|
+
"h-2",
|
|
301
|
+
connectionPercent > 80 && "[&>div]:bg-red-500",
|
|
302
|
+
connectionPercent > 60 && connectionPercent <= 80 && "[&>div]:bg-yellow-500"
|
|
303
|
+
)}
|
|
304
|
+
/>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
{/* Storage usage */}
|
|
308
|
+
<div>
|
|
309
|
+
<div className="flex items-center justify-between text-sm mb-1">
|
|
310
|
+
<span className="flex items-center gap-1 text-muted-foreground">
|
|
311
|
+
<HardDrive className="h-4 w-4" />
|
|
312
|
+
Storage
|
|
313
|
+
</span>
|
|
314
|
+
<span>
|
|
315
|
+
{formatBytes(metrics.storageUsed)} / {formatBytes(metrics.storageTotal)}
|
|
316
|
+
</span>
|
|
317
|
+
</div>
|
|
318
|
+
<Progress
|
|
319
|
+
value={storagePercent}
|
|
320
|
+
className={cn(
|
|
321
|
+
"h-2",
|
|
322
|
+
storagePercent > 90 && "[&>div]:bg-red-500",
|
|
323
|
+
storagePercent > 75 && storagePercent <= 90 && "[&>div]:bg-yellow-500"
|
|
324
|
+
)}
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
{/* Detailed metrics */}
|
|
329
|
+
{detailed && (
|
|
330
|
+
<div className="grid grid-cols-2 gap-3 pt-2 border-t">
|
|
331
|
+
{metrics.cacheHitRate !== undefined && (
|
|
332
|
+
<div className="flex items-center justify-between text-sm">
|
|
333
|
+
<span className="text-muted-foreground">Cache Hit Rate</span>
|
|
334
|
+
<span className={cn(
|
|
335
|
+
metrics.cacheHitRate < 90 && "text-yellow-500"
|
|
336
|
+
)}>
|
|
337
|
+
{metrics.cacheHitRate.toFixed(1)}%
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
)}
|
|
341
|
+
|
|
342
|
+
{metrics.replicationLag !== undefined && (
|
|
343
|
+
<div className="flex items-center justify-between text-sm">
|
|
344
|
+
<span className="text-muted-foreground">Replication Lag</span>
|
|
345
|
+
<span className={cn(
|
|
346
|
+
metrics.replicationLag > 1000 && "text-red-500",
|
|
347
|
+
metrics.replicationLag > 100 && metrics.replicationLag <= 1000 && "text-yellow-500"
|
|
348
|
+
)}>
|
|
349
|
+
{formatMs(metrics.replicationLag)}
|
|
350
|
+
</span>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
|
|
354
|
+
{metrics.uptime !== undefined && (
|
|
355
|
+
<div className="flex items-center justify-between text-sm">
|
|
356
|
+
<span className="text-muted-foreground">Uptime</span>
|
|
357
|
+
<span>{formatDuration(metrics.uptime)}</span>
|
|
358
|
+
</div>
|
|
359
|
+
)}
|
|
360
|
+
|
|
361
|
+
{database.replicas !== undefined && (
|
|
362
|
+
<div className="flex items-center justify-between text-sm">
|
|
363
|
+
<span className="text-muted-foreground">Replicas</span>
|
|
364
|
+
<span>{database.replicas}</span>
|
|
365
|
+
</div>
|
|
366
|
+
)}
|
|
367
|
+
|
|
368
|
+
{database.lastBackup && (
|
|
369
|
+
<div className="flex items-center justify-between text-sm col-span-2">
|
|
370
|
+
<span className="text-muted-foreground">Last Backup</span>
|
|
371
|
+
<span className="flex items-center gap-1">
|
|
372
|
+
<CheckCircle2 className="h-3 w-3 text-green-500" />
|
|
373
|
+
{database.lastBackup.toLocaleDateString("fr-FR", {
|
|
374
|
+
day: "2-digit",
|
|
375
|
+
month: "short",
|
|
376
|
+
hour: "2-digit",
|
|
377
|
+
minute: "2-digit",
|
|
378
|
+
})}
|
|
379
|
+
</span>
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
</div>
|
|
383
|
+
)}
|
|
384
|
+
|
|
385
|
+
{/* Tags */}
|
|
386
|
+
{database.tags && database.tags.length > 0 && (
|
|
387
|
+
<div className="flex flex-wrap gap-1 pt-2 border-t">
|
|
388
|
+
{database.tags.map((tag) => (
|
|
389
|
+
<Badge key={tag} variant="secondary" className="text-xs">
|
|
390
|
+
{tag}
|
|
391
|
+
</Badge>
|
|
392
|
+
))}
|
|
393
|
+
</div>
|
|
394
|
+
)}
|
|
395
|
+
</CardContent>
|
|
396
|
+
</Card>
|
|
397
|
+
)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Default sample database for demo
|
|
401
|
+
export const defaultDatabase: DatabaseInfo = {
|
|
402
|
+
id: "1",
|
|
403
|
+
name: "production-db",
|
|
404
|
+
type: "postgresql",
|
|
405
|
+
version: "15.4",
|
|
406
|
+
host: "db.example.com",
|
|
407
|
+
port: 5432,
|
|
408
|
+
status: "healthy",
|
|
409
|
+
role: "primary",
|
|
410
|
+
metrics: {
|
|
411
|
+
connectionsActive: 45,
|
|
412
|
+
connectionsMax: 100,
|
|
413
|
+
queriesPerSecond: 1250,
|
|
414
|
+
slowQueries: 3,
|
|
415
|
+
avgQueryTime: 12.5,
|
|
416
|
+
cacheHitRate: 98.5,
|
|
417
|
+
storageUsed: 85 * 1024 * 1024 * 1024,
|
|
418
|
+
storageTotal: 200 * 1024 * 1024 * 1024,
|
|
419
|
+
replicationLag: 25,
|
|
420
|
+
uptime: 45 * 24 * 3600,
|
|
421
|
+
},
|
|
422
|
+
replicas: 2,
|
|
423
|
+
lastBackup: new Date(Date.now() - 4 * 3600000),
|
|
424
|
+
tags: ["production", "critical"],
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Sample databases list
|
|
428
|
+
export const defaultDatabases: DatabaseInfo[] = [
|
|
429
|
+
defaultDatabase,
|
|
430
|
+
{
|
|
431
|
+
id: "2",
|
|
432
|
+
name: "cache-redis",
|
|
433
|
+
type: "redis",
|
|
434
|
+
version: "7.2",
|
|
435
|
+
host: "redis.example.com",
|
|
436
|
+
port: 6379,
|
|
437
|
+
status: "healthy",
|
|
438
|
+
role: "primary",
|
|
439
|
+
metrics: {
|
|
440
|
+
connectionsActive: 120,
|
|
441
|
+
connectionsMax: 10000,
|
|
442
|
+
queriesPerSecond: 45000,
|
|
443
|
+
slowQueries: 0,
|
|
444
|
+
avgQueryTime: 0.5,
|
|
445
|
+
storageUsed: 2 * 1024 * 1024 * 1024,
|
|
446
|
+
storageTotal: 16 * 1024 * 1024 * 1024,
|
|
447
|
+
uptime: 30 * 24 * 3600,
|
|
448
|
+
},
|
|
449
|
+
tags: ["cache", "production"],
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
id: "3",
|
|
453
|
+
name: "analytics-mongo",
|
|
454
|
+
type: "mongodb",
|
|
455
|
+
version: "7.0",
|
|
456
|
+
host: "mongo.example.com",
|
|
457
|
+
port: 27017,
|
|
458
|
+
status: "warning",
|
|
459
|
+
role: "replica",
|
|
460
|
+
metrics: {
|
|
461
|
+
connectionsActive: 78,
|
|
462
|
+
connectionsMax: 100,
|
|
463
|
+
queriesPerSecond: 350,
|
|
464
|
+
slowQueries: 12,
|
|
465
|
+
avgQueryTime: 85,
|
|
466
|
+
storageUsed: 450 * 1024 * 1024 * 1024,
|
|
467
|
+
storageTotal: 500 * 1024 * 1024 * 1024,
|
|
468
|
+
replicationLag: 150,
|
|
469
|
+
uptime: 15 * 24 * 3600,
|
|
470
|
+
},
|
|
471
|
+
tags: ["analytics"],
|
|
472
|
+
},
|
|
473
|
+
]
|