From c0f7bb26eb1459b7fb4133157d3f75ec3dc3a71e Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Thu, 17 Sep 2026 01:06:03 -0300 Subject: [PATCH] fix(team-members): keep legacy roles and colors editable in the detail drawer Members created before the SH-326 model can carry data the current backend contract rejects (PUT team-members accepts only the Dispatcher/Scheduler/Admin roles and the ten palette colors), which made them impossible to edit. - Legacy roles (e.g. Manager) now show as " (legacy)" in the edit form with helper copy asking to choose Dispatcher, Scheduler, or Admin; save stays blocked until one is chosen and the legacy value is never sent. View mode marks the stored role as legacy too. - Stored colors outside the current palette are canonicalized when they only differ by casing; otherwise the nearest palette color (RGB distance, first as fallback) is preselected with a notice naming the replaced value, so saving never fails on an untouched legacy color. - Role/color options move to team-member-form-options.ts with unit tests for canonicalization, nearest-color matching, and replacement notices. --- .../_components/add-team-member-dialog.tsx | 100 ++++++++++++------ .../_components/team-member-detail-drawer.tsx | 21 +++- .../_components/team-member-form-options.ts | 78 ++++++++++++++ .../_components/team-member-permissions.ts | 2 +- .../team-member-detail-drawer.test.tsx | 79 ++++++++++++++ .../team-member-form-options.test.ts | 54 ++++++++++ 6 files changed, 295 insertions(+), 39 deletions(-) create mode 100644 src/app/(protected)/team-members/_components/team-member-form-options.ts create mode 100644 src/test/app/(protected)/team-members/team-member-form-options.test.ts diff --git a/src/app/(protected)/team-members/_components/add-team-member-dialog.tsx b/src/app/(protected)/team-members/_components/add-team-member-dialog.tsx index 21aea977..170f3ccf 100644 --- a/src/app/(protected)/team-members/_components/add-team-member-dialog.tsx +++ b/src/app/(protected)/team-members/_components/add-team-member-dialog.tsx @@ -36,26 +36,16 @@ import { PERMISSION_LABELS, permissionOverrides, } from "./team-member-permissions"; +import { TEAM_MEMBER_COLORS, TEAM_MEMBER_ROLES } from "./team-member-form-options"; -const ROLES: TeamMemberRole[] = ["Dispatcher", "Scheduler", "Admin"]; -const COLORS = [ - "#F59E0B", - "#0D9488", - "#6366F1", - "#F43F5E", - "#7C3AED", - "#0EA5E9", - "#16A34A", - "#EA580C", - "#0891B2", - "#C026D3", -] as const; +const LEGACY_ROLE_OPTION = "__legacy-role__"; function emptyForm() { return { name: "", - role: "Dispatcher" as TeamMemberRole, + role: "Dispatcher" as TeamMemberRole | "", color: "", + colorNotice: "", email: "", phone: "", active: true, @@ -109,6 +99,53 @@ function PermissionsSection({ ); } +function RoleSelectField({ + role, + roleError, + updateRole, + disabled, + legacyRole, +}: { + role: TeamMemberRole | ""; + roleError: string; + updateRole: (role: TeamMemberRole) => void; + disabled: boolean; + legacyRole: string; +}) { + const legacyRolePending = Boolean(legacyRole) && !role; + + return ( + + Role + + {legacyRolePending && ( + + {legacyRole} is a legacy role and can no longer be saved. Choose Dispatcher, Scheduler, or + Admin. + + )} + {!legacyRolePending && Boolean(roleError) && {roleError}} + + ); +} + export function TeamMemberFormFields({ form, setForm, @@ -117,6 +154,7 @@ export function TeamMemberFormFields({ toggleArea, togglePermission, roleDisabled = false, + legacyRole = "", }: { form: TeamMemberForm; setForm: Dispatch>; @@ -125,6 +163,7 @@ export function TeamMemberFormFields({ toggleArea: (area: string) => void; togglePermission: (key: string) => void; roleDisabled?: boolean; + legacyRole?: string; }) { return ( @@ -138,36 +177,26 @@ export function TeamMemberFormFields({ required fullWidth /> - - Role - - {Boolean(errors.role) && {errors.role}} - + Color - {COLORS.map((color) => ( + {TEAM_MEMBER_COLORS.map((color) => (