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
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:
@@ -189,6 +189,12 @@ export default function TeamTabs({
|
|||||||
? blueDraft.detection_result === "detected" || blueDraft.detection_result === "partially_detected"
|
? blueDraft.detection_result === "detected" || blueDraft.detection_result === "partially_detected"
|
||||||
: test.detection_result === "detected" || test.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
|
// Hint messages shown to operators when editing is locked
|
||||||
const redLockedHint =
|
const redLockedHint =
|
||||||
test.state === "draft" && role === "red_tech"
|
test.state === "draft" && role === "red_tech"
|
||||||
@@ -520,7 +526,9 @@ export default function TeamTabs({
|
|||||||
</div>
|
</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 && (
|
{isDetected && (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -541,6 +549,7 @@ export default function TeamTabs({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{isContained && (
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1.5 block text-sm font-medium text-gray-300">
|
<label className="mb-1.5 block text-sm font-medium text-gray-300">
|
||||||
Containment Time (UTC) <span className="text-red-400">*</span>
|
Containment Time (UTC) <span className="text-red-400">*</span>
|
||||||
@@ -559,6 +568,7 @@ export default function TeamTabs({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -446,7 +446,17 @@ export default function TestDetailPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBlueFieldChange = (field: string, value: string) => {
|
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) => {
|
const handleUploadEvidence = async (file: File, team: TeamSide) => {
|
||||||
@@ -624,8 +634,9 @@ export default function TestDetailPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const isDetected = blueDraft.detection_result === "detected" || blueDraft.detection_result === "partially_detected";
|
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.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();
|
saveBlueMutation.mutate();
|
||||||
}}
|
}}
|
||||||
disabled={saveBlueMutation.isPending}
|
disabled={saveBlueMutation.isPending}
|
||||||
|
|||||||
Reference in New Issue
Block a user