diff --git a/docs/test-plans/member_role_change_manual_testing.md b/docs/test-plans/member_role_change_manual_testing.md index f0f8d911..006779d0 100644 --- a/docs/test-plans/member_role_change_manual_testing.md +++ b/docs/test-plans/member_role_change_manual_testing.md @@ -66,9 +66,9 @@ organization admin access"** on an admin's. Never both, and never the role alrea scoped to the organization in wording, so neither is mistaken for global SuperAdmin. A success toast confirms the change ("{name} is now an organization Admin" / "…is no longer an organization Admin"). -The row's `Roles:` line separately carries Coach/Coachee and SuperAdmin. It renders the plain -organization role as **"Member"** — the same word the menu and the add-member dialog use — rather -than the raw `User` enum value. +The row's `Roles:` line separately carries Coach/Coachee and Super Admin. It renders roles by their +recipient-facing names rather than raw enum values: `User` shows as **"Member"** (the same word the +menu and the add-member dialog use) and `SuperAdmin` as **"Super Admin"**. --- diff --git a/src/lib/utils/__tests__/user-roles.test.ts b/src/lib/utils/__tests__/user-roles.test.ts index 4d6f2010..33e75fd7 100644 --- a/src/lib/utils/__tests__/user-roles.test.ts +++ b/src/lib/utils/__tests__/user-roles.test.ts @@ -57,7 +57,7 @@ describe('getUserDisplayRoles', () => { const user = createUser([{ role: Role.SuperAdmin, organization_id: null }]); const roles = getUserDisplayRoles(user, organizationId, []); - expect(roles).toEqual(['SuperAdmin']); + expect(roles).toEqual(['Super Admin']); }); it('should combine organization role and coaching roles', () => { @@ -79,6 +79,14 @@ describe('getUserDisplayRoles', () => { expect(roles).toEqual(['Coach', 'Coachee', 'Member']); }); + it('renders Role.SuperAdmin as "Super Admin", not the raw enum value', () => { + const user = createUser([{ role: Role.SuperAdmin, organization_id: null }]); + const roles = getUserDisplayRoles(user, organizationId, []); + + expect(roles).toEqual(['Super Admin']); + expect(roles).not.toContain('SuperAdmin'); + }); + it('renders Role.User as "Member", the word the rest of the UI uses', () => { const user = createUser([{ role: Role.User, organization_id: organizationId }]); const roles = getUserDisplayRoles(user, organizationId, []); @@ -98,7 +106,7 @@ describe('getUserDisplayRoles', () => { ]; const roles = getUserDisplayRoles(user, organizationId, relationships); - expect(roles).toEqual(['Admin', 'Coach', 'Coachee', 'SuperAdmin']); + expect(roles).toEqual(['Admin', 'Coach', 'Coachee', 'Super Admin']); }); it('should not duplicate roles', () => { diff --git a/src/lib/utils/user-roles.ts b/src/lib/utils/user-roles.ts index 74cf934a..ad15c647 100644 --- a/src/lib/utils/user-roles.ts +++ b/src/lib/utils/user-roles.ts @@ -11,6 +11,19 @@ import { type Option, Some, None } from "@/types/option"; export type DisplayRole = Role | RelationshipRole +/** + * Recipient-facing names for roles whose enum value is not what we want to show + * a user. `Role.Admin` and the relationship roles already read correctly, so + * they are absent and fall through unchanged. + * + * "Member" matches the add-member dialog and the row's actions menu; rendering + * the raw "User" here taught a second name for the same role. + */ +const ROLE_DISPLAY_NAMES: Record = { + [Role.User]: "Member", + [Role.SuperAdmin]: "Super Admin", +}; + /** * Gets display roles for a user combining organization roles and coaching relationship roles * @param user - The user to get roles for @@ -47,11 +60,8 @@ export function getUserDisplayRoles( roles.add(RelationshipRole.Coachee); } - // "Member" is the recipient-facing word for Role.User, matching the add-member - // dialog and the row's actions menu. Showing the raw "User" here taught a - // different name for the same role. return Array.from(roles) - .map(role => (role === Role.User ? "Member" : (role as string))) + .map(role => ROLE_DISPLAY_NAMES[role] ?? (role as string)) .sort(); }