@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,396 @@
|
|
|
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 { ScrollArea } from "../scroll-area"
|
|
8
|
+
import {
|
|
9
|
+
Tooltip,
|
|
10
|
+
TooltipContent,
|
|
11
|
+
TooltipProvider,
|
|
12
|
+
TooltipTrigger,
|
|
13
|
+
} from "../tooltip"
|
|
14
|
+
import {
|
|
15
|
+
CheckCircle2,
|
|
16
|
+
XCircle,
|
|
17
|
+
Loader2,
|
|
18
|
+
Clock,
|
|
19
|
+
SkipForward,
|
|
20
|
+
RefreshCw,
|
|
21
|
+
Grid3X3,
|
|
22
|
+
ChevronDown,
|
|
23
|
+
ChevronRight,
|
|
24
|
+
ExternalLink,
|
|
25
|
+
} from "lucide-react"
|
|
26
|
+
|
|
27
|
+
export type BuildStatus = "success" | "failed" | "running" | "pending" | "skipped" | "cancelled"
|
|
28
|
+
|
|
29
|
+
export interface BuildCell {
|
|
30
|
+
id: string
|
|
31
|
+
status: BuildStatus
|
|
32
|
+
duration?: number // in seconds
|
|
33
|
+
startedAt?: Date
|
|
34
|
+
finishedAt?: Date
|
|
35
|
+
url?: string
|
|
36
|
+
logs?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BuildMatrixRow {
|
|
40
|
+
label: string
|
|
41
|
+
description?: string
|
|
42
|
+
cells: BuildCell[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WakaBuildMatrixProps {
|
|
46
|
+
/** Column headers (e.g., OS names or versions) */
|
|
47
|
+
columns: string[]
|
|
48
|
+
/** Row data with cells */
|
|
49
|
+
rows: BuildMatrixRow[]
|
|
50
|
+
/** Callback when clicking on a cell */
|
|
51
|
+
onCellClick?: (rowIndex: number, colIndex: number, cell: BuildCell) => void
|
|
52
|
+
/** Callback when retrying a build */
|
|
53
|
+
onRetry?: (rowIndex: number, colIndex: number) => void
|
|
54
|
+
/** Callback when viewing logs */
|
|
55
|
+
onViewLogs?: (cell: BuildCell) => void
|
|
56
|
+
/** Title of the matrix */
|
|
57
|
+
title?: string
|
|
58
|
+
/** Show duration in cells */
|
|
59
|
+
showDuration?: boolean
|
|
60
|
+
/** Custom class name */
|
|
61
|
+
className?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const statusConfig: Record<BuildStatus, { icon: React.ElementType; color: string; bgColor: string; label: string }> = {
|
|
65
|
+
success: { icon: CheckCircle2, color: "text-green-500", bgColor: "bg-green-500", label: "Success" },
|
|
66
|
+
failed: { icon: XCircle, color: "text-red-500", bgColor: "bg-red-500", label: "Failed" },
|
|
67
|
+
running: { icon: Loader2, color: "text-blue-500", bgColor: "bg-blue-500", label: "Running" },
|
|
68
|
+
pending: { icon: Clock, color: "text-gray-400", bgColor: "bg-gray-400", label: "Pending" },
|
|
69
|
+
skipped: { icon: SkipForward, color: "text-gray-500", bgColor: "bg-gray-500", label: "Skipped" },
|
|
70
|
+
cancelled: { icon: XCircle, color: "text-orange-500", bgColor: "bg-orange-500", label: "Cancelled" },
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function formatDuration(seconds: number): string {
|
|
74
|
+
if (seconds < 60) return `${seconds}s`
|
|
75
|
+
const minutes = Math.floor(seconds / 60)
|
|
76
|
+
const secs = seconds % 60
|
|
77
|
+
if (minutes < 60) return `${minutes}m ${secs}s`
|
|
78
|
+
const hours = Math.floor(minutes / 60)
|
|
79
|
+
return `${hours}h ${minutes % 60}m`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function BuildCellComponent({
|
|
83
|
+
cell,
|
|
84
|
+
onClick,
|
|
85
|
+
showDuration,
|
|
86
|
+
}: {
|
|
87
|
+
cell: BuildCell
|
|
88
|
+
onClick?: () => void
|
|
89
|
+
showDuration: boolean
|
|
90
|
+
}) {
|
|
91
|
+
const config = statusConfig[cell.status]
|
|
92
|
+
const Icon = config.icon
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<TooltipProvider>
|
|
96
|
+
<Tooltip>
|
|
97
|
+
<TooltipTrigger asChild>
|
|
98
|
+
<button
|
|
99
|
+
className={cn(
|
|
100
|
+
"w-12 h-12 rounded-lg flex flex-col items-center justify-center gap-0.5 transition-all",
|
|
101
|
+
"hover:scale-105 hover:shadow-md cursor-pointer",
|
|
102
|
+
"border border-transparent",
|
|
103
|
+
cell.status === "success" && "bg-green-500/10 hover:border-green-500/50",
|
|
104
|
+
cell.status === "failed" && "bg-red-500/10 hover:border-red-500/50",
|
|
105
|
+
cell.status === "running" && "bg-blue-500/10 hover:border-blue-500/50",
|
|
106
|
+
cell.status === "pending" && "bg-gray-500/10 hover:border-gray-500/50",
|
|
107
|
+
cell.status === "skipped" && "bg-gray-500/5 hover:border-gray-500/30",
|
|
108
|
+
cell.status === "cancelled" && "bg-orange-500/10 hover:border-orange-500/50"
|
|
109
|
+
)}
|
|
110
|
+
onClick={onClick}
|
|
111
|
+
>
|
|
112
|
+
<Icon
|
|
113
|
+
className={cn(
|
|
114
|
+
"h-5 w-5",
|
|
115
|
+
config.color,
|
|
116
|
+
cell.status === "running" && "animate-spin"
|
|
117
|
+
)}
|
|
118
|
+
/>
|
|
119
|
+
{showDuration && cell.duration && (
|
|
120
|
+
<span className="text-[10px] text-muted-foreground">
|
|
121
|
+
{formatDuration(cell.duration)}
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
</button>
|
|
125
|
+
</TooltipTrigger>
|
|
126
|
+
<TooltipContent>
|
|
127
|
+
<div className="space-y-1">
|
|
128
|
+
<div className="font-medium">{config.label}</div>
|
|
129
|
+
{cell.duration && <div className="text-xs">Duration: {formatDuration(cell.duration)}</div>}
|
|
130
|
+
{cell.startedAt && (
|
|
131
|
+
<div className="text-xs">Started: {cell.startedAt.toLocaleString()}</div>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
</TooltipContent>
|
|
135
|
+
</Tooltip>
|
|
136
|
+
</TooltipProvider>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function WakaBuildMatrix({
|
|
141
|
+
columns,
|
|
142
|
+
rows,
|
|
143
|
+
onCellClick,
|
|
144
|
+
onRetry,
|
|
145
|
+
onViewLogs,
|
|
146
|
+
title = "Build Matrix",
|
|
147
|
+
showDuration = true,
|
|
148
|
+
className,
|
|
149
|
+
}: WakaBuildMatrixProps) {
|
|
150
|
+
const [expandedRows, setExpandedRows] = React.useState<Set<number>>(new Set())
|
|
151
|
+
|
|
152
|
+
const toggleRow = (index: number) => {
|
|
153
|
+
setExpandedRows((prev) => {
|
|
154
|
+
const next = new Set(prev)
|
|
155
|
+
if (next.has(index)) {
|
|
156
|
+
next.delete(index)
|
|
157
|
+
} else {
|
|
158
|
+
next.add(index)
|
|
159
|
+
}
|
|
160
|
+
return next
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Calculate summary stats
|
|
165
|
+
const stats = React.useMemo(() => {
|
|
166
|
+
let total = 0
|
|
167
|
+
let success = 0
|
|
168
|
+
let failed = 0
|
|
169
|
+
let running = 0
|
|
170
|
+
|
|
171
|
+
rows.forEach((row) => {
|
|
172
|
+
row.cells.forEach((cell) => {
|
|
173
|
+
total++
|
|
174
|
+
if (cell.status === "success") success++
|
|
175
|
+
if (cell.status === "failed") failed++
|
|
176
|
+
if (cell.status === "running") running++
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return { total, success, failed, running, passRate: total > 0 ? (success / total) * 100 : 0 }
|
|
181
|
+
}, [rows])
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className={cn("border rounded-lg bg-background", className)}>
|
|
185
|
+
{/* Header */}
|
|
186
|
+
<div className="flex items-center justify-between gap-4 p-3 border-b">
|
|
187
|
+
<div className="flex items-center gap-3">
|
|
188
|
+
<Grid3X3 className="h-5 w-5" />
|
|
189
|
+
<h3 className="font-semibold">{title}</h3>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<div className="flex items-center gap-2">
|
|
193
|
+
<Badge variant="secondary" className="text-xs">
|
|
194
|
+
{stats.total} jobs
|
|
195
|
+
</Badge>
|
|
196
|
+
{stats.success > 0 && (
|
|
197
|
+
<Badge className="bg-green-500 text-xs">{stats.success} passed</Badge>
|
|
198
|
+
)}
|
|
199
|
+
{stats.failed > 0 && (
|
|
200
|
+
<Badge className="bg-red-500 text-xs">{stats.failed} failed</Badge>
|
|
201
|
+
)}
|
|
202
|
+
{stats.running > 0 && (
|
|
203
|
+
<Badge className="bg-blue-500 text-xs">{stats.running} running</Badge>
|
|
204
|
+
)}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{/* Legend */}
|
|
209
|
+
<div className="flex items-center gap-4 px-3 py-2 border-b bg-muted/30 text-xs flex-wrap">
|
|
210
|
+
{Object.entries(statusConfig).map(([status, config]) => {
|
|
211
|
+
const Icon = config.icon
|
|
212
|
+
return (
|
|
213
|
+
<div key={status} className="flex items-center gap-1.5">
|
|
214
|
+
<Icon className={cn("h-3 w-3", config.color)} />
|
|
215
|
+
<span>{config.label}</span>
|
|
216
|
+
</div>
|
|
217
|
+
)
|
|
218
|
+
})}
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
{/* Matrix */}
|
|
222
|
+
<ScrollArea className="max-h-[500px]">
|
|
223
|
+
<div className="p-3">
|
|
224
|
+
<table className="w-full">
|
|
225
|
+
<thead>
|
|
226
|
+
<tr>
|
|
227
|
+
<th className="text-left p-2 text-sm font-medium text-muted-foreground w-48">
|
|
228
|
+
Configuration
|
|
229
|
+
</th>
|
|
230
|
+
{columns.map((col, i) => (
|
|
231
|
+
<th key={i} className="p-2 text-center text-sm font-medium text-muted-foreground">
|
|
232
|
+
{col}
|
|
233
|
+
</th>
|
|
234
|
+
))}
|
|
235
|
+
</tr>
|
|
236
|
+
</thead>
|
|
237
|
+
<tbody>
|
|
238
|
+
{rows.map((row, rowIndex) => {
|
|
239
|
+
const isExpanded = expandedRows.has(rowIndex)
|
|
240
|
+
const rowStats = {
|
|
241
|
+
success: row.cells.filter((c) => c.status === "success").length,
|
|
242
|
+
failed: row.cells.filter((c) => c.status === "failed").length,
|
|
243
|
+
total: row.cells.length,
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<React.Fragment key={rowIndex}>
|
|
248
|
+
<tr className="border-t hover:bg-muted/30">
|
|
249
|
+
<td className="p-2">
|
|
250
|
+
<button
|
|
251
|
+
className="flex items-center gap-2 text-left w-full"
|
|
252
|
+
onClick={() => toggleRow(rowIndex)}
|
|
253
|
+
>
|
|
254
|
+
{isExpanded ? (
|
|
255
|
+
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
256
|
+
) : (
|
|
257
|
+
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
|
258
|
+
)}
|
|
259
|
+
<div>
|
|
260
|
+
<div className="font-medium text-sm">{row.label}</div>
|
|
261
|
+
{row.description && (
|
|
262
|
+
<div className="text-xs text-muted-foreground">{row.description}</div>
|
|
263
|
+
)}
|
|
264
|
+
</div>
|
|
265
|
+
</button>
|
|
266
|
+
</td>
|
|
267
|
+
{row.cells.map((cell, colIndex) => (
|
|
268
|
+
<td key={colIndex} className="p-2">
|
|
269
|
+
<div className="flex justify-center">
|
|
270
|
+
<BuildCellComponent
|
|
271
|
+
cell={cell}
|
|
272
|
+
onClick={() => onCellClick?.(rowIndex, colIndex, cell)}
|
|
273
|
+
showDuration={showDuration}
|
|
274
|
+
/>
|
|
275
|
+
</div>
|
|
276
|
+
</td>
|
|
277
|
+
))}
|
|
278
|
+
</tr>
|
|
279
|
+
|
|
280
|
+
{/* Expanded row details */}
|
|
281
|
+
{isExpanded && (
|
|
282
|
+
<tr className="bg-muted/20">
|
|
283
|
+
<td colSpan={columns.length + 1} className="p-4">
|
|
284
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
285
|
+
{row.cells.map((cell, colIndex) => {
|
|
286
|
+
const config = statusConfig[cell.status]
|
|
287
|
+
const Icon = config.icon
|
|
288
|
+
return (
|
|
289
|
+
<div
|
|
290
|
+
key={colIndex}
|
|
291
|
+
className="border rounded-lg p-3 bg-background"
|
|
292
|
+
>
|
|
293
|
+
<div className="flex items-center justify-between mb-2">
|
|
294
|
+
<div className="flex items-center gap-2">
|
|
295
|
+
<Icon className={cn("h-4 w-4", config.color)} />
|
|
296
|
+
<span className="font-medium text-sm">{columns[colIndex]}</span>
|
|
297
|
+
</div>
|
|
298
|
+
<Badge variant="outline" className={config.color}>
|
|
299
|
+
{config.label}
|
|
300
|
+
</Badge>
|
|
301
|
+
</div>
|
|
302
|
+
<div className="text-xs text-muted-foreground space-y-1">
|
|
303
|
+
{cell.duration && (
|
|
304
|
+
<div>Duration: {formatDuration(cell.duration)}</div>
|
|
305
|
+
)}
|
|
306
|
+
{cell.startedAt && (
|
|
307
|
+
<div>Started: {cell.startedAt.toLocaleTimeString()}</div>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
<div className="flex items-center gap-2 mt-3">
|
|
311
|
+
{cell.url && (
|
|
312
|
+
<Button
|
|
313
|
+
variant="outline"
|
|
314
|
+
size="sm"
|
|
315
|
+
className="h-7 text-xs"
|
|
316
|
+
onClick={() => window.open(cell.url, "_blank")}
|
|
317
|
+
>
|
|
318
|
+
<ExternalLink className="h-3 w-3 mr-1" />
|
|
319
|
+
View
|
|
320
|
+
</Button>
|
|
321
|
+
)}
|
|
322
|
+
{cell.status === "failed" && onRetry && (
|
|
323
|
+
<Button
|
|
324
|
+
variant="outline"
|
|
325
|
+
size="sm"
|
|
326
|
+
className="h-7 text-xs"
|
|
327
|
+
onClick={() => onRetry(rowIndex, colIndex)}
|
|
328
|
+
>
|
|
329
|
+
<RefreshCw className="h-3 w-3 mr-1" />
|
|
330
|
+
Retry
|
|
331
|
+
</Button>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
)
|
|
336
|
+
})}
|
|
337
|
+
</div>
|
|
338
|
+
</td>
|
|
339
|
+
</tr>
|
|
340
|
+
)}
|
|
341
|
+
</React.Fragment>
|
|
342
|
+
)
|
|
343
|
+
})}
|
|
344
|
+
</tbody>
|
|
345
|
+
</table>
|
|
346
|
+
</div>
|
|
347
|
+
</ScrollArea>
|
|
348
|
+
|
|
349
|
+
{/* Footer with pass rate */}
|
|
350
|
+
<div className="flex items-center justify-between px-3 py-2 border-t bg-muted/30 text-sm">
|
|
351
|
+
<span className="text-muted-foreground">
|
|
352
|
+
Pass Rate: <span className="font-medium text-foreground">{stats.passRate.toFixed(1)}%</span>
|
|
353
|
+
</span>
|
|
354
|
+
<span className="text-muted-foreground">
|
|
355
|
+
{stats.success}/{stats.total} jobs passed
|
|
356
|
+
</span>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Default sample data for demo
|
|
363
|
+
export const defaultBuildMatrixColumns = ["Ubuntu 22.04", "Ubuntu 20.04", "macOS 13", "Windows 2022"]
|
|
364
|
+
|
|
365
|
+
export const defaultBuildMatrixRows: BuildMatrixRow[] = [
|
|
366
|
+
{
|
|
367
|
+
label: "Node.js 20",
|
|
368
|
+
description: "LTS version",
|
|
369
|
+
cells: [
|
|
370
|
+
{ id: "1", status: "success", duration: 125 },
|
|
371
|
+
{ id: "2", status: "success", duration: 142 },
|
|
372
|
+
{ id: "3", status: "success", duration: 98 },
|
|
373
|
+
{ id: "4", status: "success", duration: 156 },
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
label: "Node.js 18",
|
|
378
|
+
description: "Previous LTS",
|
|
379
|
+
cells: [
|
|
380
|
+
{ id: "5", status: "success", duration: 118 },
|
|
381
|
+
{ id: "6", status: "success", duration: 135 },
|
|
382
|
+
{ id: "7", status: "failed", duration: 45 },
|
|
383
|
+
{ id: "8", status: "success", duration: 162 },
|
|
384
|
+
],
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
label: "Node.js 21",
|
|
388
|
+
description: "Current",
|
|
389
|
+
cells: [
|
|
390
|
+
{ id: "9", status: "running", startedAt: new Date() },
|
|
391
|
+
{ id: "10", status: "pending" },
|
|
392
|
+
{ id: "11", status: "pending" },
|
|
393
|
+
{ id: "12", status: "pending" },
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { cn } from "../../utils/cn"
|
|
5
|
+
import { safeNavigate } from "../../utils/security"
|
|
5
6
|
import { Command, Search, ArrowRight, CornerDownLeft, Hash, Settings, User, FileText, Zap } from "lucide-react"
|
|
6
7
|
|
|
7
8
|
// ============================================================================
|
|
@@ -214,7 +215,7 @@ export function WakaCommandBar({
|
|
|
214
215
|
onSelect?.(item)
|
|
215
216
|
item.action?.()
|
|
216
217
|
if (item.href) {
|
|
217
|
-
|
|
218
|
+
safeNavigate(item.href)
|
|
218
219
|
}
|
|
219
220
|
onOpenChange(false)
|
|
220
221
|
}
|