fix(web): footer rebrand + disable unbuilt paid-tier cta
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Two launch-day cleanups:

**Footer rebrand** — full rewrite of modules/marketing/layout/footer.tsx
from TurboStarter boilerplate (Twitter/Facebook/LinkedIn socials,
Chrome/Firefox/Edge extension links, turbostarter repo links, broken
/legal routes) to lean claudemesh structure:

- claudemesh wordmark (mesh glyph + serif) + tagline
- 2 columns: Product (Docs / Pricing / Changelog / Contact) +
  Protocol (GitHub / claude-intercom OSS / Protocol spec / Self-host
  broker)
- GitHub social icon linking to github.com/alezmad/claudemesh
- I18n controls
- Bottom bar: "© 2026 claudemesh · MIT licensed" + the existing
  BuiltWith credit pointing at claude-intercom (from cdd7931)

No trash links. No turbostarter refs. Matches landing design tokens
(--cm-*).

**Manage-plan CTA guard** — settings/billing → ManagePlan previously
always rendered an active "Visit billing portal" button that would
500 on launch day because Stripe isn't set up. For FREE-tier users
(everyone at v0.1.0) the button is now disabled + labelled
"Paid tiers coming soon". When someone is on a paid tier (future)
the real portal flow re-engages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-05 16:03:11 +01:00
parent 0a40f5b463
commit a795900e5f
2 changed files with 140 additions and 173 deletions

View File

@@ -48,24 +48,39 @@ export const ManagePlan = () => {
</SettingsCardHeader>
<SettingsCardContent>
<Button
className="w-fit gap-1"
disabled={getPortal.isPending}
onClick={() =>
getPortal.mutate({
query: {
redirectUrl: window.location.href,
},
})
}
>
{t("manage.billing.visitPortal")}
{getPortal.isPending ? (
<Icons.Loader2 className="size-4 animate-spin" />
) : (
<Icons.ArrowUpRight className="size-4" />
)}
</Button>
{plan.id === PricingPlanType.FREE ? (
// v0.1.0: only the free tier is live. Paid-tier checkout +
// Stripe customer portal land post-launch; surface that
// honestly instead of a button that would hit a 500.
<div className="flex items-center gap-2">
<Button className="w-fit gap-1" disabled>
{t("manage.billing.visitPortal")}
<Icons.ArrowUpRight className="size-4" />
</Button>
<span className="text-muted-foreground text-xs">
Paid tiers coming soon
</span>
</div>
) : (
<Button
className="w-fit gap-1"
disabled={getPortal.isPending}
onClick={() =>
getPortal.mutate({
query: {
redirectUrl: window.location.href,
},
})
}
>
{t("manage.billing.visitPortal")}
{getPortal.isPending ? (
<Icons.Loader2 className="size-4 animate-spin" />
) : (
<Icons.ArrowUpRight className="size-4" />
)}
</Button>
)}
</SettingsCardContent>
</SettingsCard>
);