feat(cloudcp): refresh Pulse Account workspace lifecycle from bootstrap

This commit is contained in:
rcourtman
2026-03-26 00:11:05 +00:00
parent 22e3199b81
commit 54095eaabe
2 changed files with 27 additions and 14 deletions
+23 -14
View File
@@ -180,10 +180,12 @@ async function refreshBootstrap() {
var response = await fetch(BOOTSTRAP_PATH, {
headers: { 'Accept': 'application/json' }
});
if (!response.ok) return;
if (!response.ok) return false;
var data = await response.json();
applyBootstrap(data);
return true;
} catch (_) {}
return false;
}
function showToast(msg, isError) {
@@ -569,8 +571,11 @@ async function createWorkspace(accountID) {
showToast((err && err.error) || 'Failed to create workspace', true);
return;
}
if (!await refreshBootstrap()) {
window.location.href = PORTAL_PATH;
return;
}
showToast('Workspace created!');
setTimeout(function() { window.location.reload(); }, 800);
} catch(e) {
showToast('Network error. Please try again.', true);
} finally {
@@ -578,26 +583,30 @@ async function createWorkspace(accountID) {
}
}
function suspendOrDelete(evt, accountID, tenantID, state, name) {
async function suspendOrDelete(evt, accountID, tenantID, state, name) {
evt.stopPropagation();
var action = state === 'active' ? 'Suspend' : 'Delete';
if (!confirm(action + ' workspace "' + name + '"?')) return;
var method = state === 'active' ? 'PATCH' : 'DELETE';
var body = state === 'active' ? JSON.stringify({ state: 'suspended' }) : undefined;
fetch(ACCOUNT_API_BASE_PATH + '/' + accountID + '/tenants/' + tenantID, {
method: method,
headers: body ? { 'Content-Type': 'application/json' } : {},
body: body
}).then(function(r) {
if (r.ok) {
showToast(action + 'd workspace.');
setTimeout(function() { window.location.reload(); }, 800);
} else {
try {
var response = await fetch(ACCOUNT_API_BASE_PATH + '/' + accountID + '/tenants/' + tenantID, {
method: method,
headers: body ? { 'Content-Type': 'application/json' } : {},
body: body
});
if (!response.ok) {
showToast('Failed to ' + action.toLowerCase() + ' workspace.', true);
return;
}
}).catch(function() {
if (!await refreshBootstrap()) {
window.location.href = PORTAL_PATH;
return;
}
showToast(action + 'd workspace.');
} catch(_) {
showToast('Network error.', true);
});
}
}
async function openBilling(accountID) {
+4
View File
@@ -809,6 +809,7 @@ func TestPortalPageTemplate_AccountServicesRendered(t *testing.T) {
`fetch(LICENSE_API_BASE + '/v1/gdpr/export'`,
`fetch(LICENSE_API_BASE + '/v1/gdpr/request-delete'`,
`fetch(LICENSE_API_BASE + '/v1/gdpr/confirm-delete'`,
`if (!await refreshBootstrap())`,
`href="https://pulserelay.pro/refund.html?email=owner%40example.com"`,
"commercial account actions now live here",
}
@@ -820,6 +821,9 @@ func TestPortalPageTemplate_AccountServicesRendered(t *testing.T) {
if strings.Contains(html, `const LICENSE_API_BASE = 'https://license.pulserelay.pro';`) {
t.Errorf("expected commercial API base URL to be renderer-owned, not hardcoded in the asset")
}
if strings.Contains(html, `window.location.reload()`) {
t.Errorf("expected workspace lifecycle to refresh the bootstrap contract instead of forcing a page reload")
}
if strings.Contains(html, `await fetch('/auth/logout'`) {
t.Errorf("expected portal paths to be renderer-owned, not hardcoded in the asset")
}