feat(assign): let leads manually assign operators + sync Jira; send platform as Jira label
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

Adds GET /users/operators (leads+admin) and wires a lead-only assign
control into the test detail header, calling the existing but
previously unreachable POST /tests/{id}/assign endpoint. Manual
assignment now also pushes the Jira ticket assignee immediately
instead of waiting for the operator to start execution.

Also adds test.platform as a kebab-case label on Jira ticket creation.
This commit is contained in:
kitos
2026-07-10 08:44:14 +02:00
parent 119db6f91d
commit 2afb886cd9
12 changed files with 487 additions and 4 deletions
@@ -19,8 +19,10 @@ import {
import { useMutation } from "@tanstack/react-query";
import { requestDiscussion } from "../../api/tests";
import type { Test, TestState, User, DataClassification } from "../../types/models";
import type { OperatorOut } from "../../api/users";
import LiveTimer from "./LiveTimer";
import DataClassificationBadge from "./DataClassificationBadge";
import AssigneeControl from "./AssigneeControl";
// ── Progress steps ─────────────────────────────────────────────────
@@ -90,6 +92,9 @@ interface TestDetailHeaderProps {
isTogglingHold: boolean;
onUpdateClassification: (value: DataClassification) => void;
isUpdatingClassification: boolean;
operators: OperatorOut[];
onAssignOperator: (side: "red" | "blue", userId: string | null) => void;
isAssigningOperator: boolean;
}
// ── Component ──────────────────────────────────────────────────────
@@ -114,6 +119,9 @@ export default function TestDetailHeader({
isTogglingHold,
onUpdateClassification,
isUpdatingClassification,
operators,
onAssignOperator,
isAssigningOperator,
}: TestDetailHeaderProps) {
const role = user?.role ?? "";
const currentIdx = STATE_INDEX[test.state];
@@ -539,6 +547,24 @@ export default function TestDetailHeader({
<p className="mt-1 text-sm text-gray-400">
Created {formatDate(test.created_at)}
</p>
<div className="mt-1.5 flex items-center gap-1.5">
<AssigneeControl
side="red"
assigneeId={test.red_tech_assignee}
operators={operators}
canEdit={role === "red_lead" || role === "admin"}
isSaving={isAssigningOperator}
onAssign={(userId) => onAssignOperator("red", userId)}
/>
<AssigneeControl
side="blue"
assigneeId={test.blue_tech_assignee}
operators={operators}
canEdit={role === "blue_lead" || role === "admin"}
isSaving={isAssigningOperator}
onAssign={(userId) => onAssignOperator("blue", userId)}
/>
</div>
{renderValidationIndicators()}
</div>
</div>