fix(tests): stop requiring Containment Time when result is Not Contained
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

Containment Time was gated on detection alone, so picking "Not Contained"
still showed the field and required it before saving — there's no
containment moment to record when nothing was contained. Now only shown
and required when the result is Contained or Partially Contained; a
stale value from a prior Contained selection is cleared when the result
changes away from it, so it can't get silently resubmitted once hidden.
This commit is contained in:
kitos
2026-07-14 12:00:53 +02:00
parent 9d66c30127
commit 92d65374ef
2 changed files with 42 additions and 21 deletions
@@ -189,6 +189,12 @@ export default function TeamTabs({
? blueDraft.detection_result === "detected" || blueDraft.detection_result === "partially_detected"
: test.detection_result === "detected" || test.detection_result === "partially_detected";
// Containment Time only makes sense when something was actually contained —
// "Not Contained" has no containment moment to record.
const isContained = canEditBlue
? blueDraft.containment_result === "contained" || blueDraft.containment_result === "partially_contained"
: test.containment_result === "contained" || test.containment_result === "partially_contained";
// Hint messages shown to operators when editing is locked
const redLockedHint =
test.state === "draft" && role === "red_tech"
@@ -520,7 +526,9 @@ export default function TeamTabs({
</div>
)}
{/* Detection & Containment times — only when detected/partially_detected */}
{/* Detection & Containment times — Detection Time whenever detected;
Containment Time only when something was actually contained —
"Not Contained" has no containment moment to record. */}
{isDetected && (
<div className="grid grid-cols-2 gap-4">
<div>
@@ -541,6 +549,7 @@ export default function TeamTabs({
</p>
)}
</div>
{isContained && (
<div>
<label className="mb-1.5 block text-sm font-medium text-gray-300">
Containment Time (UTC) <span className="text-red-400">*</span>
@@ -559,6 +568,7 @@ export default function TeamTabs({
</p>
)}
</div>
)}
</div>
)}
+13 -2
View File
@@ -446,7 +446,17 @@ export default function TestDetailPage() {
};
const handleBlueFieldChange = (field: string, value: string) => {
setBlueDraft((prev) => ({ ...prev, [field]: value }));
setBlueDraft((prev) => {
const next = { ...prev, [field]: value };
// Containment Time only applies when something was actually
// contained — clear a stale value left over from a previous
// "Contained"/"Partially Contained" selection so it doesn't get
// silently resubmitted once the field is hidden again.
if (field === "containment_result" && value !== "contained" && value !== "partially_contained") {
next.containment_time = "";
}
return next;
});
};
const handleUploadEvidence = async (file: File, team: TeamSide) => {
@@ -624,8 +634,9 @@ export default function TestDetailPage() {
<button
onClick={() => {
const isDetected = blueDraft.detection_result === "detected" || blueDraft.detection_result === "partially_detected";
const isContained = blueDraft.containment_result === "contained" || blueDraft.containment_result === "partially_contained";
if (isDetected && !blueDraft.detection_time) { showToast("Detection Time is required", "error"); return; }
if (isDetected && !blueDraft.containment_time) { showToast("Containment Time is required", "error"); return; }
if (isContained && !blueDraft.containment_time) { showToast("Containment Time is required", "error"); return; }
saveBlueMutation.mutate();
}}
disabled={saveBlueMutation.isPending}