From 3793cc161bff26dd057973f6275b0a68ae3149c6 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 27 Mar 2026 23:10:52 +0000 Subject: [PATCH] style(portal): improve team roster state views --- .../cloudcp/portal/dist/build_manifest.json | 2 +- internal/cloudcp/portal/dist/portal_app.css | 28 ++++++++++++----- internal/cloudcp/portal/dist/portal_app.js | 7 +++++ .../portal/frontend/src/account_view.ts | 7 +++++ .../cloudcp/portal/frontend/src/styles.css | 30 ++++++++++++++----- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/internal/cloudcp/portal/dist/build_manifest.json b/internal/cloudcp/portal/dist/build_manifest.json index bc2659c0b7..2967777d98 100644 --- a/internal/cloudcp/portal/dist/build_manifest.json +++ b/internal/cloudcp/portal/dist/build_manifest.json @@ -1,5 +1,5 @@ { - "source_hash": "7a0f2e57cde80096b6b3e6f00199572003416ff27bb9288a1228175a2e70bf63", + "source_hash": "5c0116abefe33e38423a53786fa29fe46555d46fa8bc8dcea9ca2063cb1a7cc2", "build_inputs": [ "package.json", "tsconfig.json", diff --git a/internal/cloudcp/portal/dist/portal_app.css b/internal/cloudcp/portal/dist/portal_app.css index 2a1b432265..b21f0980e6 100644 --- a/internal/cloudcp/portal/dist/portal_app.css +++ b/internal/cloudcp/portal/dist/portal_app.css @@ -1276,6 +1276,12 @@ header .logout-btn:hover, .team-roster { align-self: start; } +.team-roster.state-only { + border: none; + background: transparent; + box-shadow: none; + padding: 0; +} .team-operations-panel { display: flex; flex-direction: column; @@ -1313,6 +1319,12 @@ header .logout-btn:hover, background: #fff; overflow: hidden; } +.team-roster-list.state-only { + border: none; + border-radius: 0; + background: transparent; + overflow: visible; +} .team-roster-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; @@ -1401,12 +1413,13 @@ header .logout-btn:hover, flex-direction: column; align-items: flex-start; justify-content: center; - gap: 6px; - min-height: 148px; - padding: 18px; - border: 1px dashed var(--line-strong); - border-radius: 10px; - background: var(--panel-muted); + gap: 8px; + min-height: 196px; + padding: 22px; + border: 1px solid var(--line); + border-radius: 12px; + background: #f8fbfd; + box-shadow: inset 0 0 0 1px rgba(11, 106, 114, 0.04); } .team-list-message-title { font-size: 16px; @@ -1420,8 +1433,9 @@ header .logout-btn:hover, color: var(--ink-soft); } .team-list-message.error { - border-color: rgba(166, 27, 27, 0.2); + border-color: rgba(166, 27, 27, 0.18); background: var(--danger-soft); + box-shadow: inset 4px 0 0 rgba(166, 27, 27, 0.26); } .team-list-message.error .team-list-message-title, .team-list-message.error .team-list-message-copy { diff --git a/internal/cloudcp/portal/dist/portal_app.js b/internal/cloudcp/portal/dist/portal_app.js index 31902ec90a..317b0c022d 100644 --- a/internal/cloudcp/portal/dist/portal_app.js +++ b/internal/cloudcp/portal/dist/portal_app.js @@ -144,6 +144,7 @@ } function setContainerMessage(container, title, msg, isError) { container.textContent = ""; + container.classList.add("state-only"); var message = document.createElement("div"); message.className = "team-list-message" + (isError ? " error" : ""); var heading = document.createElement("strong"); @@ -268,6 +269,7 @@ var section = getElement("team-section-" + accountID); var roster = getElement("team-list-" + accountID); if (!section || !roster) return; + var rosterPanel = roster.closest(".team-roster"); var actorRole = section.getAttribute("data-actor-role") || ""; var isOwner = actorRole === "owner"; section.classList.toggle("visible", entry.teamVisible); @@ -276,18 +278,23 @@ return; } if (entry.teamQuery.status === "loading") { + if (rosterPanel) rosterPanel.classList.add("state-only"); setContainerMessage(roster, "Loading roster", "Checking who currently has access to this account.", false); return; } if (entry.teamQuery.status === "error") { + if (rosterPanel) rosterPanel.classList.add("state-only"); setContainerMessage(roster, "Roster needs attention", entry.teamQuery.error, true); return; } if (!entry.teamQuery.data.length) { + if (rosterPanel) rosterPanel.classList.add("state-only"); setContainerMessage(roster, "No operators yet", "Invite someone new when this hosted account needs shared access.", false); return; } roster.textContent = ""; + roster.classList.remove("state-only"); + if (rosterPanel) rosterPanel.classList.remove("state-only"); ensureRosterHead(roster); for (var i = 0; i < entry.teamQuery.data.length; i += 1) { var member = entry.teamQuery.data[i]; diff --git a/internal/cloudcp/portal/frontend/src/account_view.ts b/internal/cloudcp/portal/frontend/src/account_view.ts index 73825a08f2..15cb9f6cf4 100644 --- a/internal/cloudcp/portal/frontend/src/account_view.ts +++ b/internal/cloudcp/portal/frontend/src/account_view.ts @@ -169,6 +169,7 @@ export function renderWorkspaceManagement(account: PortalAccountSummary, entry: function setContainerMessage(container: HTMLElement, title: string, msg: string, isError: boolean): void { container.textContent = ''; + container.classList.add('state-only'); var message = document.createElement('div'); message.className = 'team-list-message' + (isError ? ' error' : ''); var heading = document.createElement('strong'); @@ -322,6 +323,7 @@ export function renderTeamSection(accountID: string, entry: PortalAccountUIEntry var section = getElement('team-section-' + accountID); var roster = getElement('team-list-' + accountID); if (!section || !roster) return; + var rosterPanel = roster.closest('.team-roster') as HTMLElement | null; var actorRole = section.getAttribute('data-actor-role') || ''; var isOwner = actorRole === 'owner'; @@ -332,19 +334,24 @@ export function renderTeamSection(accountID: string, entry: PortalAccountUIEntry return; } if (entry.teamQuery.status === 'loading') { + if (rosterPanel) rosterPanel.classList.add('state-only'); setContainerMessage(roster, 'Loading roster', 'Checking who currently has access to this account.', false); return; } if (entry.teamQuery.status === 'error') { + if (rosterPanel) rosterPanel.classList.add('state-only'); setContainerMessage(roster, 'Roster needs attention', entry.teamQuery.error, true); return; } if (!entry.teamQuery.data.length) { + if (rosterPanel) rosterPanel.classList.add('state-only'); setContainerMessage(roster, 'No operators yet', 'Invite someone new when this hosted account needs shared access.', false); return; } roster.textContent = ''; + roster.classList.remove('state-only'); + if (rosterPanel) rosterPanel.classList.remove('state-only'); ensureRosterHead(roster); for (var i = 0; i < entry.teamQuery.data.length; i += 1) { var member = entry.teamQuery.data[i]; diff --git a/internal/cloudcp/portal/frontend/src/styles.css b/internal/cloudcp/portal/frontend/src/styles.css index 095957c71a..23bcf7fe49 100644 --- a/internal/cloudcp/portal/frontend/src/styles.css +++ b/internal/cloudcp/portal/frontend/src/styles.css @@ -1440,6 +1440,13 @@ header .logout-btn:hover, align-self: start; } +.team-roster.state-only { + border: none; + background: transparent; + box-shadow: none; + padding: 0; +} + .team-operations-panel { display: flex; flex-direction: column; @@ -1484,6 +1491,13 @@ header .logout-btn:hover, overflow: hidden; } +.team-roster-list.state-only { + border: none; + border-radius: 0; + background: transparent; + overflow: visible; +} + .team-roster-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; @@ -1583,12 +1597,13 @@ header .logout-btn:hover, flex-direction: column; align-items: flex-start; justify-content: center; - gap: 6px; - min-height: 148px; - padding: 18px; - border: 1px dashed var(--line-strong); - border-radius: 10px; - background: var(--panel-muted); + gap: 8px; + min-height: 196px; + padding: 22px; + border: 1px solid var(--line); + border-radius: 12px; + background: #f8fbfd; + box-shadow: inset 0 0 0 1px rgba(11, 106, 114, 0.04); } .team-list-message-title { @@ -1605,8 +1620,9 @@ header .logout-btn:hover, } .team-list-message.error { - border-color: rgba(166, 27, 27, 0.2); + border-color: rgba(166, 27, 27, 0.18); background: var(--danger-soft); + box-shadow: inset 4px 0 0 rgba(166, 27, 27, 0.26); } .team-list-message.error .team-list-message-title,