feat(review): review queue for leads + manual reviewer reassignment
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
Leads get the same two-queue view operators already have: 'Available
to Review' (pending reviews currently assigned to a peer lead) and
'My Assigned Reviews' (assigned to me), replacing the old single
'My Reviews' toggle. Since the load-balanced auto-assignment always
picks a reviewer immediately, 'available' means peer-assigned reviews
a lead could pick up if the assignee can't get to them, not unclaimed
ones (there aren't any).
POST /tests/{id}/assign now also accepts red_reviewer_assignee /
blue_reviewer_assignee, validated against the matching lead role and
synced to Jira the same way operator assignment already is. The
AssigneeControl UI gained a 'reviewer' kind (leads-only picker) shown
on the test detail header while a test sits in red_review/blue_review
— this also fixes the reviewer assignment being invisible in Aegis
even though it was already being pushed to Jira correctly.
This commit is contained in:
@@ -93,7 +93,10 @@ interface TestDetailHeaderProps {
|
||||
onUpdateClassification: (value: DataClassification) => void;
|
||||
isUpdatingClassification: boolean;
|
||||
operators: OperatorOut[];
|
||||
onAssignOperator: (side: "red" | "blue", userId: string | null) => void;
|
||||
onAssignOperator: (
|
||||
field: "red_tech_assignee" | "blue_tech_assignee" | "red_reviewer_assignee" | "blue_reviewer_assignee",
|
||||
userId: string | null,
|
||||
) => void;
|
||||
isAssigningOperator: boolean;
|
||||
}
|
||||
|
||||
@@ -551,13 +554,37 @@ export default function TestDetailHeader({
|
||||
|
||||
<div className="flex flex-col items-end gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{test.state === "red_review" && (
|
||||
<AssigneeControl
|
||||
side="red"
|
||||
kind="reviewer"
|
||||
assigneeId={test.red_reviewer_assignee}
|
||||
operators={operators}
|
||||
canEdit={role === "red_lead" || role === "manager"}
|
||||
isSaving={isAssigningOperator}
|
||||
onAssign={(userId) => onAssignOperator("red_reviewer_assignee", userId)}
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
{test.state === "blue_review" && (
|
||||
<AssigneeControl
|
||||
side="blue"
|
||||
kind="reviewer"
|
||||
assigneeId={test.blue_reviewer_assignee}
|
||||
operators={operators}
|
||||
canEdit={role === "blue_lead" || role === "manager"}
|
||||
isSaving={isAssigningOperator}
|
||||
onAssign={(userId) => onAssignOperator("blue_reviewer_assignee", userId)}
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
<AssigneeControl
|
||||
side="red"
|
||||
assigneeId={test.red_tech_assignee}
|
||||
operators={operators}
|
||||
canEdit={role === "red_lead" || role === "manager"}
|
||||
isSaving={isAssigningOperator}
|
||||
onAssign={(userId) => onAssignOperator("red", userId)}
|
||||
onAssign={(userId) => onAssignOperator("red_tech_assignee", userId)}
|
||||
size="lg"
|
||||
/>
|
||||
<AssigneeControl
|
||||
@@ -566,7 +593,7 @@ export default function TestDetailHeader({
|
||||
operators={operators}
|
||||
canEdit={role === "blue_lead" || role === "manager"}
|
||||
isSaving={isAssigningOperator}
|
||||
onAssign={(userId) => onAssignOperator("blue", userId)}
|
||||
onAssign={(userId) => onAssignOperator("blue_tech_assignee", userId)}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user