mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 02:28:59 +00:00
Lead self-hosted billing with real jobs
This commit is contained in:
parent
35c056bc8f
commit
48554917b9
8 changed files with 25 additions and 23 deletions
|
|
@ -188,7 +188,9 @@ Core rules:
|
|||
entry points, not an always-open billing dashboard. Self-hosted billing,
|
||||
license, refund, and privacy panels should appear only when that exact
|
||||
billing job is active, and opening one on phone widths must reveal the
|
||||
active panel in-frame.
|
||||
active panel in-frame. When no hosted account exists, `Billing` must lead
|
||||
directly with the self-hosted job picker instead of front-loading an
|
||||
empty hosted-billing block.
|
||||
27. `Support` must stay honest to account shape. Self-hosted-only accounts
|
||||
must reduce `Support` to the billing escalation path only; hosted
|
||||
workspace or access failure routes and task buttons must not render
|
||||
|
|
|
|||
|
|
@ -3408,21 +3408,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"work_claims": [
|
||||
{
|
||||
"id": "codex-portal-lane-l3",
|
||||
"agent_id": "codex-portal",
|
||||
"summary": "Continue Playwright-driven portal redesign from built review",
|
||||
"target_id": "v6-rc-stabilization",
|
||||
"claimed_at": "2026-03-28T19:12:30Z",
|
||||
"heartbeat_at": "2026-03-28T19:12:30Z",
|
||||
"expires_at": "2026-03-28T21:12:30Z",
|
||||
"work_item": {
|
||||
"kind": "lane",
|
||||
"id": "L3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"work_claims": [],
|
||||
"open_decisions": [],
|
||||
"source_of_truth_file": "docs/release-control/v6/internal/SOURCE_OF_TRUTH.md",
|
||||
"resolved_decisions": [
|
||||
|
|
|
|||
|
|
@ -291,7 +291,9 @@ panel on phone-width layouts instead of leaving it offscreen. The same
|
|||
bootstrap/runtime contract must also carry explicit truth for whether
|
||||
self-hosted commercial history is relevant to the signed-in account, so
|
||||
hosted-only accounts do not render self-hosted license, refund, privacy, or
|
||||
self-hosted escalation paths by default. `Support` follows the same
|
||||
self-hosted escalation paths by default, and self-hosted-only accounts do not
|
||||
front-load an empty hosted-billing block before the real self-hosted jobs.
|
||||
`Support` follows the same
|
||||
account-shape runtime contract: self-hosted-only accounts expose only the
|
||||
billing escalation path and billing-specific handoff packet, and hosted
|
||||
workspace/access escalation controls must not render when no hosted account
|
||||
|
|
|
|||
|
|
@ -846,7 +846,9 @@ controls appear only when that exact access job is active. `Billing` follows
|
|||
the same task-first rule: hosted billing leads when present, the self-hosted
|
||||
billing, license, refund, and privacy paths reduce to explicit job pickers,
|
||||
and the active self-hosted billing panel must stay hidden until that exact job
|
||||
is opened and then be revealed in-frame on narrow layouts. `Support` follows
|
||||
is opened and then be revealed in-frame on narrow layouts. When no hosted
|
||||
account exists, the billing shell must skip any empty hosted-billing block and
|
||||
lead directly with the self-hosted job picker. `Support` follows
|
||||
the same account-shape rule: self-hosted-only accounts reduce to the billing
|
||||
escalation path and billing-specific handoff packet only, and hosted
|
||||
workspace/access escalation routes must not render without hosted accounts.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"source_hash": "30ae2fd08e074502921f7f7d0f942df540d1192e9e5234e6ea09387cc8593dbb",
|
||||
"source_hash": "f417480ba4d79a766c8bb7511a50e02f825cb2199eb6961c618b5a9429d63975",
|
||||
"build_inputs": [
|
||||
"package.json",
|
||||
"tsconfig.json",
|
||||
|
|
|
|||
3
internal/cloudcp/portal/dist/portal_app.js
vendored
3
internal/cloudcp/portal/dist/portal_app.js
vendored
|
|
@ -2287,13 +2287,14 @@
|
|||
return account.has_billing;
|
||||
}).length;
|
||||
var billingNote = hosted ? showSelfHostedCommercial ? "Use hosted billing first when the request belongs to a hosted workspace account. Self-hosted licenses, refunds, and privacy stay separate underneath it." : "Use this billing surface only for hosted billing on your hosted workspace accounts." : "Use this billing surface only for self-hosted subscriptions, licenses, refunds, and privacy requests.";
|
||||
var selfHostedBillingEscalationCopy = hosted ? "Escalate with the same hosted billing action or self-hosted path and the exact failed step." : "Escalate with the same self-hosted billing path and the exact failed step.";
|
||||
var hostedContent = accounts.length ? accounts.map(function(account) {
|
||||
return '<section class="account-surface"><div class="account-surface-body">' + renderAccountWorkspaceSection(account, context.accountAPIBasePath) + renderAccountAccessSection(account) + "</div></section>";
|
||||
}).join("") : renderNoHostedWorkspacesSection() + renderNoHostedAccessSection();
|
||||
return '<div class="portal-shell" data-shell-section="' + activeSection + '"><div class="portal-shell-layout">' + renderShellNavigation(accounts, context.bootstrap.support_email || "", activeSection) + '<div class="portal-shell-main">' + (accounts.length === 1 ? renderAccountContextStrip(accounts[0]) : "") + '<section class="portal-content-panel portal-content-panel-overview">' + renderShellOverviewSection(context) + '<div id="accounts-root">' + hostedContent + '</div></section><section class="portal-content-panel portal-content-panel-billing billing-section" id="billing-section"><div class="billing-header"><div><div class="account-panel-kicker">Billing</div><h2>Billing</h2>' + renderSectionContextChips([
|
||||
hostedBillingCount > 0 ? "Hosted billing" : "No hosted billing",
|
||||
showSelfHostedCommercial ? "Self-hosted tools" : "Hosted only"
|
||||
]) + '</div><div class="billing-note">' + billingNote + '</div></div><div class="billing-overview-grid">' + renderHostedBillingCards(accounts, showSelfHostedCommercial) + "</div>" + (showSelfHostedCommercial ? '<div class="billing-shell billing-shell-idle"><div class="billing-shell-main"><div class="billing-shell-main-head"><div class="account-panel-kicker">Self-hosted billing</div><h3>Pick the self-hosted job</h3><p>Use self-hosted billing only for self-hosted purchases. Open one path at a time when hosted billing does not apply.</p></div><div class="billing-action-list">' + renderBillingActionRow("open-manage-billing", "Self-hosted billing", "Manage subscriptions", "Billing", "Open Stripe for self-hosted plan, invoice, and payment changes.", "manage-billing-panel", "manage-inline-email", ["Plan changes", "Invoices"]) + renderBillingActionRow("open-retrieve-billing", "Licenses", "Retrieve licenses", "Licenses", "Recover the latest active self-hosted license and invoice link.", "retrieve-billing-panel", "retrieve-inline-email", ["Latest active license", "Invoice lookup"]) + renderBillingActionRow("open-refund-billing", "Refunds", "Refund requests", "Refunds", "Request a self-serve refund when the purchase is still eligible.", "refund-billing-panel", "refund-inline-email", ["Eligibility check", "Revocation"]) + renderBillingActionRow("open-data-billing", "Privacy", "Data and privacy", "Privacy", "Request export or deletion for commercial account data.", "data-billing-panel", "data-export-email", ["Export", "Deletion"]) + '</div><div class="billing-inline-support"><div class="account-panel-kicker">Escalation only</div><h4>Use Support only after Billing fails</h4><p>Escalate with the same hosted billing action or self-hosted path and the exact failed step.</p><div class="billing-inline-support-actions"><button type="button" class="btn-secondary btn-compact" data-shell-action="activate-section" data-shell-section="support">Open support</button><a class="portal-support-link" href="mailto:' + escapeAttr(context.bootstrap.support_email || "") + '">' + escapeHTML(context.bootstrap.support_email || "") + '</a></div></div></div><div class="billing-shell-detail" id="billing-detail-shell" hidden>' + renderBillingTaskPanel(
|
||||
]) + '</div><div class="billing-note">' + billingNote + "</div></div>" + (hosted ? '<div class="billing-overview-grid">' + renderHostedBillingCards(accounts, showSelfHostedCommercial) + "</div>" : "") + (showSelfHostedCommercial ? '<div class="billing-shell billing-shell-idle"><div class="billing-shell-main"><div class="billing-shell-main-head"><div class="account-panel-kicker">Self-hosted billing</div><h3>Pick the self-hosted job</h3><p>Use self-hosted billing only for self-hosted purchases. Open one path at a time when hosted billing does not apply.</p></div><div class="billing-action-list">' + renderBillingActionRow("open-manage-billing", "Self-hosted billing", "Manage subscriptions", "Billing", "Open Stripe for self-hosted plan, invoice, and payment changes.", "manage-billing-panel", "manage-inline-email", ["Plan changes", "Invoices"]) + renderBillingActionRow("open-retrieve-billing", "Licenses", "Retrieve licenses", "Licenses", "Recover the latest active self-hosted license and invoice link.", "retrieve-billing-panel", "retrieve-inline-email", ["Latest active license", "Invoice lookup"]) + renderBillingActionRow("open-refund-billing", "Refunds", "Refund requests", "Refunds", "Request a self-serve refund when the purchase is still eligible.", "refund-billing-panel", "refund-inline-email", ["Eligibility check", "Revocation"]) + renderBillingActionRow("open-data-billing", "Privacy", "Data and privacy", "Privacy", "Request export or deletion for commercial account data.", "data-billing-panel", "data-export-email", ["Export", "Deletion"]) + '</div><div class="billing-inline-support"><div class="account-panel-kicker">Escalation only</div><h4>Use Support only after Billing fails</h4><p>' + selfHostedBillingEscalationCopy + '</p><div class="billing-inline-support-actions"><button type="button" class="btn-secondary btn-compact" data-shell-action="activate-section" data-shell-section="support">Open support</button><a class="portal-support-link" href="mailto:' + escapeAttr(context.bootstrap.support_email || "") + '">' + escapeHTML(context.bootstrap.support_email || "") + '</a></div></div></div><div class="billing-shell-detail" id="billing-detail-shell" hidden>' + renderBillingTaskPanel(
|
||||
"Manage subscriptions",
|
||||
"Open Stripe for self-hosted plan, invoice, and payment changes.",
|
||||
"manage-billing-panel",
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ describe('shell view', function() {
|
|||
expect(html).toContain('id="data-billing-panel"');
|
||||
expect(html).toContain('Billing path failed');
|
||||
expect(html).toContain('licenses, refunds, or privacy');
|
||||
expect(html).toContain('Escalate with the same hosted billing action or self-hosted path and the exact failed step.');
|
||||
});
|
||||
|
||||
it('keeps top-level task navigation in the canonical section order', function() {
|
||||
|
|
@ -401,11 +402,14 @@ describe('shell view', function() {
|
|||
expect(html).toContain('There is nothing to open or manage here yet.');
|
||||
expect(html).toContain('There are no hosted roles or invites to manage for this account right now.');
|
||||
expect(html).toContain('Use this billing surface only for self-hosted subscriptions, licenses, refunds, and privacy requests.');
|
||||
expect(html).toContain('No hosted billing attached');
|
||||
expect(html).toContain('Use self-hosted billing only for self-hosted purchases. Open one path at a time when hosted billing does not apply.');
|
||||
expect(html).toContain('Escalate with the same self-hosted billing path and the exact failed step.');
|
||||
expect(html).toContain('Billing');
|
||||
expect(html).toContain('Use support only when the Billing path has already stopped you.');
|
||||
expect(html).toContain('Self-hosted billing path failed');
|
||||
expect(html).toContain('Purchase email');
|
||||
expect(html).not.toContain('No hosted billing attached');
|
||||
expect(html).not.toContain('Escalate with the same hosted billing action or self-hosted path and the exact failed step.');
|
||||
expect(html).not.toContain('Workspace or access path failed');
|
||||
expect(html).not.toContain('Open workspaces');
|
||||
expect(html).not.toContain('Open access');
|
||||
|
|
|
|||
|
|
@ -1223,6 +1223,9 @@ export function renderAuthenticatedPortalHTML(context: ShellViewContext): string
|
|||
? 'Use hosted billing first when the request belongs to a hosted workspace account. Self-hosted licenses, refunds, and privacy stay separate underneath it.'
|
||||
: 'Use this billing surface only for hosted billing on your hosted workspace accounts.')
|
||||
: 'Use this billing surface only for self-hosted subscriptions, licenses, refunds, and privacy requests.';
|
||||
var selfHostedBillingEscalationCopy = hosted
|
||||
? 'Escalate with the same hosted billing action or self-hosted path and the exact failed step.'
|
||||
: 'Escalate with the same self-hosted billing path and the exact failed step.';
|
||||
var hostedContent = accounts.length
|
||||
? accounts.map(function(account) {
|
||||
return (
|
||||
|
|
@ -1258,7 +1261,9 @@ export function renderAuthenticatedPortalHTML(context: ShellViewContext): string
|
|||
'</div>' +
|
||||
'<div class="billing-note">' + billingNote + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="billing-overview-grid">' + renderHostedBillingCards(accounts, showSelfHostedCommercial) + '</div>' +
|
||||
(hosted
|
||||
? ('<div class="billing-overview-grid">' + renderHostedBillingCards(accounts, showSelfHostedCommercial) + '</div>')
|
||||
: '') +
|
||||
(showSelfHostedCommercial
|
||||
? ('<div class="billing-shell billing-shell-idle">' +
|
||||
'<div class="billing-shell-main">' +
|
||||
|
|
@ -1276,7 +1281,7 @@ export function renderAuthenticatedPortalHTML(context: ShellViewContext): string
|
|||
'<div class="billing-inline-support">' +
|
||||
'<div class="account-panel-kicker">Escalation only</div>' +
|
||||
'<h4>Use Support only after Billing fails</h4>' +
|
||||
'<p>Escalate with the same hosted billing action or self-hosted path and the exact failed step.</p>' +
|
||||
'<p>' + selfHostedBillingEscalationCopy + '</p>' +
|
||||
'<div class="billing-inline-support-actions">' +
|
||||
'<button type="button" class="btn-secondary btn-compact" data-shell-action="activate-section" data-shell-section="support">Open support</button>' +
|
||||
'<a class="portal-support-link" href="mailto:' +
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue