diff --git a/apps/web/src/lib/billing-ui.ts b/apps/web/src/lib/billing-ui.ts index d6db64dd..16414638 100644 --- a/apps/web/src/lib/billing-ui.ts +++ b/apps/web/src/lib/billing-ui.ts @@ -200,6 +200,11 @@ export function renderPlanCardsGridHtml( planSource: billing.planSource, }); + const cardClass = + "plan-card flex flex-col gap-2 rounded-[10px] border border-border p-4 data-[current=true]:border-foreground"; + const ctaClass = + "plan-card__cta mt-auto self-start rounded-md border border-border bg-card px-3 py-1.5 text-xs tracking-wide text-primary hover:border-primary focus-visible:border-primary focus-visible:outline-none disabled:cursor-default disabled:opacity-60"; + return cardPlans .map((plan) => { const isCurrent = plan.id === currentPlanId; @@ -210,16 +215,16 @@ export function renderPlanCardsGridHtml( if (!isCurrent && plan.id === "pro") { footer = cta.kind === "unavailable" - ? `` - : ``; + ? `` + : ``; } return ` -
- ${isCurrent ? `Current plan` : ""} -

${escapeHtml(plan.name)}

- ${priceLine ? `

${escapeHtml(priceLine)}

` : ""} -

${escapeHtml(plan.blurb)}

- +
+ ${isCurrent ? `Current plan` : ""} +

${escapeHtml(plan.name)}

+ ${priceLine ? `

${escapeHtml(priceLine)}

` : ""} +

${escapeHtml(plan.blurb)}

+ ${footer}
`; }) diff --git a/apps/web/src/lib/workspace-ui.test.ts b/apps/web/src/lib/workspace-ui.test.ts index 8a47a158..143366cc 100644 --- a/apps/web/src/lib/workspace-ui.test.ts +++ b/apps/web/src/lib/workspace-ui.test.ts @@ -51,7 +51,7 @@ describe("renderMembersHtml", () => { it("leads with the email when there is no display name, without a sub-line", () => { const html = renderMembersHtml([{ email: "c@d.com", name: "", role: "member" }]); - expect(html).toContain('member-row__name">c@d.com<'); + expect(html).toMatch(/member-row__name[^"]*">c@d\.com { it("mirrors the real member row's two-column structure", () => { const html = renderMembersPlaceholderHtml(2); - expect(html.match(/class="member-row"/g)).toHaveLength(2); + expect(html.match(/class="member-row /g)).toHaveLength(2); // Both halves present, or the flex row collapses and the swap rearranges. expect(html).toContain("member-row__who"); expect(html).toContain("member-row__name"); diff --git a/apps/web/src/lib/workspace-ui.ts b/apps/web/src/lib/workspace-ui.ts index 42e83a60..237cb845 100644 --- a/apps/web/src/lib/workspace-ui.ts +++ b/apps/web/src/lib/workspace-ui.ts @@ -158,21 +158,32 @@ export function canManageMemberRow(member: MemberRow, opts: MemberRowOptions): b * People-tab member list. Name leads when set (email sub-line); manageable * rows get role `` + + ? `` + + `` + `` + `` - : `${escapeHtml(m.role)}`; - return `
${escapeHtml(lead)}${sub}${controls}
`; + : `${escapeHtml(m.role)}`; + return `
${escapeHtml(lead)}${sub}${controls}
`; }) .join(""); } @@ -191,9 +202,9 @@ export function renderMembersPlaceholderHtml(rows = 2): string { return Array.from( { length: rows }, (_, i) => - `
` + - `${skeletonBarHtml(widths[i % widths.length])}` + - `${skeletonBarHtml("42px")}` + + `
` + + `${skeletonBarHtml(widths[i % widths.length])}` + + `${skeletonBarHtml("42px")}` + `
`, ).join(""); } @@ -209,10 +220,10 @@ export function renderInvitesHtml( .map((inv) => { const status = inv.status || "pending"; return ( - `
` + - `${escapeHtml(inv.email)}` + - `` + - `${escapeHtml(status)}` + + `
` + + `${escapeHtml(inv.email)}` + + `` + + `${escapeHtml(status)}` + `` + `
` ); diff --git a/apps/web/src/pages/account/workspaces/[name]/billing.astro b/apps/web/src/pages/account/workspaces/[name]/billing.astro index cd7f4405..4cd8dc2a 100644 --- a/apps/web/src/pages/account/workspaces/[name]/billing.astro +++ b/apps/web/src/pages/account/workspaces/[name]/billing.astro @@ -96,7 +96,7 @@ const cta: BillingCtaState = billing

Plans

@@ -113,6 +113,7 @@ const cta: BillingCtaState = billing