feat(tests): dispute resolution UI — manager escalation + red/blue queue selector
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
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
validateAsBlueLead,
|
||||
reviewAsRedLead,
|
||||
reviewAsBlueLead,
|
||||
resolveDispute,
|
||||
reopenTest,
|
||||
pauseTimer,
|
||||
resumeTimer,
|
||||
@@ -32,6 +33,7 @@ import TestDetailHeader from "../components/test-detail/TestDetailHeader";
|
||||
import TeamTabs from "../components/test-detail/TeamTabs";
|
||||
import ValidationModal from "../components/test-detail/ValidationModal";
|
||||
import ReviewModal from "../components/test-detail/ReviewModal";
|
||||
import ResolveDisputeModal from "../components/test-detail/ResolveDisputeModal";
|
||||
import ConfirmDialog from "../components/ConfirmDialog";
|
||||
import JiraLinkPanel from "../components/JiraLinkPanel";
|
||||
import TestPhaseTimeline from "../components/TestPhaseTimeline";
|
||||
@@ -64,6 +66,8 @@ export default function TestDetailPage() {
|
||||
side: "red" | "blue";
|
||||
}>({ open: false, side: "red" });
|
||||
|
||||
const [resolveDisputeModalOpen, setResolveDisputeModalOpen] = useState(false);
|
||||
|
||||
const [confirmReopen, setConfirmReopen] = useState(false);
|
||||
const [holdModal, setHoldModal] = useState(false);
|
||||
const [holdReason, setHoldReason] = useState("");
|
||||
@@ -284,6 +288,17 @@ export default function TestDetailPage() {
|
||||
onError: (err: unknown) => showToast(extractError(err), "error"),
|
||||
});
|
||||
|
||||
const resolveDisputeMutation = useMutation({
|
||||
mutationFn: (payload: { target_team: "red" | "blue"; notes?: string }) =>
|
||||
resolveDispute(testId!, payload),
|
||||
onSuccess: () => {
|
||||
invalidateAll();
|
||||
setResolveDisputeModalOpen(false);
|
||||
showToast("Dispute resolved — test sent back for rework", "success");
|
||||
},
|
||||
onError: (err: unknown) => showToast(extractError(err), "error"),
|
||||
});
|
||||
|
||||
const reopenMutation = useMutation({
|
||||
mutationFn: () => reopenTest(testId!),
|
||||
onSuccess: () => {
|
||||
@@ -406,6 +421,10 @@ export default function TestDetailPage() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleResolveDisputeSubmit = (targetTeam: "red" | "blue", notes: string) => {
|
||||
resolveDisputeMutation.mutate({ target_team: targetTeam, notes: notes || undefined });
|
||||
};
|
||||
|
||||
const isTransitioning =
|
||||
startExecMutation.isPending ||
|
||||
submitRedMutation.isPending ||
|
||||
@@ -486,6 +505,7 @@ export default function TestDetailPage() {
|
||||
onStartBlueWork={() => startBlueWorkMutation.mutate()}
|
||||
onOpenValidateModal={(side) => setValidationModal({ open: true, side })}
|
||||
onOpenReviewModal={(side) => setReviewModal({ open: true, side })}
|
||||
onOpenResolveDisputeModal={() => setResolveDisputeModalOpen(true)}
|
||||
onReopen={() => setConfirmReopen(true)}
|
||||
onPauseTimer={() => pauseTimerMutation.mutate()}
|
||||
onResumeTimer={() => resumeTimerMutation.mutate()}
|
||||
@@ -780,6 +800,15 @@ export default function TestDetailPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Resolve Dispute Modal (approver flips vote to reject, picks a queue) */}
|
||||
{resolveDisputeModalOpen && (
|
||||
<ResolveDisputeModal
|
||||
isSubmitting={resolveDisputeMutation.isPending}
|
||||
onSubmit={handleResolveDisputeSubmit}
|
||||
onClose={() => setResolveDisputeModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Save as Template Modal */}
|
||||
{showTemplateModal && (
|
||||
<SaveAsTemplateModal
|
||||
|
||||
Reference in New Issue
Block a user