Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a64f314
feat(membership): redesign membership page and add Supporting Member …
mohanadft Aug 6, 2026
bca9faf
fix(membership): left-align hero photo on -new pages
mohanadft Aug 6, 2026
1c26a4a
Merge remote-tracking branch 'origin/main' into feat/membership-v2-su…
mohanadft Aug 7, 2026
11b2056
fix(membership): balance desktop layout, alternate testimonials, trim…
mohanadft Aug 7, 2026
4054885
fix(membership): smooth-scroll anchor links, contain testimonials to …
mohanadft Aug 7, 2026
e987647
style(membership): visual redesign of /membership and /supporting-member
mohanadft Aug 7, 2026
3f0bc1e
Revert "style(membership): visual redesign of /membership and /suppor…
mohanadft Aug 7, 2026
4a09a76
style(membership): polish spacing, hover/focus states, and card treat…
mohanadft Aug 7, 2026
50c3358
feat(membership): track join CTA clicks in Plausible
mohanadft Aug 10, 2026
48e4124
fix(membership): scope join-click tracking to Supporting Member only
mohanadft Aug 10, 2026
8028d9f
docs: add design spec for membership About You step
mohanadft Aug 12, 2026
a53b55a
feat(membership): support Qgiv URL pre-fill in qgivEmbedUrl
mohanadft Aug 12, 2026
45fa6fa
feat(membership): add AboutYouStep component
mohanadft Aug 12, 2026
a4402c2
feat(membership): thread Qgiv pre-fill prop through QgivJoin
mohanadft Aug 12, 2026
96dc45b
feat(membership): gate payment step behind About You form
mohanadft Aug 12, 2026
811ce49
fix(membership): restore About You field values when going back from …
mohanadft Aug 12, 2026
a24971f
fix(membership): scope About You step to the payment embed cell only
mohanadft Aug 12, 2026
770ab91
fix(membership): keep QgivJoin mounted across step toggles, add loadi…
mohanadft Aug 12, 2026
fdb776a
fix(membership): guarantee the loading overlay is actually perceptible
mohanadft Aug 12, 2026
35b5d6d
fix(membership): wait for the Qgiv iframe's load event, not just its …
mohanadft Aug 12, 2026
6af1e87
fix(membership): animate the join panel height instead of snapping
mohanadft Aug 12, 2026
57a7358
fix(membership): use a shared fixed height for the join panel
mohanadft Aug 12, 2026
8d75375
fix(membership): center the About You form to match Qgiv's centered l…
mohanadft Aug 12, 2026
afb0763
fix(membership): center Step 1 note/button, fix loading overlay heigh…
mohanadft Aug 12, 2026
2ddfd61
fix(membership): use an explicit fixed height for the loading box
mohanadft Aug 12, 2026
52cb770
fix(membership): reload the Qgiv iframe when prefill data changes
mohanadft Aug 12, 2026
31e67fb
fix(membership): stop treating QgivJoin.tsx as a binary file
mohanadft Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default defineConfig({
"/js/web.js/",
"/admin/conversions/",
"/membership-new/",
"/supporting-member-new/",
"/volunteer-new/",
"/e4p-new/",
"/help/hire-new/",
Expand Down
32 changes: 16 additions & 16 deletions docs/API.md

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions docs/superpowers/specs/2026-08-12-membership-about-you-step-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Two-step "About You" gate for /membership and /supporting-member

## Problem

`/membership` and `/supporting-member` currently drop visitors straight into an
embedded Qgiv payment form (`LegacyJoinSection` → `QgivJoin`) with no
site-owned entry step. The team wants a lightweight "Step 1/2 – About You"
screen (name + email) in front of the existing payment step, matching this
wireframe:

```
Step 1/2 - About You

Name: [____________]
Email: [__________]

You'll be redirected to our secure payment partner to complete your membership.

[Next button]
```

## Non-goals

- No change to when EmailOctopus tagging happens. `/api/membership-complete`
keeps firing only after Qgiv's `donationComplete` event, exactly as today.
Step 1's name/email is never sent to our backend.
- No change to the Qgiv iframe embed model — payment stays embedded on-page,
it is not a full navigation to a hosted Qgiv URL.
- No change to the MembershipCalculator A/B test, waiver panel, or Hub-invite
logic.
- No persistence of Step 1 data across a page reload (sessionStorage/
localStorage). Low stakes if a visitor has to re-enter name/email after a
refresh; not worth the extra state management.
- No analytics event for this flow (explicitly out of scope for this pass).

## Design

### Where it lives

Both `/membership` (via `MembershipPage.tsx`) and `/supporting-member.astro`
already render `LegacyJoinSection` directly. The step gate is added *inside*
`LegacyJoinSection.tsx` via a new piece of state:

```ts
const [step, setStep] = useState<"about-you" | "payment">("about-you");
const [aboutYou, setAboutYou] = useState<{ name: string; email: string } | null>(null);
```

Both call sites get the new flow automatically — no changes needed at either
page or at `MembershipPage.tsx`.

### New component: `src/components/membership/AboutYouStep.tsx`

Props:

```ts
interface AboutYouStepProps {
onContinue: (data: { name: string; email: string }) => void;
}
```

Behavior:

- Two MUI `TextField`s: **Name** (single input, matching the wireframe) and
**Email**. Uses local `useState`, not react-hook-form — two fields don't
justify pulling in the `hook-form` scaffolding used elsewhere in the repo.
- Copy, top to bottom: "Step 1/2 – About You" heading, the two fields, the
line "You'll be redirected to our secure payment partner to complete your
membership.", then a **Next** button.
- Validation runs on submit (not per-keystroke):
- Name: required, non-empty after trim.
- Email: required, matches `/^[^\s@]+@[^\s@]+\.[^\s@]+$/` — the same regex
`/api/membership-complete.ts` already validates against, so client and
server agree on "valid email" without sharing code across the client/
server boundary.
- Invalid fields show inline error text beneath them; `onContinue` is not
called until both pass.
- Visual style follows the existing `LegacyJoinSection` conventions (MUI
`Box`/`Typography`, same color tokens: `#111827` headings, `#374151` body,
`#168039` brand green for the button/focus ring).

### Step 2 (existing payment section), lightly extended

- Renders today's calculator/waiver/`QgivJoin` layout unchanged, plus:
- A "Step 2/2" label above it, for continuity with Step 1.
- A "← Edit your info" link/button that sets `step` back to `"about-you"`.
`aboutYou` state is preserved (not cleared), so the fields are still
filled in when the visitor goes back.

### Qgiv pre-fill

Qgiv supports pre-filling hosted/embedded form fields via a documented URL
suffix: `/v/first_name=...,last_name=...,email=...` (confirmed against
Qgiv's public API docs). We use this so a visitor doesn't retype what they
just gave us in Step 1.

- `qgiv.ts`: `qgivEmbedUrl(form, prefill?)` gains an optional third-ish
argument (or an options object) and appends the `/v/...` suffix, URL-
component-encoded, when `prefill` is provided.
- `QgivJoin.tsx`: gains an optional prop
`prefill?: { firstName: string; lastName: string; email: string }`, passed
through to `qgivEmbedUrl`.
- `LegacyJoinSection.tsx`: once `step === "payment"`, splits `aboutYou.name`
on the first space into `firstName`/`lastName` (best-effort — a single-word
name yields an empty `lastName`) and passes that plus `aboutYou.email` as
`QgivJoin`'s `prefill` prop.
- This only affects Qgiv pre-fill convenience. It has no effect on
validation, EmailOctopus data, or the Hub invite — those still come
entirely from whatever Qgiv reports back on `donationComplete`.
- Implementation note: Qgiv's `/v/` prefill syntax is documented for their
hosted form URLs; it hasn't been verified yet against the specific
`/embed/<id>/` iframe src path this repo uses. Verify against a live Qgiv
embed during implementation before relying on it, and fail open (render
the embed without pre-fill) if the suffix doesn't take effect.

## Files touched

- `src/components/membership/AboutYouStep.tsx` (new)
- `src/components/membership/LegacyJoinSection.tsx` (step state, wiring)
- `src/components/membership/QgivJoin.tsx` (optional `prefill` prop)
- `src/components/membership/qgiv.ts` (`qgivEmbedUrl` prefill support)

No API routes, EmailOctopus config, or page-level files (`membership.astro`,
`supporting-member.astro`, `MembershipPage.tsx`) need to change.
9 changes: 8 additions & 1 deletion public/_redirects
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,11 @@

# /community-call reverted to the old design; new-design page removed.
/community-call-old /community-call 301
/community-call-old/ /community-call 301
/community-call-old/ /community-call 301

# -new redesign shelved (see above) also covers the membership pages built
# after that decision landed; /supporting-member-new never launched.
/supporting-member-new /supporting-member 301
/supporting-member-new/ /supporting-member 301
/supporting-members /supporting-member 301
/supporting-members/ /supporting-member 301
Binary file added public/images/membership/members-group.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/membership/supporting-panel.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions src/components/MembershipCalculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const rowSx = {
display: "flex",
alignItems: "center",
gap: 1.5,
mb: 1,
mb: 1.5,
};

const labelSx = {
Expand Down Expand Up @@ -67,10 +67,11 @@ export default function MembershipCalculator() {
sx={{
maxWidth: 600,
mx: "auto",
borderRadius: 2,
borderRadius: 3,
border: "2px solid #168039",
backgroundColor: "white",
p: 1.5,
boxShadow: "0 4px 14px -6px rgba(17, 107, 47, 0.2)",
p: 2,
mb: 4,
}}
>
Expand Down Expand Up @@ -149,10 +150,10 @@ export default function MembershipCalculator() {
sx={{
display: "flex",
gap: 1,
mt: 0.5,
p: 1.5,
mt: 1,
p: 2,
backgroundColor: "#f0fdf4",
borderRadius: 1.5,
borderRadius: 2,
border: "2px solid #168039",
}}
>
Expand Down
Loading
Loading