Skip to content

feat(community): restore public signup form on /community - #26

Merged
sre-helmcode merged 4 commits into
mainfrom
feat/restore-community-signup-form
Aug 19, 2026
Merged

feat(community): restore public signup form on /community#26
sre-helmcode merged 4 commits into
mainfrom
feat/restore-community-signup-form

Conversation

@sre-helmcode

Copy link
Copy Markdown
Contributor

What

Restores the public community-tier signup form on /community and /es/community, which was removed in 8d2ee6d (2026-07-30).

Why

The community tier ($14.99/mo) is the only paid tier without an admin-invite path — the admin UI only invites to inference (€70/$75). The sole way to become a community member is the public Stripe Checkout for the community price. When the CommunitySignupForm island was removed, the /api/community-signup endpoint and lib/communitySignup.ts were kept alive but left with no UI caller, so there was no way for new members to sign up to community anymore.

This is how the 22 existing community members originally joined (before 2026-07-30). After the removal, the only workaround was generating a Stripe Checkout link by hand via the API — which is what surfaced the gap.

Changes

  • Restored src/components/landing/CommunitySignupForm.tsx (Preact island, client:load) — calls POST /api/community-signup which forwards to cloud-api POST /api/community/signup.
  • Restored the cclose__form section + .cclose__form CSS rule in src/pages/_community.astro (covers both /community and /es/community via the _-prefixed body).
  • Restored the 17 community.formStrings i18n keys in i18n/en.json and i18n/es.json (email, region, submit, errors, etc.).
  • Restored the import '../styles/global.css' the island needs (Tailwind utilities mapped to project tokens).
  • Reverted the waitlistClient.ts comment — the third copy of isValidEmail/REGIONS is back (known tech debt, acceptable).

Verification

  • npm test670 passed (45 files)
  • astro check0 errors / 0 warnings / 0 hints
  • npm run build → OK (Cloudflare Workers server build)
  • /community chunk references CommunitySignupForm + community-signup endpoint confirmed in dist/

Deploy notes

No VERSION file on this repo — deploy is GitHub Actions on push to main (deploy.yml: npm cinpm testnpm run buildnpx wrangler deploy). Merging this PR to main triggers the prod deploy to Cloudflare.

After deploy, the public community signup works again at https://nan.builders/community and https://nan.builders/es/community.

Reverts the form removal from 8d2ee6d. The community-tier Stripe checkout
is the only path to the community tier (14.99/mo) - the admin UI only
invites to inference - so removing the public form left no way for new
members to join community.

Restores the CommunitySignupForm Preact island, the cclose__form section
and CSS on _community.astro, the 17 community.formStrings i18n keys
(en + es), the styles/global.css import, and the waitlistClient.ts
comment about the third copy of isValidEmail/REGIONS being back.

Both /community and /es/community render the form again. This is how the
22 existing community members originally signed up (before 2026-07-30).

670 tests pass - astro check 0/0/0 - npm run build OK.
…rtal

The nan_community pricing card linked to https://cloud.nan.builders/
(the member portal login), which has no signup form. Now that the public
community signup form is restored on /community, the CTA must send
visitors there so they can actually sign up.

Region-aware: /community (en) or /es/community (es).
QA blockers:
- Add CommunitySignupForm logic tests (resolveSignupResponse +
  errorMessageFor) in src/tests/lib/communitySignup.test.ts (18 cases)
- Extract response classification into lib/communitySignup.ts as pure
  functions so the component's error mapping is testable in node
- Extend i18nParity.test.ts to cover root-level community + hackaton
  subtrees, not just nan.* (catches en-only keys rendering as raw paths)
- Assert the community pricing card CTA href in pricing.test.ts

UX blockers:
- Button now uses the .btn .btn-primary system classes instead of raw
  Tailwind utilities (the un-layered button reset in tokens.css was
  cancelling bg-violet-600, leaving the submit button unstyled)
- Break the /community <-> #pricing loop: hero CTA scrolls to #signup
  (the form) on the same page, pricing card routes to /community#signup
- Remove the client-side honeypot short-circuit: it hung real users
  (password manager / autofill) on a spinner with no recovery, and
  did not stop bots that POST directly. The server is authoritative
- already_subscribed no longer destroys the form: it renders an inline
  banner above the still-mounted form so the user can retry with another
  email
- Add alreadyMemberLabel i18n key (was a hardcoded English literal)

688 tests pass - astro check 0/0/0 - npm run build OK.
QA re-review (3 blockers):
- Fix tautological CTA href regex in pricing.test.ts: assert the full
  ternary so the EN branch regression is caught, not masked by the ES
  substring also containing '/community#signup'
- Add communityPage.test.ts: assert id="signup" exists on .cclose,
  CommunitySignupForm mounted with client:load, and every t('community.*')
  call resolves to a real string (not the raw key path) in both locales
  — closes the gap that let '// already a member' slip through as a
  hardcoded literal

UX re-review (2 blockers):
- Show the price on /community: render community.priceLabel (14,99€),
  priceTaxNote and footer next to the form. The price was only visible
  on the home pricing card; the /community form sent users to Stripe
  Checkout without ever showing the amount. The strings already existed
  but were orphaned.
- Remove the client-side honeypot field entirely: removing only the
  short-circuit left a honeypot-filled response redirecting real users
  (password manager / autofill) to the home with a fake-success query
  param. The field is gone from the island; the server rate-limiter and
  server-side honeypot (when _hp is sent) are the real bot defence.

691 tests pass - astro check 0/0/0 - npm run build OK.
@sre-helmcode
sre-helmcode merged commit 7afdd83 into main Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants