feat(phase-30): add coverage snapshots, temporal comparison and auto re-testing (T-230 to T-232)
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
validateAsBlueLead,
|
||||
reopenTest,
|
||||
getTestTimeline,
|
||||
getRetestChain,
|
||||
} from "../api/tests";
|
||||
import { uploadEvidence, getEvidence } from "../api/evidence";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
@@ -79,6 +80,12 @@ export default function TestDetailPage() {
|
||||
enabled: !!testId,
|
||||
});
|
||||
|
||||
const { data: retestChain = [] } = useQuery({
|
||||
queryKey: ["retest-chain", testId],
|
||||
queryFn: () => getRetestChain(testId!),
|
||||
enabled: !!testId && !!test && (test.retest_of !== null || test.retest_count > 0),
|
||||
});
|
||||
|
||||
// Hydrate drafts from test data
|
||||
useEffect(() => {
|
||||
if (test) {
|
||||
@@ -442,6 +449,55 @@ export default function TestDetailPage() {
|
||||
)}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{/* Retest Chain */}
|
||||
{(test.retest_of || test.retest_count > 0 || retestChain.length > 1) && (
|
||||
<div className="rounded-xl border border-gray-800 bg-gray-900 p-6">
|
||||
<h2 className="mb-4 text-lg font-semibold text-white">Retest Chain</h2>
|
||||
{test.retest_of && (
|
||||
<div className="mb-3">
|
||||
<span className="text-xs font-medium uppercase text-gray-500">
|
||||
Retest {test.retest_count} / 3
|
||||
</span>
|
||||
<div className="mt-1 h-2 w-full rounded-full bg-gray-800 overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full bg-cyan-500 transition-all"
|
||||
style={{ width: `${(test.retest_count / 3) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
{retestChain.map((entry) => (
|
||||
<button
|
||||
key={entry.id}
|
||||
onClick={() => entry.id !== testId && navigate(`/tests/${entry.id}`)}
|
||||
className={`flex w-full items-center justify-between rounded-lg border px-3 py-2 text-left text-sm transition-colors ${
|
||||
entry.id === testId
|
||||
? "border-cyan-500/30 bg-cyan-900/30 text-cyan-400"
|
||||
: "border-gray-700 bg-gray-800/50 text-gray-300 hover:border-cyan-500/30 hover:text-cyan-400"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 truncate">
|
||||
<span className="truncate text-xs">
|
||||
{entry.retest_of ? `#${entry.retest_count}` : "Original"}
|
||||
</span>
|
||||
<span className="truncate">{entry.name}</span>
|
||||
</div>
|
||||
<span className={`shrink-0 rounded-full px-2 py-0.5 text-xs ${
|
||||
entry.state === "validated"
|
||||
? "bg-green-900/50 text-green-400"
|
||||
: entry.state === "rejected"
|
||||
? "bg-red-900/50 text-red-400"
|
||||
: "bg-gray-800/50 text-gray-500"
|
||||
}`}>
|
||||
{entry.state || "draft"}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user