From 2ce28486407b199e7d128069f7bc3873462f45bb Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 27 Mar 2026 18:03:54 +0000 Subject: [PATCH] fix(portal): normalize legacy team roles --- .../cloudcp/portal/dist/build_manifest.json | 2 +- internal/cloudcp/portal/dist/portal_app.js | 26 ++++++++++------- .../portal/frontend/src/account_view.test.ts | 28 +++++++++++++++++++ .../portal/frontend/src/account_view.ts | 20 ++++++++----- .../portal/frontend/src/shell_view.test.ts | 2 ++ .../cloudcp/portal/frontend/src/shell_view.ts | 19 +++++++++---- 6 files changed, 74 insertions(+), 23 deletions(-) diff --git a/internal/cloudcp/portal/dist/build_manifest.json b/internal/cloudcp/portal/dist/build_manifest.json index ddc868e9cf..e4adead051 100644 --- a/internal/cloudcp/portal/dist/build_manifest.json +++ b/internal/cloudcp/portal/dist/build_manifest.json @@ -1,5 +1,5 @@ { - "source_hash": "b48ed843c8ab211f26d40bdb069e5a6a3f35c206eedef2cf15301ca0f640e8e3", + "source_hash": "b99ef3907290e1285e448dd8af5af07b4767f6fe4f6afd76d1b46722980f5a18", "build_inputs": [ "package.json", "tsconfig.json", diff --git a/internal/cloudcp/portal/dist/portal_app.js b/internal/cloudcp/portal/dist/portal_app.js index 87bd71140f..5580b814e6 100644 --- a/internal/cloudcp/portal/dist/portal_app.js +++ b/internal/cloudcp/portal/dist/portal_app.js @@ -1,7 +1,11 @@ (() => { // src/account_view.ts + function normalizedTeamRole(role) { + if (role === "member") return "read_only"; + return role || "read_only"; + } function roleLabel(role) { - switch (role) { + switch (normalizedTeamRole(role)) { case "owner": return "Owner"; case "admin": @@ -17,7 +21,7 @@ } } function roleCapabilityCopy(role) { - switch (role) { + switch (normalizedTeamRole(role)) { case "owner": return "Full account control, including billing, team access, and hosted operations."; case "admin": @@ -110,7 +114,7 @@ function countMembersByRole(members, role) { var count = 0; for (var i = 0; i < members.length; i += 1) { - if (members[i].role === role) count += 1; + if (normalizedTeamRole(members[i].role) === role) count += 1; } return count; } @@ -133,10 +137,11 @@ stats.innerHTML = '
Members' + String(members.length) + '
Owners' + String(countMembersByRole(members, "owner")) + '
Admins' + String(countMembersByRole(members, "admin")) + '
Operators' + String(countMembersByRole(members, "tech") + countMembersByRole(members, "read_only")) + "
"; } function renderTeamRoleControl(accountID, member, isOwner) { - if (member.role === "owner" && !isOwner) { + var currentRole = normalizedTeamRole(member.role); + if (currentRole === "owner" && !isOwner) { var locked = document.createElement("span"); locked.className = "team-role-badge"; - locked.textContent = roleLabel(member.role); + locked.textContent = roleLabel(currentRole); return locked; } var sel = document.createElement("select"); @@ -146,7 +151,7 @@ var opt = document.createElement("option"); opt.value = roles[j]; opt.textContent = roleLabel(roles[j]); - if (member.role === roles[j]) opt.selected = true; + if (currentRole === roles[j]) opt.selected = true; sel.appendChild(opt); } sel.setAttribute("data-action", "change-role"); @@ -155,7 +160,7 @@ return sel; } function renderTeamMemberAction(accountID, member, isOwner) { - if (member.role === "owner" && !isOwner) { + if (normalizedTeamRole(member.role) === "owner" && !isOwner) { return null; } var btn = document.createElement("button"); @@ -1763,11 +1768,12 @@ } function renderAccountOverviewSection(account) { var workspaces = Array.isArray(account.workspaces) ? account.workspaces : []; - var summaryText = accountKindLabel(account) + " \xB7 " + titleCase(account.role) + " \xB7 " + workspaceCountLabel(workspaces.length); var healthyCount = countWorkspacesByHealth(workspaces, "healthy"); var checkingCount = countWorkspacesByHealth(workspaces, "checking"); var unhealthyCount = countWorkspacesByHealth(workspaces, "unhealthy"); var activeCount = countWorkspacesByState(workspaces, "active"); + var postureTitle = unhealthyCount > 0 ? "Hosted posture needs review" : checkingCount > 0 ? "Hosted posture is still settling" : "Hosted posture is stable"; + var postureCopy = unhealthyCount > 0 ? "One or more workspaces still need attention before the hosted fleet is trustworthy." : checkingCount > 0 ? "The hosted fleet is mostly healthy, but some workspaces are still waiting on a completed health check." : "The hosted fleet is healthy and ready for routine operator work."; var operationsCopy = account.kind === "msp" ? "Manage the client fleet from this account surface. Workspace creation, billing, and team actions belong here." : "Use this account surface to open hosted workspaces, manage billing, and control access for this hosted account."; var actions = ""; var addWorkspaceForm = ""; @@ -1777,8 +1783,8 @@ addWorkspaceForm = '
'; } } - return '