@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,528 @@
|
|
|
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 { Progress } from "../progress"
|
|
9
|
+
import { ScrollArea } from "../scroll-area"
|
|
10
|
+
import {
|
|
11
|
+
Tooltip,
|
|
12
|
+
TooltipContent,
|
|
13
|
+
TooltipProvider,
|
|
14
|
+
TooltipTrigger,
|
|
15
|
+
} from "../tooltip"
|
|
16
|
+
import {
|
|
17
|
+
Box,
|
|
18
|
+
CheckCircle2,
|
|
19
|
+
XCircle,
|
|
20
|
+
AlertTriangle,
|
|
21
|
+
Clock,
|
|
22
|
+
Cpu,
|
|
23
|
+
HardDrive,
|
|
24
|
+
RotateCcw,
|
|
25
|
+
Terminal,
|
|
26
|
+
FileText,
|
|
27
|
+
Trash2,
|
|
28
|
+
ChevronDown,
|
|
29
|
+
ChevronRight,
|
|
30
|
+
Container,
|
|
31
|
+
Activity,
|
|
32
|
+
} from "lucide-react"
|
|
33
|
+
|
|
34
|
+
export type PodPhase = "Running" | "Pending" | "Succeeded" | "Failed" | "Unknown"
|
|
35
|
+
export type ContainerState = "running" | "waiting" | "terminated"
|
|
36
|
+
|
|
37
|
+
export interface PodContainer {
|
|
38
|
+
name: string
|
|
39
|
+
image: string
|
|
40
|
+
state: ContainerState
|
|
41
|
+
ready: boolean
|
|
42
|
+
restartCount: number
|
|
43
|
+
startedAt?: Date
|
|
44
|
+
reason?: string
|
|
45
|
+
message?: string
|
|
46
|
+
resources?: {
|
|
47
|
+
cpu: { used: number; limit: number }
|
|
48
|
+
memory: { used: number; limit: number }
|
|
49
|
+
}
|
|
50
|
+
ports?: Array<{ containerPort: number; protocol: string }>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PodEvent {
|
|
54
|
+
type: "Normal" | "Warning"
|
|
55
|
+
reason: string
|
|
56
|
+
message: string
|
|
57
|
+
timestamp: Date
|
|
58
|
+
count?: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PodDetails {
|
|
62
|
+
name: string
|
|
63
|
+
namespace: string
|
|
64
|
+
phase: PodPhase
|
|
65
|
+
nodeName?: string
|
|
66
|
+
podIP?: string
|
|
67
|
+
hostIP?: string
|
|
68
|
+
startTime?: Date
|
|
69
|
+
containers: PodContainer[]
|
|
70
|
+
events?: PodEvent[]
|
|
71
|
+
labels?: Record<string, string>
|
|
72
|
+
annotations?: Record<string, string>
|
|
73
|
+
conditions?: Array<{
|
|
74
|
+
type: string
|
|
75
|
+
status: boolean
|
|
76
|
+
reason?: string
|
|
77
|
+
message?: string
|
|
78
|
+
lastTransitionTime?: Date
|
|
79
|
+
}>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WakaPodCardProps {
|
|
83
|
+
/** Pod details */
|
|
84
|
+
pod: PodDetails
|
|
85
|
+
/** Callback when viewing logs */
|
|
86
|
+
onViewLogs?: (containerName: string) => void
|
|
87
|
+
/** Callback when opening terminal */
|
|
88
|
+
onExec?: (containerName: string) => void
|
|
89
|
+
/** Callback when deleting pod */
|
|
90
|
+
onDelete?: () => void
|
|
91
|
+
/** Callback when restarting (deleting for recreation) */
|
|
92
|
+
onRestart?: () => void
|
|
93
|
+
/** Show events section */
|
|
94
|
+
showEvents?: boolean
|
|
95
|
+
/** Compact mode */
|
|
96
|
+
compact?: boolean
|
|
97
|
+
/** Custom class name */
|
|
98
|
+
className?: string
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const phaseConfig: Record<PodPhase, { color: string; bgColor: string; icon: React.ElementType }> = {
|
|
102
|
+
Running: { color: "text-green-500", bgColor: "bg-green-500/10", icon: CheckCircle2 },
|
|
103
|
+
Pending: { color: "text-yellow-500", bgColor: "bg-yellow-500/10", icon: Clock },
|
|
104
|
+
Succeeded: { color: "text-blue-500", bgColor: "bg-blue-500/10", icon: CheckCircle2 },
|
|
105
|
+
Failed: { color: "text-red-500", bgColor: "bg-red-500/10", icon: XCircle },
|
|
106
|
+
Unknown: { color: "text-gray-500", bgColor: "bg-gray-500/10", icon: AlertTriangle },
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const containerStateConfig: Record<ContainerState, { color: string; label: string }> = {
|
|
110
|
+
running: { color: "text-green-500", label: "Running" },
|
|
111
|
+
waiting: { color: "text-yellow-500", label: "Waiting" },
|
|
112
|
+
terminated: { color: "text-gray-500", label: "Terminated" },
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatBytes(bytes: number): string {
|
|
116
|
+
if (bytes === 0) return "0 B"
|
|
117
|
+
const k = 1024
|
|
118
|
+
const sizes = ["B", "KB", "MB", "GB"]
|
|
119
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
120
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function formatAge(date?: Date): string {
|
|
124
|
+
if (!date) return "-"
|
|
125
|
+
const diff = Date.now() - date.getTime()
|
|
126
|
+
const minutes = Math.floor(diff / 60000)
|
|
127
|
+
const hours = Math.floor(minutes / 60)
|
|
128
|
+
const days = Math.floor(hours / 24)
|
|
129
|
+
|
|
130
|
+
if (days > 0) return `${days}d ago`
|
|
131
|
+
if (hours > 0) return `${hours}h ago`
|
|
132
|
+
return `${minutes}m ago`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ContainerCard({
|
|
136
|
+
container,
|
|
137
|
+
onViewLogs,
|
|
138
|
+
onExec,
|
|
139
|
+
}: {
|
|
140
|
+
container: PodContainer
|
|
141
|
+
onViewLogs?: (name: string) => void
|
|
142
|
+
onExec?: (name: string) => void
|
|
143
|
+
}) {
|
|
144
|
+
const stateConfig = containerStateConfig[container.state]
|
|
145
|
+
const [expanded, setExpanded] = React.useState(false)
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="border rounded-lg overflow-hidden">
|
|
149
|
+
<div
|
|
150
|
+
className={cn(
|
|
151
|
+
"flex items-center gap-3 p-3 cursor-pointer hover:bg-muted/50 transition-colors",
|
|
152
|
+
!container.ready && "bg-yellow-500/5"
|
|
153
|
+
)}
|
|
154
|
+
onClick={() => setExpanded(!expanded)}
|
|
155
|
+
>
|
|
156
|
+
<button className="text-muted-foreground">
|
|
157
|
+
{expanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
|
|
158
|
+
</button>
|
|
159
|
+
|
|
160
|
+
<Container className="h-4 w-4 text-muted-foreground" />
|
|
161
|
+
|
|
162
|
+
<div className="flex-1 min-w-0">
|
|
163
|
+
<div className="font-medium truncate">{container.name}</div>
|
|
164
|
+
<div className="text-xs text-muted-foreground truncate">{container.image}</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<Badge variant="outline" className={stateConfig.color}>
|
|
168
|
+
{stateConfig.label}
|
|
169
|
+
</Badge>
|
|
170
|
+
|
|
171
|
+
{container.restartCount > 0 && (
|
|
172
|
+
<TooltipProvider>
|
|
173
|
+
<Tooltip>
|
|
174
|
+
<TooltipTrigger>
|
|
175
|
+
<Badge variant="secondary" className="text-xs">
|
|
176
|
+
<RotateCcw className="h-3 w-3 mr-1" />
|
|
177
|
+
{container.restartCount}
|
|
178
|
+
</Badge>
|
|
179
|
+
</TooltipTrigger>
|
|
180
|
+
<TooltipContent>Restart count</TooltipContent>
|
|
181
|
+
</Tooltip>
|
|
182
|
+
</TooltipProvider>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
<div className="flex items-center gap-1">
|
|
186
|
+
<TooltipProvider>
|
|
187
|
+
<Tooltip>
|
|
188
|
+
<TooltipTrigger asChild>
|
|
189
|
+
<Button
|
|
190
|
+
variant="ghost"
|
|
191
|
+
size="sm"
|
|
192
|
+
className="h-7 w-7 p-0"
|
|
193
|
+
onClick={(e) => {
|
|
194
|
+
e.stopPropagation()
|
|
195
|
+
onViewLogs?.(container.name)
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<FileText className="h-4 w-4" />
|
|
199
|
+
</Button>
|
|
200
|
+
</TooltipTrigger>
|
|
201
|
+
<TooltipContent>View Logs</TooltipContent>
|
|
202
|
+
</Tooltip>
|
|
203
|
+
</TooltipProvider>
|
|
204
|
+
|
|
205
|
+
{container.state === "running" && (
|
|
206
|
+
<TooltipProvider>
|
|
207
|
+
<Tooltip>
|
|
208
|
+
<TooltipTrigger asChild>
|
|
209
|
+
<Button
|
|
210
|
+
variant="ghost"
|
|
211
|
+
size="sm"
|
|
212
|
+
className="h-7 w-7 p-0"
|
|
213
|
+
onClick={(e) => {
|
|
214
|
+
e.stopPropagation()
|
|
215
|
+
onExec?.(container.name)
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<Terminal className="h-4 w-4" />
|
|
219
|
+
</Button>
|
|
220
|
+
</TooltipTrigger>
|
|
221
|
+
<TooltipContent>Open Terminal</TooltipContent>
|
|
222
|
+
</Tooltip>
|
|
223
|
+
</TooltipProvider>
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
{expanded && (
|
|
229
|
+
<div className="border-t bg-muted/20 p-3 space-y-3">
|
|
230
|
+
{/* Resources */}
|
|
231
|
+
{container.resources && (
|
|
232
|
+
<div className="grid grid-cols-2 gap-4">
|
|
233
|
+
<div className="space-y-1">
|
|
234
|
+
<div className="flex items-center justify-between text-xs">
|
|
235
|
+
<span className="flex items-center gap-1">
|
|
236
|
+
<Cpu className="h-3 w-3" /> CPU
|
|
237
|
+
</span>
|
|
238
|
+
<span>
|
|
239
|
+
{container.resources.cpu.used}m / {container.resources.cpu.limit}m
|
|
240
|
+
</span>
|
|
241
|
+
</div>
|
|
242
|
+
<Progress
|
|
243
|
+
value={(container.resources.cpu.used / container.resources.cpu.limit) * 100}
|
|
244
|
+
className="h-1.5"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
<div className="space-y-1">
|
|
248
|
+
<div className="flex items-center justify-between text-xs">
|
|
249
|
+
<span className="flex items-center gap-1">
|
|
250
|
+
<HardDrive className="h-3 w-3" /> Memory
|
|
251
|
+
</span>
|
|
252
|
+
<span>
|
|
253
|
+
{formatBytes(container.resources.memory.used)} / {formatBytes(container.resources.memory.limit)}
|
|
254
|
+
</span>
|
|
255
|
+
</div>
|
|
256
|
+
<Progress
|
|
257
|
+
value={(container.resources.memory.used / container.resources.memory.limit) * 100}
|
|
258
|
+
className="h-1.5"
|
|
259
|
+
/>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{/* Ports */}
|
|
265
|
+
{container.ports && container.ports.length > 0 && (
|
|
266
|
+
<div>
|
|
267
|
+
<div className="text-xs text-muted-foreground mb-1">Ports</div>
|
|
268
|
+
<div className="flex flex-wrap gap-1">
|
|
269
|
+
{container.ports.map((port, i) => (
|
|
270
|
+
<Badge key={i} variant="outline" className="text-xs">
|
|
271
|
+
{port.containerPort}/{port.protocol}
|
|
272
|
+
</Badge>
|
|
273
|
+
))}
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{/* State details */}
|
|
279
|
+
{(container.reason || container.message) && (
|
|
280
|
+
<div className="text-xs">
|
|
281
|
+
{container.reason && (
|
|
282
|
+
<div className="font-medium text-yellow-500">{container.reason}</div>
|
|
283
|
+
)}
|
|
284
|
+
{container.message && (
|
|
285
|
+
<div className="text-muted-foreground">{container.message}</div>
|
|
286
|
+
)}
|
|
287
|
+
</div>
|
|
288
|
+
)}
|
|
289
|
+
|
|
290
|
+
{/* Started time */}
|
|
291
|
+
{container.startedAt && (
|
|
292
|
+
<div className="text-xs text-muted-foreground">
|
|
293
|
+
Started: {container.startedAt.toLocaleString()}
|
|
294
|
+
</div>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function WakaPodCard({
|
|
303
|
+
pod,
|
|
304
|
+
onViewLogs,
|
|
305
|
+
onExec,
|
|
306
|
+
onDelete,
|
|
307
|
+
onRestart,
|
|
308
|
+
showEvents = true,
|
|
309
|
+
compact = false,
|
|
310
|
+
className,
|
|
311
|
+
}: WakaPodCardProps) {
|
|
312
|
+
const config = phaseConfig[pod.phase]
|
|
313
|
+
const PhaseIcon = config.icon
|
|
314
|
+
const readyContainers = pod.containers.filter((c) => c.ready).length
|
|
315
|
+
const totalRestarts = pod.containers.reduce((acc, c) => acc + c.restartCount, 0)
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<Card className={cn(config.bgColor, className)}>
|
|
319
|
+
<CardHeader className="pb-3">
|
|
320
|
+
<div className="flex items-start justify-between">
|
|
321
|
+
<div className="flex items-center gap-3">
|
|
322
|
+
<Box className="h-5 w-5 text-muted-foreground" />
|
|
323
|
+
<div>
|
|
324
|
+
<CardTitle className="text-base">{pod.name}</CardTitle>
|
|
325
|
+
<div className="text-sm text-muted-foreground">{pod.namespace}</div>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div className="flex items-center gap-2">
|
|
330
|
+
<Badge variant="outline" className={config.color}>
|
|
331
|
+
<PhaseIcon className="h-3 w-3 mr-1" />
|
|
332
|
+
{pod.phase}
|
|
333
|
+
</Badge>
|
|
334
|
+
|
|
335
|
+
<div className="flex items-center gap-1">
|
|
336
|
+
{onRestart && (
|
|
337
|
+
<TooltipProvider>
|
|
338
|
+
<Tooltip>
|
|
339
|
+
<TooltipTrigger asChild>
|
|
340
|
+
<Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={onRestart}>
|
|
341
|
+
<RotateCcw className="h-4 w-4" />
|
|
342
|
+
</Button>
|
|
343
|
+
</TooltipTrigger>
|
|
344
|
+
<TooltipContent>Restart Pod</TooltipContent>
|
|
345
|
+
</Tooltip>
|
|
346
|
+
</TooltipProvider>
|
|
347
|
+
)}
|
|
348
|
+
{onDelete && (
|
|
349
|
+
<TooltipProvider>
|
|
350
|
+
<Tooltip>
|
|
351
|
+
<TooltipTrigger asChild>
|
|
352
|
+
<Button
|
|
353
|
+
variant="ghost"
|
|
354
|
+
size="sm"
|
|
355
|
+
className="h-8 w-8 p-0 text-destructive"
|
|
356
|
+
onClick={onDelete}
|
|
357
|
+
>
|
|
358
|
+
<Trash2 className="h-4 w-4" />
|
|
359
|
+
</Button>
|
|
360
|
+
</TooltipTrigger>
|
|
361
|
+
<TooltipContent>Delete Pod</TooltipContent>
|
|
362
|
+
</Tooltip>
|
|
363
|
+
</TooltipProvider>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
{/* Quick stats */}
|
|
370
|
+
<div className="flex items-center gap-4 text-sm text-muted-foreground mt-2">
|
|
371
|
+
<span>Ready: {readyContainers}/{pod.containers.length}</span>
|
|
372
|
+
<span>Restarts: {totalRestarts}</span>
|
|
373
|
+
{pod.nodeName && <span>Node: {pod.nodeName}</span>}
|
|
374
|
+
{pod.startTime && <span>Age: {formatAge(pod.startTime)}</span>}
|
|
375
|
+
</div>
|
|
376
|
+
</CardHeader>
|
|
377
|
+
|
|
378
|
+
<CardContent className="space-y-4">
|
|
379
|
+
{/* Pod IPs */}
|
|
380
|
+
{(pod.podIP || pod.hostIP) && (
|
|
381
|
+
<div className="flex items-center gap-4 text-sm">
|
|
382
|
+
{pod.podIP && (
|
|
383
|
+
<div>
|
|
384
|
+
<span className="text-muted-foreground">Pod IP: </span>
|
|
385
|
+
<code className="bg-muted px-1.5 py-0.5 rounded text-xs">{pod.podIP}</code>
|
|
386
|
+
</div>
|
|
387
|
+
)}
|
|
388
|
+
{pod.hostIP && (
|
|
389
|
+
<div>
|
|
390
|
+
<span className="text-muted-foreground">Host IP: </span>
|
|
391
|
+
<code className="bg-muted px-1.5 py-0.5 rounded text-xs">{pod.hostIP}</code>
|
|
392
|
+
</div>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
395
|
+
)}
|
|
396
|
+
|
|
397
|
+
{/* Containers */}
|
|
398
|
+
<div className="space-y-2">
|
|
399
|
+
<div className="text-sm font-medium">Containers ({pod.containers.length})</div>
|
|
400
|
+
{pod.containers.map((container) => (
|
|
401
|
+
<ContainerCard
|
|
402
|
+
key={container.name}
|
|
403
|
+
container={container}
|
|
404
|
+
onViewLogs={onViewLogs}
|
|
405
|
+
onExec={onExec}
|
|
406
|
+
/>
|
|
407
|
+
))}
|
|
408
|
+
</div>
|
|
409
|
+
|
|
410
|
+
{/* Events */}
|
|
411
|
+
{showEvents && pod.events && pod.events.length > 0 && (
|
|
412
|
+
<div className="space-y-2">
|
|
413
|
+
<div className="text-sm font-medium flex items-center gap-2">
|
|
414
|
+
<Activity className="h-4 w-4" />
|
|
415
|
+
Events
|
|
416
|
+
</div>
|
|
417
|
+
<ScrollArea className="h-32">
|
|
418
|
+
<div className="space-y-2">
|
|
419
|
+
{pod.events.map((event, i) => (
|
|
420
|
+
<div
|
|
421
|
+
key={i}
|
|
422
|
+
className={cn(
|
|
423
|
+
"text-xs p-2 rounded border",
|
|
424
|
+
event.type === "Warning"
|
|
425
|
+
? "bg-yellow-500/10 border-yellow-500/30"
|
|
426
|
+
: "bg-muted/50"
|
|
427
|
+
)}
|
|
428
|
+
>
|
|
429
|
+
<div className="flex items-center justify-between mb-1">
|
|
430
|
+
<span className="font-medium">{event.reason}</span>
|
|
431
|
+
<span className="text-muted-foreground">{formatAge(event.timestamp)}</span>
|
|
432
|
+
</div>
|
|
433
|
+
<div className="text-muted-foreground">{event.message}</div>
|
|
434
|
+
</div>
|
|
435
|
+
))}
|
|
436
|
+
</div>
|
|
437
|
+
</ScrollArea>
|
|
438
|
+
</div>
|
|
439
|
+
)}
|
|
440
|
+
|
|
441
|
+
{/* Labels */}
|
|
442
|
+
{pod.labels && Object.keys(pod.labels).length > 0 && !compact && (
|
|
443
|
+
<div className="space-y-1">
|
|
444
|
+
<div className="text-xs text-muted-foreground">Labels</div>
|
|
445
|
+
<div className="flex flex-wrap gap-1">
|
|
446
|
+
{Object.entries(pod.labels).slice(0, 5).map(([key, value]) => (
|
|
447
|
+
<Badge key={key} variant="outline" className="text-xs">
|
|
448
|
+
{key}: {value}
|
|
449
|
+
</Badge>
|
|
450
|
+
))}
|
|
451
|
+
{Object.keys(pod.labels).length > 5 && (
|
|
452
|
+
<Badge variant="outline" className="text-xs">
|
|
453
|
+
+{Object.keys(pod.labels).length - 5} more
|
|
454
|
+
</Badge>
|
|
455
|
+
)}
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
)}
|
|
459
|
+
</CardContent>
|
|
460
|
+
</Card>
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Default sample pod for demo
|
|
465
|
+
export const defaultPodDetails: PodDetails = {
|
|
466
|
+
name: "api-server-7d8f9b6c4d-x2k9l",
|
|
467
|
+
namespace: "default",
|
|
468
|
+
phase: "Running",
|
|
469
|
+
nodeName: "node-2",
|
|
470
|
+
podIP: "10.244.1.15",
|
|
471
|
+
hostIP: "192.168.1.10",
|
|
472
|
+
startTime: new Date(Date.now() - 5 * 24 * 3600000),
|
|
473
|
+
containers: [
|
|
474
|
+
{
|
|
475
|
+
name: "api",
|
|
476
|
+
image: "myapp/api:v1.2.3",
|
|
477
|
+
state: "running",
|
|
478
|
+
ready: true,
|
|
479
|
+
restartCount: 0,
|
|
480
|
+
startedAt: new Date(Date.now() - 5 * 24 * 3600000),
|
|
481
|
+
resources: {
|
|
482
|
+
cpu: { used: 150, limit: 500 },
|
|
483
|
+
memory: { used: 256 * 1024 * 1024, limit: 512 * 1024 * 1024 },
|
|
484
|
+
},
|
|
485
|
+
ports: [
|
|
486
|
+
{ containerPort: 8080, protocol: "TCP" },
|
|
487
|
+
{ containerPort: 9090, protocol: "TCP" },
|
|
488
|
+
],
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: "sidecar",
|
|
492
|
+
image: "envoyproxy/envoy:v1.28",
|
|
493
|
+
state: "running",
|
|
494
|
+
ready: true,
|
|
495
|
+
restartCount: 1,
|
|
496
|
+
startedAt: new Date(Date.now() - 5 * 24 * 3600000),
|
|
497
|
+
resources: {
|
|
498
|
+
cpu: { used: 50, limit: 200 },
|
|
499
|
+
memory: { used: 64 * 1024 * 1024, limit: 128 * 1024 * 1024 },
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
events: [
|
|
504
|
+
{
|
|
505
|
+
type: "Normal",
|
|
506
|
+
reason: "Scheduled",
|
|
507
|
+
message: "Successfully assigned default/api-server-7d8f9b6c4d-x2k9l to node-2",
|
|
508
|
+
timestamp: new Date(Date.now() - 5 * 24 * 3600000),
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
type: "Normal",
|
|
512
|
+
reason: "Pulled",
|
|
513
|
+
message: "Container image \"myapp/api:v1.2.3\" already present on machine",
|
|
514
|
+
timestamp: new Date(Date.now() - 5 * 24 * 3600000),
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
type: "Normal",
|
|
518
|
+
reason: "Started",
|
|
519
|
+
message: "Started container api",
|
|
520
|
+
timestamp: new Date(Date.now() - 5 * 24 * 3600000),
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
labels: {
|
|
524
|
+
app: "api-server",
|
|
525
|
+
version: "v1.2.3",
|
|
526
|
+
environment: "production",
|
|
527
|
+
},
|
|
528
|
+
}
|