diff --git a/internal/cloudcp/portal/dist/build_manifest.json b/internal/cloudcp/portal/dist/build_manifest.json
index 2fa9f4fd34..7b2181efb8 100644
--- a/internal/cloudcp/portal/dist/build_manifest.json
+++ b/internal/cloudcp/portal/dist/build_manifest.json
@@ -1,5 +1,5 @@
{
- "source_hash": "b4d41db9eab964bde794244e2c7d9d7e0aa77de1523520c2c448d1f08cfe969f",
+ "source_hash": "640380cc9fd2cd2036a3ac2a9f949e018d3adfb7a6953cc3aa46c8d799980740",
"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 b99d432e4d..b62540a6d0 100644
--- a/internal/cloudcp/portal/dist/portal_app.css
+++ b/internal/cloudcp/portal/dist/portal_app.css
@@ -953,7 +953,7 @@ header .logout-btn:hover,
}
.workspace-operations-shell {
display: grid;
- grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.9fr);
+ grid-template-columns: minmax(0, 1.42fr) minmax(360px, 1fr);
gap: 14px;
align-items: start;
padding: 16px;
@@ -977,10 +977,9 @@ header .logout-btn:hover,
background: #fff;
}
.workspace-management-content {
- display: grid;
- grid-template-columns: minmax(0, 1fr) auto;
- gap: 16px;
- align-items: end;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
}
.workspace-management-header,
.team-management-header {
@@ -1004,16 +1003,50 @@ header .logout-btn:hover,
line-height: 1.5;
color: var(--ink);
}
+.workspace-management-empty-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ gap: 12px;
+}
+.workspace-management-empty-card {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 14px;
+ border: 1px solid var(--line);
+ border-radius: 12px;
+ background: #fff;
+}
+.workspace-management-empty-card h4 {
+ margin: 0;
+ font-size: 17px;
+ line-height: 1.1;
+ letter-spacing: -0.025em;
+}
+.workspace-management-empty-card p {
+ margin: 0;
+ font-size: 13px;
+ line-height: 1.55;
+ color: var(--ink-soft);
+}
+.workspace-management-empty-card-muted {
+ background: var(--panel-muted);
+}
+.workspace-management-empty-list {
+ margin: 0;
+ padding-left: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ color: var(--ink-soft);
+ font-size: 12px;
+ line-height: 1.5;
+}
.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;
}
@@ -1024,6 +1057,54 @@ header .logout-btn:hover,
text-transform: uppercase;
color: var(--ink-muted);
}
+.workspace-management-facts {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 10px;
+}
+.workspace-management-fact {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ padding: 12px;
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ background: var(--panel-muted);
+}
+.workspace-management-fact span {
+ font-size: 11px;
+ font-weight: 800;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--ink-muted);
+}
+.workspace-management-fact strong {
+ font-size: 15px;
+ line-height: 1.2;
+ letter-spacing: -0.02em;
+ color: var(--ink);
+}
+.workspace-management-guidance {
+ padding: 14px;
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ background: #fff;
+ font-size: 13px;
+ line-height: 1.55;
+ color: var(--ink-soft);
+}
+.workspace-management-actions {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: 10px;
+ margin-top: 4px;
+}
+.workspace-management-actions .btn-danger {
+ width: 100%;
+ justify-content: center;
+ max-width: 220px;
+}
.add-workspace-form,
.team-section,
.service-panel {
@@ -1697,6 +1778,9 @@ header .logout-btn:hover,
.workspace-management-content {
grid-template-columns: 1fr;
}
+ .workspace-management-facts {
+ grid-template-columns: 1fr;
+ }
.workspace-list-head {
display: none;
}
diff --git a/internal/cloudcp/portal/dist/portal_app.js b/internal/cloudcp/portal/dist/portal_app.js
index ceb8c1d250..d78f12e24f 100644
--- a/internal/cloudcp/portal/dist/portal_app.js
+++ b/internal/cloudcp/portal/dist/portal_app.js
@@ -54,6 +54,32 @@
if (workspace.health_status === "unhealthy") return "This workspace needs attention before it is trustworthy.";
return "This workspace is still waiting on a completed health check.";
}
+ function workspaceHealthLabel(workspace) {
+ if (workspace.health_status === "healthy") return "Healthy";
+ if (workspace.health_status === "unhealthy") return "Needs attention";
+ return "Checking";
+ }
+ function workspaceCreatedLabel(workspace) {
+ if (!workspace.created_at) return "Unknown";
+ var date = new Date(workspace.created_at);
+ if (Number.isNaN(date.getTime())) return "Unknown";
+ return date.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
+ }
+ function workspaceGuidance(workspace) {
+ if (workspace.state === "active" && workspace.health_status === "healthy") {
+ return "This workspace looks ready for normal operator work. Use the fleet table to open it, or suspend it here if you are intentionally taking it out of service.";
+ }
+ if (workspace.state === "active" && workspace.health_status === "checking") {
+ return "This workspace is active but still waiting on a completed health check. Review it before you treat the hosted posture as settled.";
+ }
+ if (workspace.health_status === "unhealthy") {
+ return "This workspace needs review before it is treated as trustworthy. Use the management action only when you intend to suspend or remove it from the hosted fleet.";
+ }
+ if (workspace.state === "suspended") {
+ return "This workspace is already suspended. The remaining lifecycle action here is deletion, so treat it as a deliberate irreversible step.";
+ }
+ return "Review the lifecycle state before taking the next explicit action for this workspace.";
+ }
function workspaceMeta(workspace) {
var parts = [workspace.state];
if (workspace.health_status) parts.push(workspace.health_status);
@@ -79,9 +105,13 @@
var title = getElement("workspace-management-title-" + account.id);
var meta = getElement("workspace-management-meta-" + account.id);
var summary = getElement("workspace-management-summary-" + account.id);
+ var health = getElement("workspace-management-health-" + account.id);
+ var lifecycle = getElement("workspace-management-lifecycle-" + account.id);
+ var created = getElement("workspace-management-created-" + account.id);
+ var guidance = getElement("workspace-management-guidance-" + account.id);
var actionButton = getElement("workspace-management-action-" + account.id);
var closeButton = getElement("workspace-management-close-" + account.id);
- if (!empty || !content || !title || !meta || !summary || !actionButton || !closeButton) return;
+ if (!empty || !content || !title || !meta || !summary || !health || !lifecycle || !created || !guidance || !actionButton || !closeButton) return;
var workspace = entry.selectedWorkspaceID ? findWorkspace(account, entry.selectedWorkspaceID) : null;
var hasSelection = !!workspace;
panel.classList.add("visible");
@@ -97,6 +127,10 @@
title.textContent = workspace.display_name;
meta.textContent = workspaceMeta(workspace);
summary.textContent = workspaceSummary(workspace);
+ health.textContent = workspaceHealthLabel(workspace);
+ lifecycle.textContent = workspace.state ? workspace.state.charAt(0).toUpperCase() + workspace.state.slice(1) : "Unknown";
+ created.textContent = workspaceCreatedLabel(workspace);
+ guidance.textContent = workspaceGuidance(workspace);
actionButton.textContent = workspaceActionLabel(workspace);
actionButton.disabled = entry.manageWorkspace.pending;
actionButton.setAttribute("data-workspace-id", workspace.id);
@@ -1816,7 +1850,7 @@
workspaceDeskActions += '';
}
workspaceDeskActions += '';
- workspaceManagement = ' Select a workspace from the fleet to review its lifecycle state and run explicit management actions. Select a workspace from the fleet to review its lifecycle state and run explicit management actions. Use the management desk for workspace-level decisions, and keep billing or team changes one click away.Review one workspace at a time
Review one workspace at a time
Keep account-wide actions close
Use the management desk for workspace-level decisions, and keep billing or team changes one click away.
' + + '