Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/test-plans/member_role_change_manual_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"**.

---

Expand Down
12 changes: 10 additions & 2 deletions src/lib/utils/__tests__/user-roles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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');
});
Comment on lines +82 to +88

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Avoid duplicate SuperAdmin coverage

This test repeats the existing SuperAdmin setup and exact ['Super Admin'] assertion, so future label changes require maintaining two equivalent cases. Consolidate the raw-value assertion into the existing test to retain the distinct check without duplicating the behavior.

Context Used: I want you to look at existing code patterns and e... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code


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, []);
Expand All @@ -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', () => {
Expand Down
18 changes: 14 additions & 4 deletions src/lib/utils/user-roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Comment on lines +14 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Trim redundant mapping commentary

This comment restates the visible mappings and fall-through implementation across two paragraphs, increasing documentation maintenance whenever role handling changes. Keep only the non-obvious rationale, or remove the comment where the names are already self-explanatory.

Suggested change
/**
* 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.
*/

Context Used: Use refactor-platform-fe's coding standards file f... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

const ROLE_DISPLAY_NAMES: Record<string, string> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Constrain display-name map keys

Record<string, string> accepts invalid role keys without a type error, allowing a typo to silently fall through and render the raw enum value. Typing this partial mapping with DisplayRole preserves the intentional fallback while rejecting unsupported keys.

Suggested change
const ROLE_DISPLAY_NAMES: Record<string, string> = {
const ROLE_DISPLAY_NAMES: Partial<Record<DisplayRole, string>> = {

Context Used: Use refactor-platform-fe's coding standards file f... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

[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
Expand Down Expand Up @@ -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();
}

Expand Down
Loading