Merge branch 'fix/launch-flow-copy-and-pre-reg-link'
Ships the 5 launch-flow fixes decided 2026-04-30: - /join, dashboard, welcome-email copy aligned to monthly-waves model - welcome email now sends on free /accept-invite activations - /join signups auto-link to matching PreRegistration records
This commit is contained in:
commit
d9444b022b
7 changed files with 51 additions and 4 deletions
|
|
@ -317,13 +317,17 @@
|
||||||
<ParchmentInset>
|
<ParchmentInset>
|
||||||
<h2>How membership works</h2>
|
<h2>How membership works</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Full access to the knowledge commons, Slack, and peer support</li>
|
<li>Full access to the knowledge commons, events and workshops, and community</li>
|
||||||
<li>Free access to all Ghost Guild events</li>
|
<li>Free access to all Ghost Guild events</li>
|
||||||
<li>Equal access for every member, regardless of contribution</li>
|
<li>Equal access for every member, regardless of contribution</li>
|
||||||
<li>Your circle reflects where you are, not rank</li>
|
<li>Your circle reflects where you are, not rank</li>
|
||||||
<li>Pay what you can ($0–$50+/month, separate from circle)</li>
|
<li>Pay what you can ($0–$50+/month, separate from circle)</li>
|
||||||
<li>Higher contributions create solidarity spots for others</li>
|
<li>Higher contributions create solidarity spots for others</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p>
|
||||||
|
Community connection happens in our Slack workspace, joined in monthly
|
||||||
|
onboarding waves — there may be a short wait after you join.
|
||||||
|
</p>
|
||||||
</ParchmentInset>
|
</ParchmentInset>
|
||||||
|
|
||||||
<!-- THREE CIRCLES -->
|
<!-- THREE CIRCLES -->
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
<span>${{ memberData?.contributionAmount ?? 0 }} CAD/mo</span>
|
<span>${{ memberData?.contributionAmount ?? 0 }} CAD/mo</span>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="showSlackComingNote" class="slack-coming-note">
|
<p v-if="showSlackComingNote" class="slack-coming-note">
|
||||||
Slack workspace access is part of your membership. Your invitation
|
Slack workspace access is part of your membership. Invitations are
|
||||||
typically arrives within 2–3 weeks of joining.
|
sent in monthly onboarding waves — we'll be in touch.
|
||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { getRequestHeader, getRequestIP } from 'h3'
|
||||||
import Member from '../../models/member.js'
|
import Member from '../../models/member.js'
|
||||||
import { connectDB } from '../../utils/mongoose.js'
|
import { connectDB } from '../../utils/mongoose.js'
|
||||||
import { createHelcimCustomer } from '../../utils/helcim.js'
|
import { createHelcimCustomer } from '../../utils/helcim.js'
|
||||||
|
import PreRegistration from '../../models/preRegistration.js'
|
||||||
import { sendMagicLink } from '../../utils/magicLink.js'
|
import { sendMagicLink } from '../../utils/magicLink.js'
|
||||||
import { setPaymentBridgeCookie } from '../../utils/auth.js'
|
import { setPaymentBridgeCookie } from '../../utils/auth.js'
|
||||||
import { rateLimit } from '../../utils/rateLimit.js'
|
import { rateLimit } from '../../utils/rateLimit.js'
|
||||||
|
|
@ -82,6 +83,32 @@ export default defineEventHandler(async (event) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this email matches a pending pre-registrant, mark the PreRegistration
|
||||||
|
// as accepted and link it to the new Member. Silent — keeps /join and
|
||||||
|
// /admin/pre-registrants from showing the same person twice.
|
||||||
|
try {
|
||||||
|
const preReg = await PreRegistration.findOne({ email: normalizedEmail })
|
||||||
|
if (
|
||||||
|
preReg &&
|
||||||
|
!preReg.memberId &&
|
||||||
|
['pending', 'selected', 'invited'].includes(preReg.status)
|
||||||
|
) {
|
||||||
|
await PreRegistration.findByIdAndUpdate(
|
||||||
|
preReg._id,
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
status: 'accepted',
|
||||||
|
acceptedAt: new Date(),
|
||||||
|
memberId: member._id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ runValidators: false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (linkError) {
|
||||||
|
console.error('Failed to link PreRegistration to new member:', linkError)
|
||||||
|
}
|
||||||
|
|
||||||
await sendMagicLink(normalizedEmail, {
|
await sendMagicLink(normalizedEmail, {
|
||||||
subject: 'Verify your Ghost Guild signup',
|
subject: 'Verify your Ghost Guild signup',
|
||||||
intro: 'Verify your email to finish your Ghost Guild signup:',
|
intro: 'Verify your email to finish your Ghost Guild signup:',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { connectDB } from '../../utils/mongoose.js'
|
||||||
import { setAuthCookie } from '../../utils/auth.js'
|
import { setAuthCookie } from '../../utils/auth.js'
|
||||||
import { assignMemberNumber } from '../../utils/memberNumber.js'
|
import { assignMemberNumber } from '../../utils/memberNumber.js'
|
||||||
import { createHelcimCustomer } from '../../utils/helcim.js'
|
import { createHelcimCustomer } from '../../utils/helcim.js'
|
||||||
|
import { sendWelcomeEmail } from '../../utils/resend.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const body = await validateBody(event, inviteAcceptSchema)
|
const body = await validateBody(event, inviteAcceptSchema)
|
||||||
|
|
@ -88,6 +89,15 @@ export default defineEventHandler(async (event) => {
|
||||||
// For free tier, redirect to welcome
|
// For free tier, redirect to welcome
|
||||||
if (body.contributionAmount === 0) {
|
if (body.contributionAmount === 0) {
|
||||||
await autoFlagPreExistingSlackAccess(member)
|
await autoFlagPreExistingSlackAccess(member)
|
||||||
|
try {
|
||||||
|
await sendWelcomeEmail(member)
|
||||||
|
logActivity(member._id, 'email_sent', {
|
||||||
|
emailType: 'welcome',
|
||||||
|
subject: 'Welcome to Ghost Guild'
|
||||||
|
})
|
||||||
|
} catch (emailError) {
|
||||||
|
console.error('Failed to send welcome email:', emailError)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
requiresPayment: false,
|
requiresPayment: false,
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ Welcome to Ghost Guild! You're now part of the ${member.circle} circle.
|
||||||
Sign in to your dashboard to get started:
|
Sign in to your dashboard to get started:
|
||||||
${baseUrl}/member/dashboard
|
${baseUrl}/member/dashboard
|
||||||
|
|
||||||
If you have questions, reach out to jennie + eileen on Slack or reply to this email.`,
|
If you have questions, just reply to this email.`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ vi.mock('../../../server/models/member.js', () => ({
|
||||||
findOneAndUpdate: vi.fn()
|
findOneAndUpdate: vi.fn()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
vi.mock('../../../server/models/preRegistration.js', () => ({
|
||||||
|
default: { findOne: vi.fn().mockResolvedValue(null), findByIdAndUpdate: vi.fn() }
|
||||||
|
}))
|
||||||
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
|
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
|
||||||
vi.mock('../../../server/utils/helcim.js', () => ({
|
vi.mock('../../../server/utils/helcim.js', () => ({
|
||||||
createHelcimCustomer: vi.fn(),
|
createHelcimCustomer: vi.fn(),
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ import { createMockEvent } from '../helpers/createMockEvent.js'
|
||||||
vi.mock('../../../server/models/member.js', () => ({
|
vi.mock('../../../server/models/member.js', () => ({
|
||||||
default: { findOne: vi.fn(), create: vi.fn(), findByIdAndUpdate: vi.fn() }
|
default: { findOne: vi.fn(), create: vi.fn(), findByIdAndUpdate: vi.fn() }
|
||||||
}))
|
}))
|
||||||
|
vi.mock('../../../server/models/preRegistration.js', () => ({
|
||||||
|
default: { findOne: vi.fn().mockResolvedValue(null), findByIdAndUpdate: vi.fn() }
|
||||||
|
}))
|
||||||
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
|
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
|
||||||
vi.mock('../../../server/utils/helcim.js', () => ({
|
vi.mock('../../../server/utils/helcim.js', () => ({
|
||||||
createHelcimCustomer: vi.fn()
|
createHelcimCustomer: vi.fn()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue