dev3000 0.0.114 → 0.0.115
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/cli.js +32 -2
- package/dist/cli.js.map +1 -1
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +5 -4
- package/dist/dev-environment.js.map +1 -1
- package/dist/src/tui-interface-impl.tsx +175 -127
- package/dist/tui-interface-impl.d.ts.map +1 -1
- package/dist/tui-interface-impl.js +113 -74
- package/dist/tui-interface-impl.js.map +1 -1
- package/mcp-server/.next/BUILD_ID +1 -1
- package/mcp-server/.next/build-manifest.json +2 -2
- package/mcp-server/.next/fallback-build-manifest.json +2 -2
- package/mcp-server/.next/prerender-manifest.json +3 -3
- package/mcp-server/.next/server/app/_global-error.html +2 -2
- package/mcp-server/.next/server/app/_global-error.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/_not-found.html +1 -1
- package/mcp-server/.next/server/app/_not-found.rsc +2 -2
- package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/mcp-server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/mcp-server/.next/server/app/api/cloud/start-fix/route.js +2 -2
- package/mcp-server/.next/server/app/api/cloud/start-fix/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/auth/error/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/auth/error.html +1 -1
- package/mcp-server/.next/server/app/auth/error.rsc +2 -2
- package/mcp-server/.next/server/app/auth/error.segments/_full.segment.rsc +2 -2
- package/mcp-server/.next/server/app/auth/error.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/auth/error.segments/_index.segment.rsc +2 -2
- package/mcp-server/.next/server/app/auth/error.segments/_tree.segment.rsc +2 -2
- package/mcp-server/.next/server/app/auth/error.segments/auth/error/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/auth/error.segments/auth/error.segment.rsc +1 -1
- package/mcp-server/.next/server/app/auth/error.segments/auth.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.html +1 -1
- package/mcp-server/.next/server/app/index.rsc +3 -3
- package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/mcp-server/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/signin/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/video/[session]/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/workflows/[id]/report/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/workflows/new/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/workflows/new/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/workflows/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/workflows/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__157de66b._.js +199 -134
- package/mcp-server/.next/server/chunks/[root-of-the-server]__157de66b._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__730a8fd0._.js +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__730a8fd0._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__c1681338._.js +3 -0
- package/mcp-server/.next/server/chunks/[root-of-the-server]__c1681338._.js.map +1 -0
- package/mcp-server/.next/server/chunks/[root-of-the-server]__ec6a1335._.js.map +1 -1
- package/mcp-server/.next/server/chunks/bee4f_next_dist_esm_build_templates_app-route_1ece9366.js +54 -54
- package/mcp-server/.next/server/chunks/bee4f_next_dist_esm_build_templates_app-route_1ece9366.js.map +1 -1
- package/mcp-server/.next/server/chunks/mcp-server_app_api_cloud_fix-workflow_steps_ts_b65f3271._.js +30 -16
- package/mcp-server/.next/server/chunks/mcp-server_app_api_cloud_fix-workflow_steps_ts_b65f3271._.js.map +1 -1
- package/mcp-server/.next/server/chunks/node_modules__pnpm_85ddbe9c._.js +1 -1
- package/mcp-server/.next/server/chunks/node_modules__pnpm_85ddbe9c._.js.map +1 -1
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__2e44f0db._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__2e44f0db._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__3585c949._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__3585c949._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__477c3bbb._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__477c3bbb._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__570677dc._.js → [root-of-the-server]__880839a0._.js} +2 -2
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__880839a0._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/_41b8f993._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/_41b8f993._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/_9ba0ef29._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/_9ba0ef29._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/_cd4dc25e._.js.map +1 -1
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_workflows_new_new-workflow-client_tsx_1312c046._.js +2 -2
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_workflows_new_new-workflow-client_tsx_1312c046._.js.map +1 -1
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_workflows_workflows-client_tsx_268cfd4a._.js +7 -0
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_workflows_workflows-client_tsx_268cfd4a._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_961f21c4._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_961f21c4._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_a82244bf._.js +3 -0
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_a82244bf._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/{node_modules__pnpm_07527699._.js → node_modules__pnpm_eb98e511._.js} +2 -2
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_eb98e511._.js.map +1 -0
- package/mcp-server/.next/server/server-reference-manifest.js +1 -1
- package/mcp-server/.next/server/server-reference-manifest.json +1 -1
- package/mcp-server/.next/static/chunks/000849a6a897f531.css +1 -0
- package/mcp-server/.next/static/chunks/048cee2510ddb1a0.js +1 -0
- package/mcp-server/.next/static/chunks/0622bd0e093adee7.js +3 -0
- package/mcp-server/.next/static/chunks/{46f60efee5f19794.js → 16359f64918a93f3.js} +1 -1
- package/mcp-server/.next/static/chunks/1851a3e70d7efc10.js +1 -0
- package/mcp-server/.next/static/chunks/{cc6addc4bb10fa11.js → 2ad16eeb719786f1.js} +1 -1
- package/mcp-server/.next/static/chunks/57feca7a4e06545e.js +7 -0
- package/mcp-server/.next/static/chunks/93db5737a327ab0c.js +6 -0
- package/mcp-server/.next/static/chunks/9fd3c715ecfb4d05.js +1 -0
- package/mcp-server/.next/static/chunks/b4b1ec6435790587.js +1 -0
- package/mcp-server/.next/static/chunks/cfe150cb2048b7e8.js +1 -0
- package/mcp-server/app/api/cloud/fix-workflow/steps.ts +267 -28
- package/mcp-server/app/api/cloud/fix-workflow/workflow.ts +16 -8
- package/mcp-server/app/api/cloud/start-fix/route.ts +2 -2
- package/mcp-server/app/api/workflows/route.ts +45 -1
- package/mcp-server/app/workflows/workflows-client.tsx +259 -100
- package/package.json +1 -1
- package/src/tui-interface-impl.tsx +175 -127
- package/mcp-server/.next/server/chunks/[root-of-the-server]__75d68567._.js +0 -3
- package/mcp-server/.next/server/chunks/[root-of-the-server]__75d68567._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__0ff05d72._.js +0 -3
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__0ff05d72._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__27cc5956._.js +0 -3
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__27cc5956._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__570677dc._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__ef510343._.js +0 -3
- package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__ef510343._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_07527699._.js.map +0 -1
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_7cc36047._.js +0 -3
- package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_7cc36047._.js.map +0 -1
- package/mcp-server/.next/static/chunks/07848f6bd2a7e5f6.js +0 -3
- package/mcp-server/.next/static/chunks/637a66565f27572f.js +0 -6
- package/mcp-server/.next/static/chunks/aed4fb5252a4bc95.js +0 -3
- package/mcp-server/.next/static/chunks/e8d521464b0c96ca.css +0 -1
- package/mcp-server/.next/static/chunks/ff53279afa939907.js +0 -1
- package/mcp-server/.next/static/chunks/ffa2ecb6845be49c.js +0 -1
- /package/mcp-server/.next/static/{ZvLsxTWoDyQzaSsr_VeX6 → 5zfTZk2QSS7WLdL1K8Q5I}/_buildManifest.js +0 -0
- /package/mcp-server/.next/static/{ZvLsxTWoDyQzaSsr_VeX6 → 5zfTZk2QSS7WLdL1K8Q5I}/_clientMiddlewareManifest.json +0 -0
- /package/mcp-server/.next/static/{ZvLsxTWoDyQzaSsr_VeX6 → 5zfTZk2QSS7WLdL1K8Q5I}/_ssgManifest.js +0 -0
|
@@ -2,10 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import Image from "next/image"
|
|
4
4
|
import Link from "next/link"
|
|
5
|
-
import { useState } from "react"
|
|
5
|
+
import { useCallback, useRef, useState } from "react"
|
|
6
6
|
import { Badge } from "@/components/ui/badge"
|
|
7
7
|
import { Button } from "@/components/ui/button"
|
|
8
8
|
import { Card, CardContent } from "@/components/ui/card"
|
|
9
|
+
import { Checkbox } from "@/components/ui/checkbox"
|
|
10
|
+
import {
|
|
11
|
+
Dialog,
|
|
12
|
+
DialogClose,
|
|
13
|
+
DialogContent,
|
|
14
|
+
DialogDescription,
|
|
15
|
+
DialogFooter,
|
|
16
|
+
DialogHeader,
|
|
17
|
+
DialogTitle
|
|
18
|
+
} from "@/components/ui/dialog"
|
|
9
19
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
|
10
20
|
import type { WorkflowRun } from "@/lib/workflow-storage"
|
|
11
21
|
|
|
@@ -23,6 +33,11 @@ interface WorkflowsClientProps {
|
|
|
23
33
|
|
|
24
34
|
export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientProps) {
|
|
25
35
|
const [isSigningOut, setIsSigningOut] = useState(false)
|
|
36
|
+
const [runs, setRuns] = useState<WorkflowRun[]>(initialRuns)
|
|
37
|
+
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
|
|
38
|
+
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
|
39
|
+
const [isDeleting, setIsDeleting] = useState(false)
|
|
40
|
+
const lastSelectedIndex = useRef<number | null>(null)
|
|
26
41
|
|
|
27
42
|
async function handleSignOut() {
|
|
28
43
|
setIsSigningOut(true)
|
|
@@ -35,16 +50,100 @@ export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientPr
|
|
|
35
50
|
}
|
|
36
51
|
}
|
|
37
52
|
|
|
53
|
+
const handleSelectAll = useCallback(
|
|
54
|
+
(checked: boolean) => {
|
|
55
|
+
if (checked) {
|
|
56
|
+
setSelectedIds(new Set(runs.map((run) => run.id)))
|
|
57
|
+
} else {
|
|
58
|
+
setSelectedIds(new Set())
|
|
59
|
+
}
|
|
60
|
+
lastSelectedIndex.current = null
|
|
61
|
+
},
|
|
62
|
+
[runs]
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const handleSelectRow = useCallback(
|
|
66
|
+
(runId: string, index: number, event: React.MouseEvent) => {
|
|
67
|
+
setSelectedIds((prev) => {
|
|
68
|
+
const newSelected = new Set(prev)
|
|
69
|
+
|
|
70
|
+
// Handle shift-click for range selection
|
|
71
|
+
if (event.shiftKey && lastSelectedIndex.current !== null) {
|
|
72
|
+
const start = Math.min(lastSelectedIndex.current, index)
|
|
73
|
+
const end = Math.max(lastSelectedIndex.current, index)
|
|
74
|
+
|
|
75
|
+
// Select all items in the range
|
|
76
|
+
for (let i = start; i <= end; i++) {
|
|
77
|
+
newSelected.add(runs[i].id)
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// Regular click - toggle selection
|
|
81
|
+
if (newSelected.has(runId)) {
|
|
82
|
+
newSelected.delete(runId)
|
|
83
|
+
} else {
|
|
84
|
+
newSelected.add(runId)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return newSelected
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
lastSelectedIndex.current = index
|
|
92
|
+
},
|
|
93
|
+
[runs]
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const handleDelete = async () => {
|
|
97
|
+
setIsDeleting(true)
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch("/api/workflows", {
|
|
100
|
+
method: "DELETE",
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "application/json"
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
userId: user.id,
|
|
106
|
+
runIds: Array.from(selectedIds)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const result = await response.json()
|
|
111
|
+
|
|
112
|
+
if (result.success) {
|
|
113
|
+
// Remove deleted runs from state
|
|
114
|
+
setRuns((prev) => prev.filter((run) => !selectedIds.has(run.id)))
|
|
115
|
+
setSelectedIds(new Set())
|
|
116
|
+
setIsDeleteDialogOpen(false)
|
|
117
|
+
} else {
|
|
118
|
+
console.error("Failed to delete workflows:", result.error)
|
|
119
|
+
alert(`Failed to delete workflows: ${result.error}`)
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("Failed to delete workflows:", error)
|
|
123
|
+
alert("Failed to delete workflows. Please try again.")
|
|
124
|
+
} finally {
|
|
125
|
+
setIsDeleting(false)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const allSelected = runs.length > 0 && selectedIds.size === runs.length
|
|
130
|
+
const someSelected = selectedIds.size > 0 && selectedIds.size < runs.length
|
|
131
|
+
|
|
38
132
|
return (
|
|
39
|
-
<div className="
|
|
40
|
-
<div className="
|
|
41
|
-
<div className="
|
|
133
|
+
<div className="h-screen flex flex-col bg-gray-50">
|
|
134
|
+
<div className="flex-shrink-0 px-4 sm:px-6 lg:px-8 pt-8 pb-4 max-w-7xl mx-auto w-full">
|
|
135
|
+
<div className="flex justify-between items-start">
|
|
42
136
|
<div>
|
|
43
137
|
<h1 className="text-3xl font-bold text-gray-900">d3k Workflow Runs</h1>
|
|
44
138
|
<p className="mt-2 text-gray-600">View all your d3k workflow fix proposals and PRs</p>
|
|
45
139
|
<p className="mt-1 text-sm text-gray-500">Signed in as {user.email}</p>
|
|
46
140
|
</div>
|
|
47
141
|
<div className="flex gap-3">
|
|
142
|
+
{selectedIds.size > 0 && (
|
|
143
|
+
<Button variant="destructive" onClick={() => setIsDeleteDialogOpen(true)}>
|
|
144
|
+
Delete {selectedIds.size} selected
|
|
145
|
+
</Button>
|
|
146
|
+
)}
|
|
48
147
|
<Button asChild>
|
|
49
148
|
<Link href="/workflows/new">New Workflow</Link>
|
|
50
149
|
</Button>
|
|
@@ -53,8 +152,10 @@ export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientPr
|
|
|
53
152
|
</Button>
|
|
54
153
|
</div>
|
|
55
154
|
</div>
|
|
155
|
+
</div>
|
|
56
156
|
|
|
57
|
-
|
|
157
|
+
<div className="flex-1 min-h-0 px-4 sm:px-6 lg:px-8 pb-8 max-w-7xl mx-auto w-full">
|
|
158
|
+
{runs.length === 0 ? (
|
|
58
159
|
<Card className="p-12 text-center">
|
|
59
160
|
<CardContent>
|
|
60
161
|
<p className="text-muted-foreground">No workflow runs yet</p>
|
|
@@ -62,105 +163,163 @@ export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientPr
|
|
|
62
163
|
</CardContent>
|
|
63
164
|
</Card>
|
|
64
165
|
) : (
|
|
65
|
-
<Card>
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
>
|
|
98
|
-
{run.status}
|
|
99
|
-
</Badge>
|
|
100
|
-
</TableCell>
|
|
101
|
-
<TableCell className="text-muted-foreground">{new Date(run.timestamp).toLocaleString()}</TableCell>
|
|
102
|
-
<TableCell>
|
|
103
|
-
{run.beforeScreenshotUrl ? (
|
|
104
|
-
<a href={run.beforeScreenshotUrl} target="_blank" rel="noopener noreferrer">
|
|
105
|
-
<Image
|
|
106
|
-
src={run.beforeScreenshotUrl}
|
|
107
|
-
alt="Before"
|
|
108
|
-
width={64}
|
|
109
|
-
height={40}
|
|
110
|
-
className="object-cover rounded border hover:opacity-80 transition-opacity"
|
|
111
|
-
unoptimized
|
|
112
|
-
/>
|
|
113
|
-
</a>
|
|
114
|
-
) : (
|
|
115
|
-
<span className="text-muted-foreground text-xs">-</span>
|
|
116
|
-
)}
|
|
117
|
-
</TableCell>
|
|
118
|
-
<TableCell>
|
|
119
|
-
{run.afterScreenshotUrl ? (
|
|
120
|
-
<a href={run.afterScreenshotUrl} target="_blank" rel="noopener noreferrer">
|
|
121
|
-
<Image
|
|
122
|
-
src={run.afterScreenshotUrl}
|
|
123
|
-
alt="After"
|
|
124
|
-
width={64}
|
|
125
|
-
height={40}
|
|
126
|
-
className="object-cover rounded border hover:opacity-80 transition-opacity"
|
|
127
|
-
unoptimized
|
|
128
|
-
/>
|
|
129
|
-
</a>
|
|
130
|
-
) : (
|
|
131
|
-
<span className="text-muted-foreground text-xs">-</span>
|
|
132
|
-
)}
|
|
133
|
-
</TableCell>
|
|
134
|
-
<TableCell>
|
|
135
|
-
{run.reportBlobUrl ? (
|
|
136
|
-
<Link href={`/workflows/${run.id}/report`} className="text-primary hover:underline">
|
|
137
|
-
View Report
|
|
138
|
-
</Link>
|
|
139
|
-
) : (
|
|
140
|
-
<span className="text-muted-foreground">No report</span>
|
|
141
|
-
)}
|
|
142
|
-
</TableCell>
|
|
143
|
-
<TableCell>
|
|
144
|
-
{run.prUrl ? (
|
|
145
|
-
<a
|
|
146
|
-
href={run.prUrl}
|
|
147
|
-
target="_blank"
|
|
148
|
-
rel="noopener noreferrer"
|
|
149
|
-
className="text-primary hover:underline"
|
|
150
|
-
>
|
|
151
|
-
View PR
|
|
152
|
-
</a>
|
|
153
|
-
) : (
|
|
154
|
-
<span className="text-muted-foreground">No PR</span>
|
|
155
|
-
)}
|
|
156
|
-
</TableCell>
|
|
166
|
+
<Card className="h-full flex flex-col overflow-hidden">
|
|
167
|
+
<div className="overflow-auto flex-1 [&_[data-slot=table-container]]:overflow-visible">
|
|
168
|
+
<Table>
|
|
169
|
+
<TableHeader className="sticky top-0 bg-card z-10 shadow-[0_1px_3px_-1px_rgba(0,0,0,0.1)]">
|
|
170
|
+
<TableRow>
|
|
171
|
+
<TableHead className="w-8 pr-0">
|
|
172
|
+
<Checkbox
|
|
173
|
+
checked={allSelected}
|
|
174
|
+
ref={(el) => {
|
|
175
|
+
if (el) {
|
|
176
|
+
// Set indeterminate state for "some selected"
|
|
177
|
+
const input = el.querySelector("button") as HTMLButtonElement
|
|
178
|
+
if (input) {
|
|
179
|
+
input.dataset.state = someSelected
|
|
180
|
+
? "indeterminate"
|
|
181
|
+
: allSelected
|
|
182
|
+
? "checked"
|
|
183
|
+
: "unchecked"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}}
|
|
187
|
+
onCheckedChange={handleSelectAll}
|
|
188
|
+
aria-label="Select all"
|
|
189
|
+
/>
|
|
190
|
+
</TableHead>
|
|
191
|
+
<TableHead>Project ({runs.length})</TableHead>
|
|
192
|
+
<TableHead>Status</TableHead>
|
|
193
|
+
<TableHead>Timestamp</TableHead>
|
|
194
|
+
<TableHead>Before</TableHead>
|
|
195
|
+
<TableHead>After</TableHead>
|
|
196
|
+
<TableHead>Report</TableHead>
|
|
197
|
+
<TableHead>PR</TableHead>
|
|
157
198
|
</TableRow>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
199
|
+
</TableHeader>
|
|
200
|
+
<TableBody>
|
|
201
|
+
{runs.map((run, index) => (
|
|
202
|
+
<TableRow
|
|
203
|
+
key={`${run.id}-${run.timestamp}`}
|
|
204
|
+
className={selectedIds.has(run.id) ? "bg-muted/50" : undefined}
|
|
205
|
+
>
|
|
206
|
+
<TableCell className="pr-0">
|
|
207
|
+
<Checkbox
|
|
208
|
+
checked={selectedIds.has(run.id)}
|
|
209
|
+
onClick={(e) => handleSelectRow(run.id, index, e)}
|
|
210
|
+
onCheckedChange={() => {}}
|
|
211
|
+
aria-label={`Select ${run.projectName}`}
|
|
212
|
+
/>
|
|
213
|
+
</TableCell>
|
|
214
|
+
<TableCell>
|
|
215
|
+
<div className="font-medium">{run.projectName}</div>
|
|
216
|
+
<div className="text-xs text-muted-foreground">{run.id}</div>
|
|
217
|
+
</TableCell>
|
|
218
|
+
<TableCell>
|
|
219
|
+
<Badge
|
|
220
|
+
variant={
|
|
221
|
+
run.status === "done" ? "secondary" : run.status === "running" ? "default" : "destructive"
|
|
222
|
+
}
|
|
223
|
+
className={
|
|
224
|
+
run.status === "done"
|
|
225
|
+
? "bg-green-100 text-green-800 hover:bg-green-100"
|
|
226
|
+
: run.status === "running"
|
|
227
|
+
? "bg-blue-100 text-blue-800 hover:bg-blue-100"
|
|
228
|
+
: ""
|
|
229
|
+
}
|
|
230
|
+
>
|
|
231
|
+
{run.status}
|
|
232
|
+
</Badge>
|
|
233
|
+
</TableCell>
|
|
234
|
+
<TableCell className="text-muted-foreground">
|
|
235
|
+
{new Date(run.timestamp).toLocaleString()}
|
|
236
|
+
</TableCell>
|
|
237
|
+
<TableCell>
|
|
238
|
+
{run.beforeScreenshotUrl ? (
|
|
239
|
+
<a href={run.beforeScreenshotUrl} target="_blank" rel="noopener noreferrer">
|
|
240
|
+
<Image
|
|
241
|
+
src={run.beforeScreenshotUrl}
|
|
242
|
+
alt="Before"
|
|
243
|
+
width={64}
|
|
244
|
+
height={40}
|
|
245
|
+
className="object-cover rounded border hover:opacity-80 transition-opacity"
|
|
246
|
+
unoptimized
|
|
247
|
+
/>
|
|
248
|
+
</a>
|
|
249
|
+
) : (
|
|
250
|
+
<span className="text-muted-foreground text-xs">-</span>
|
|
251
|
+
)}
|
|
252
|
+
</TableCell>
|
|
253
|
+
<TableCell>
|
|
254
|
+
{run.afterScreenshotUrl ? (
|
|
255
|
+
<a href={run.afterScreenshotUrl} target="_blank" rel="noopener noreferrer">
|
|
256
|
+
<Image
|
|
257
|
+
src={run.afterScreenshotUrl}
|
|
258
|
+
alt="After"
|
|
259
|
+
width={64}
|
|
260
|
+
height={40}
|
|
261
|
+
className="object-cover rounded border hover:opacity-80 transition-opacity"
|
|
262
|
+
unoptimized
|
|
263
|
+
/>
|
|
264
|
+
</a>
|
|
265
|
+
) : (
|
|
266
|
+
<span className="text-muted-foreground text-xs">-</span>
|
|
267
|
+
)}
|
|
268
|
+
</TableCell>
|
|
269
|
+
<TableCell>
|
|
270
|
+
{run.reportBlobUrl ? (
|
|
271
|
+
<Link href={`/workflows/${run.id}/report`} className="text-primary hover:underline">
|
|
272
|
+
View Report
|
|
273
|
+
</Link>
|
|
274
|
+
) : (
|
|
275
|
+
<span className="text-muted-foreground">No report</span>
|
|
276
|
+
)}
|
|
277
|
+
</TableCell>
|
|
278
|
+
<TableCell>
|
|
279
|
+
{run.prUrl ? (
|
|
280
|
+
<a
|
|
281
|
+
href={run.prUrl}
|
|
282
|
+
target="_blank"
|
|
283
|
+
rel="noopener noreferrer"
|
|
284
|
+
className="text-primary hover:underline"
|
|
285
|
+
>
|
|
286
|
+
View PR
|
|
287
|
+
</a>
|
|
288
|
+
) : (
|
|
289
|
+
<span className="text-muted-foreground">No PR</span>
|
|
290
|
+
)}
|
|
291
|
+
</TableCell>
|
|
292
|
+
</TableRow>
|
|
293
|
+
))}
|
|
294
|
+
</TableBody>
|
|
295
|
+
</Table>
|
|
296
|
+
</div>
|
|
161
297
|
</Card>
|
|
162
298
|
)}
|
|
163
299
|
</div>
|
|
300
|
+
|
|
301
|
+
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
|
302
|
+
<DialogContent>
|
|
303
|
+
<DialogHeader>
|
|
304
|
+
<DialogTitle>Delete Workflow Runs</DialogTitle>
|
|
305
|
+
<DialogDescription>
|
|
306
|
+
Are you sure you want to delete {selectedIds.size} workflow run{selectedIds.size === 1 ? "" : "s"}? This
|
|
307
|
+
will permanently delete all associated data including screenshots and reports. This action cannot be
|
|
308
|
+
undone.
|
|
309
|
+
</DialogDescription>
|
|
310
|
+
</DialogHeader>
|
|
311
|
+
<DialogFooter>
|
|
312
|
+
<DialogClose asChild>
|
|
313
|
+
<Button variant="outline" disabled={isDeleting}>
|
|
314
|
+
Cancel
|
|
315
|
+
</Button>
|
|
316
|
+
</DialogClose>
|
|
317
|
+
<Button variant="destructive" onClick={handleDelete} disabled={isDeleting}>
|
|
318
|
+
{isDeleting ? "Deleting..." : `Delete ${selectedIds.size} run${selectedIds.size === 1 ? "" : "s"}`}
|
|
319
|
+
</Button>
|
|
320
|
+
</DialogFooter>
|
|
321
|
+
</DialogContent>
|
|
322
|
+
</Dialog>
|
|
164
323
|
</div>
|
|
165
324
|
)
|
|
166
325
|
}
|