diff --git a/frontend/src/components/test-detail/TestDetailHeader.tsx b/frontend/src/components/test-detail/TestDetailHeader.tsx
index 26b58f7..fd3bfee 100644
--- a/frontend/src/components/test-detail/TestDetailHeader.tsx
+++ b/frontend/src/components/test-detail/TestDetailHeader.tsx
@@ -87,17 +87,21 @@ export default function TestDetailHeader({
const role = user?.role ?? "";
const currentIdx = STATE_INDEX[test.state];
const [showDiscussModal, setShowDiscussModal] = useState(false);
+ const [discussionSent, setDiscussionSent] = useState(false);
const [discussResult, setDiscussResult] = useState<{
username: string; email: string | null; role: string;
} | null>(null);
const discussMutation = useMutation({
mutationFn: () => requestDiscussion(test.id),
- onSuccess: (data) => setDiscussResult({
- username: data.rejector_username,
- email: data.rejector_email,
- role: data.rejector_role,
- }),
+ onSuccess: (data) => {
+ setDiscussResult({
+ username: data.rejector_username,
+ email: data.rejector_email,
+ role: data.rejector_role,
+ });
+ setDiscussionSent(true);
+ },
});
const formatDate = (d: string | null) => {
@@ -234,37 +238,74 @@ export default function TestDetailHeader({
);
}
- // Disputed: the lead who approved can change their vote to rejected
+ // Disputed state: symmetric actions for both leads
if (test.state === "disputed") {
- const canChangeVote =
+ const isApproving =
(role === "red_lead" && test.red_validation_status === "approved") ||
(role === "blue_lead" && test.blue_validation_status === "approved") ||
- role === "admin";
+ (role === "admin" && test.red_validation_status === "approved");
- if (canChangeVote) {
- const side = role === "red_lead" ? "red" : role === "blue_lead" ? "blue" : null;
- if (side || role === "admin") {
- buttons.push(
-
- {/* Confirm keeping approval → open discussion modal */}
+ const isRejecting =
+ (role === "red_lead" && test.red_validation_status === "rejected") ||
+ (role === "blue_lead" && test.blue_validation_status === "rejected") ||
+ (role === "admin" && test.blue_validation_status === "rejected");
+
+ const approvingSide: "red" | "blue" =
+ test.red_validation_status === "approved" ? "red" : "blue";
+ const rejectingSide: "red" | "blue" =
+ test.red_validation_status === "rejected" ? "red" : "blue";
+
+ if (isApproving || role === "admin") {
+ buttons.push(
+
+
+ {/* Discussion request button — shows ✓ after sent */}
+ {discussionSent ? (
+
+
+ Discussion Requested ✓
+
+ ) : (
+
+ )}
+ {/* Change approving vote to rejected */}
- {/* Change vote to rejected */}
-
-
,
- );
- }
+
+
+ You approved — change your vote or request a discussion
+
+
,
+ );
+ }
+
+ if (isRejecting && !isApproving) {
+ buttons.push(
+
+ {/* Change rejecting vote to approved */}
+
+
+ You rejected — change your vote if you agree after discussion
+
+
,
+ );
}
}