style(portal): add section canvases and workspace desk

This commit is contained in:
rcourtman
2026-03-27 18:47:25 +00:00
parent 9b4cd6471e
commit 497dbe8076
5 changed files with 120 additions and 19 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{
"source_hash": "ddd93142cd52f630277d943b39580c9f06d810ca3dffe1b96ab87ab9705ee6ed",
"source_hash": "8b0b284da60918f341d35eb2e51aed2893c7af4b399d949fd6dc0cef3e166408",
"build_inputs": [
"package.json",
"tsconfig.json",
+42 -8
View File
@@ -541,13 +541,14 @@ header .logout-btn:hover,
.account-content-panel {
display: block;
}
.account-content-panel-overview,
.account-content-panel-workspaces,
.account-content-panel-team {
padding: 16px;
padding: 18px;
border: 1px solid var(--line);
border-radius: var(--radius-md);
background: var(--panel);
box-shadow: var(--shadow-sm);
border-radius: var(--radius-lg);
background: #eef3f7;
box-shadow: 0 12px 28px rgba(10, 25, 41, 0.045);
}
.account-command-deck,
.account-overview-card,
@@ -604,6 +605,10 @@ header .logout-btn:hover,
grid-template-columns: 1fr;
gap: 14px;
align-items: stretch;
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(255, 255, 255, 0.6);
}
.account-overview-card {
display: grid;
@@ -660,7 +665,6 @@ header .logout-btn:hover,
gap: 18px;
grid-column: 1 / -1;
padding: 16px;
border-top: 1px solid var(--line);
background: var(--panel);
border-radius: var(--radius-md);
}
@@ -814,7 +818,7 @@ header .logout-btn:hover,
color: var(--ink-soft);
}
.account-stage-header {
padding: 0 0 12px;
padding: 0 0 14px;
border-bottom: 1px solid var(--line);
margin-bottom: 14px;
}
@@ -822,6 +826,10 @@ header .logout-btn:hover,
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
}
.workspace-list-toolbar {
display: flex;
@@ -957,6 +965,10 @@ header .logout-btn:hover,
grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.9fr);
gap: 14px;
align-items: start;
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(255, 255, 255, 0.56);
}
.workspace-operations-main,
.workspace-operations-detail {
@@ -988,11 +1000,29 @@ header .logout-btn:hover,
}
.workspace-management-empty {
margin-top: 16px;
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
border-radius: var(--radius-sm);
background: var(--panel-muted);
color: var(--ink-soft);
}
.workspace-management-empty-copy {
font-size: 14px;
line-height: 1.5;
color: var(--ink);
}
.workspace-management-empty-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.workspace-management-empty-note {
font-size: 12px;
line-height: 1.5;
color: var(--ink-soft);
}
.workspace-management-content {
margin-top: 16px;
}
@@ -1073,8 +1103,8 @@ header .logout-btn:hover,
}
.team-roster,
.team-invite-panel {
background: var(--panel-muted);
border-color: var(--line-strong);
background: #fff;
border-color: var(--line);
}
.team-panel-heading {
margin-bottom: 12px;
@@ -1241,6 +1271,10 @@ header .logout-btn:hover,
grid-template-columns: minmax(280px, 0.82fr) minmax(0, 1.18fr);
gap: 16px;
align-items: start;
padding: 16px;
border: 1px solid var(--line);
border-radius: var(--radius-lg);
background: #eef3f7;
}
.service-shell-main,
.service-shell-sidebar {
+9 -1
View File
@@ -1797,7 +1797,15 @@
var unhealthyCount = countWorkspacesByHealth(workspaces, "unhealthy");
var workspaceManagement = "";
if (account.can_manage) {
workspaceManagement = '<section class="workspace-management-panel" id="workspace-management-' + escapeAttr(account.id) + '"><div class="workspace-management-header"><div><div class="account-panel-kicker">Workspace management</div><h3>Review one workspace at a time</h3><p>Select a workspace from the fleet to review its lifecycle state and run explicit management actions.</p></div><button type="button" class="btn-secondary btn-compact" id="workspace-management-close-' + escapeAttr(account.id) + '" data-action="clear-workspace-selection" data-account-id="' + escapeAttr(account.id) + '">Done</button></div><div class="workspace-management-empty" id="workspace-management-empty-' + escapeAttr(account.id) + '">Choose a workspace to manage from the fleet above.</div><div class="workspace-management-content" id="workspace-management-content-' + escapeAttr(account.id) + '" hidden><div class="workspace-management-meta" id="workspace-management-meta-' + escapeAttr(account.id) + '"></div><h4 id="workspace-management-title-' + escapeAttr(account.id) + '"></h4><p class="workspace-management-summary" id="workspace-management-summary-' + escapeAttr(account.id) + '"></p><div class="workspace-management-actions"><button type="button" class="btn-danger" id="workspace-management-action-' + escapeAttr(account.id) + '" data-action="workspace-action" data-account-id="' + escapeAttr(account.id) + '">Manage workspace</button></div></div></section>';
var workspaceDeskActions = "";
if (account.kind === "msp") {
workspaceDeskActions += '<button type="button" class="btn-secondary btn-compact" data-action="toggle-add-workspace" data-account-id="' + escapeAttr(account.id) + '">Add workspace</button>';
}
if (account.has_billing) {
workspaceDeskActions += '<button type="button" class="btn-secondary btn-compact" data-action="open-billing" data-account-id="' + escapeAttr(account.id) + '">Manage billing</button>';
}
workspaceDeskActions += '<button type="button" class="btn-secondary btn-compact" data-action="toggle-team" data-account-id="' + escapeAttr(account.id) + '" data-shell-target="team">Manage team</button>';
workspaceManagement = '<section class="workspace-management-panel" id="workspace-management-' + escapeAttr(account.id) + '"><div class="workspace-management-header"><div><div class="account-panel-kicker">Workspace management</div><h3>Review one workspace at a time</h3><p>Select a workspace from the fleet to review its lifecycle state and run explicit management actions.</p></div><button type="button" class="btn-secondary btn-compact" id="workspace-management-close-' + escapeAttr(account.id) + '" data-action="clear-workspace-selection" data-account-id="' + escapeAttr(account.id) + '">Done</button></div><div class="workspace-management-empty" id="workspace-management-empty-' + escapeAttr(account.id) + '"><div class="workspace-management-empty-copy">Choose a workspace to manage from the fleet above.</div><div class="workspace-management-empty-actions">' + workspaceDeskActions + '</div><div class="workspace-management-empty-note">Use the fleet table for workspace-level work, or run account-wide billing and team actions from here.</div></div><div class="workspace-management-content" id="workspace-management-content-' + escapeAttr(account.id) + '" hidden><div class="workspace-management-meta" id="workspace-management-meta-' + escapeAttr(account.id) + '"></div><h4 id="workspace-management-title-' + escapeAttr(account.id) + '"></h4><p class="workspace-management-summary" id="workspace-management-summary-' + escapeAttr(account.id) + '"></p><div class="workspace-management-actions"><button type="button" class="btn-danger" id="workspace-management-action-' + escapeAttr(account.id) + '" data-action="workspace-action" data-account-id="' + escapeAttr(account.id) + '">Manage workspace</button></div></div></section>';
}
var workspaceHTML = workspaces.length ? '<div class="workspace-list-wrap"><div class="workspace-list-toolbar"><div class="workspace-list-summary">Review the hosted fleet, open a workspace, and keep lifecycle actions explicit.</div><div class="workspace-list-stats"><span class="workspace-list-stat"><strong>' + String(workspaces.length) + '</strong> total</span><span class="workspace-list-stat"><strong>' + String(healthyCount) + '</strong> healthy</span><span class="workspace-list-stat"><strong>' + String(checkingCount) + '</strong> checking</span><span class="workspace-list-stat workspace-list-stat-attention"><strong>' + String(unhealthyCount) + '</strong> needs attention</span></div></div><div class="workspace-list-head"><span>Workspace</span><span>Health</span><span>Lifecycle</span><span>Actions</span></div><div class="workspace-list">' + workspaces.map(function(workspace) {
return renderWorkspaceCard(account, workspace, accountAPIBasePath);
@@ -469,6 +469,24 @@ function renderAccountWorkspaceSection(account: PortalAccountSummary, accountAPI
var unhealthyCount = countWorkspacesByHealth(workspaces, 'unhealthy');
var workspaceManagement = '';
if (account.can_manage) {
var workspaceDeskActions = '';
if (account.kind === 'msp') {
workspaceDeskActions +=
'<button type="button" class="btn-secondary btn-compact" data-action="toggle-add-workspace" data-account-id="' +
escapeAttr(account.id) +
'">Add workspace</button>';
}
if (account.has_billing) {
workspaceDeskActions +=
'<button type="button" class="btn-secondary btn-compact" data-action="open-billing" data-account-id="' +
escapeAttr(account.id) +
'">Manage billing</button>';
}
workspaceDeskActions +=
'<button type="button" class="btn-secondary btn-compact" data-action="toggle-team" data-account-id="' +
escapeAttr(account.id) +
'" data-shell-target="team">Manage team</button>';
workspaceManagement =
'<section class="workspace-management-panel" id="workspace-management-' +
escapeAttr(account.id) +
@@ -487,7 +505,11 @@ function renderAccountWorkspaceSection(account: PortalAccountSummary, accountAPI
'</div>' +
'<div class="workspace-management-empty" id="workspace-management-empty-' +
escapeAttr(account.id) +
'">Choose a workspace to manage from the fleet above.</div>' +
'">' +
'<div class="workspace-management-empty-copy">Choose a workspace to manage from the fleet above.</div>' +
'<div class="workspace-management-empty-actions">' + workspaceDeskActions + '</div>' +
'<div class="workspace-management-empty-note">Use the fleet table for workspace-level work, or run account-wide billing and team actions from here.</div>' +
'</div>' +
'<div class="workspace-management-content" id="workspace-management-content-' +
escapeAttr(account.id) +
'" hidden>' +
@@ -595,13 +595,14 @@ header .logout-btn:hover,
display: block;
}
.account-content-panel-overview,
.account-content-panel-workspaces,
.account-content-panel-team {
padding: 16px;
padding: 18px;
border: 1px solid var(--line);
border-radius: var(--radius-md);
background: var(--panel);
box-shadow: var(--shadow-sm);
border-radius: var(--radius-lg);
background: #eef3f7;
box-shadow: 0 12px 28px rgba(10, 25, 41, 0.045);
}
.account-command-deck,
@@ -663,6 +664,10 @@ header .logout-btn:hover,
grid-template-columns: 1fr;
gap: 14px;
align-items: stretch;
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(255, 255, 255, 0.6);
}
.account-overview-card {
@@ -721,7 +726,6 @@ header .logout-btn:hover,
gap: 18px;
grid-column: 1 / -1;
padding: 16px;
border-top: 1px solid var(--line);
background: var(--panel);
border-radius: var(--radius-md);
}
@@ -903,7 +907,7 @@ header .logout-btn:hover,
}
.account-stage-header {
padding: 0 0 12px;
padding: 0 0 14px;
border-bottom: 1px solid var(--line);
margin-bottom: 14px;
}
@@ -912,6 +916,10 @@ header .logout-btn:hover,
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
}
.workspace-list-toolbar {
@@ -1068,6 +1076,10 @@ header .logout-btn:hover,
grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.9fr);
gap: 14px;
align-items: start;
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(255, 255, 255, 0.56);
}
.workspace-operations-main,
@@ -1106,12 +1118,33 @@ header .logout-btn:hover,
.workspace-management-empty {
margin-top: 16px;
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
border-radius: var(--radius-sm);
background: var(--panel-muted);
color: var(--ink-soft);
}
.workspace-management-empty-copy {
font-size: 14px;
line-height: 1.5;
color: var(--ink);
}
.workspace-management-empty-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.workspace-management-empty-note {
font-size: 12px;
line-height: 1.5;
color: var(--ink-soft);
}
.workspace-management-content {
margin-top: 16px;
}
@@ -1204,8 +1237,8 @@ header .logout-btn:hover,
.team-roster,
.team-invite-panel {
background: var(--panel-muted);
border-color: var(--line-strong);
background: #fff;
border-color: var(--line);
}
.team-panel-heading {
@@ -1397,6 +1430,10 @@ header .logout-btn:hover,
grid-template-columns: minmax(280px, 0.82fr) minmax(0, 1.18fr);
gap: 16px;
align-items: start;
padding: 16px;
border: 1px solid var(--line);
border-radius: var(--radius-lg);
background: #eef3f7;
}
.service-shell-main,