feat(signup): unify cadence UX across accept-invite, join, and account

Extract shared SignupFlowOverlay component. Static "Monthly Contribution"
label on all three contribution inputs (was misleadingly dynamic).
"Per Year"/"Per Month" toggle copy; Per Year default on accept-invite,
Per Month default on join. Live billing-summary card on both signup
flows. Welcome-heading on dashboard via ?welcome=1 for new signups.
$0-member polish on account page (hide payment-history + Solidarity
Fund prompts). State-aware contribution-change hint. Invite accept now
creates Helcim customer and sets auth cookie server-side for both free
and paid branches. Pre-registrant invite + /join signup flows manually
verified against Cleo Nguyen preReg and $0-$50 variants.
This commit is contained in:
Jennie Robinson Faber 2026-04-20 12:34:59 +01:00
parent 493be2f3bc
commit a80728f0a8
10 changed files with 553 additions and 321 deletions

View file

@ -0,0 +1,48 @@
require('dotenv').config()
const mongoose = require('mongoose')
const jwt = require('jsonwebtoken')
const { randomUUID } = require('crypto')
const BASE_URL = process.argv[2]
const EMAIL = process.argv[3] || 'jennie+cleonguyen@machinemagic.co'
if (!BASE_URL) {
console.error('Usage: node scripts/mint-invite-link.cjs <base-url> [email]')
process.exit(1)
}
const secret = process.env.NUXT_JWT_SECRET || process.env.JWT_SECRET
if (!secret) {
console.error('Missing NUXT_JWT_SECRET / JWT_SECRET in .env')
process.exit(1)
}
;(async () => {
await mongoose.connect(process.env.MONGODB_URI)
const db = mongoose.connection.db
const preReg = await db.collection('preregistrations').findOne({ email: EMAIL })
if (!preReg) {
console.error(`No preregistration found for ${EMAIL}`)
await mongoose.disconnect()
process.exit(1)
}
const jti = randomUUID()
const token = jwt.sign(
{ preRegistrationId: preReg._id.toString(), jti, type: 'prereg-invite' },
secret,
{ expiresIn: '48h' },
)
await db.collection('preregistrations').updateOne(
{ _id: preReg._id },
{ $set: { magicLinkJti: jti, magicLinkJtiUsed: false, status: 'invited' } },
)
const link = `${BASE_URL.replace(/\/$/, '')}/accept-invite#${token}`
console.log('\nFresh invite link for', EMAIL, ':\n')
console.log(link, '\n')
await mongoose.disconnect()
})()