feature plus ux rearrangements
This commit is contained in:
@@ -5,6 +5,9 @@ import { createPbClient } from '$lib/server/pocketbase';
|
||||
import { setSessionCookie } from '$lib/server/session';
|
||||
import { issueAccess } from '$lib/server/member-otp';
|
||||
import { slugify, handle, famUsername, handleOf } from '@shared/slugify';
|
||||
import { applyAccessCode } from '$lib/server/access';
|
||||
import { createEmbeddedCheckoutSession, PLAN_IDS } from '$lib/server/stripe';
|
||||
import type { PlanId } from '$lib/server/stripe';
|
||||
|
||||
class SignupError extends Error {}
|
||||
|
||||
@@ -32,11 +35,14 @@ export const actions = {
|
||||
const username = famUsername(slug, handleName);
|
||||
|
||||
try {
|
||||
const fam = await pbAdmin.create('fams', {
|
||||
const famData: Record<string, unknown> = {
|
||||
name: famName,
|
||||
slug,
|
||||
timezone: 'auto'
|
||||
});
|
||||
timezone: 'auto',
|
||||
paymentMode: 'none',
|
||||
active: false
|
||||
};
|
||||
const fam = await pbAdmin.create('fams', famData);
|
||||
const user = await pbAdmin.create('users', {
|
||||
username,
|
||||
name: parentName,
|
||||
@@ -61,7 +67,6 @@ export const actions = {
|
||||
.catch(() => null);
|
||||
if (authResult?.token) setSessionCookie(event.cookies, authResult.token);
|
||||
|
||||
// `username` here is the handle (URL segment), not the composite.
|
||||
return { success: true, famSlug: slug, username: handleName };
|
||||
},
|
||||
|
||||
@@ -71,11 +76,14 @@ export const actions = {
|
||||
const fd = await event.request.formData();
|
||||
const name = ((fd.get('member') as string) || '').trim();
|
||||
|
||||
// No [fam] URL param here (signup isn't fam-scoped) — resolve the slug
|
||||
// from DB. Everywhere else, the slug comes from event.params.fam /
|
||||
// page.data.famSlug ([fam] layout load) — never copy it into state.
|
||||
const fam = await pbAdmin.getOne('fams', user.famId);
|
||||
const famSlug = fam?.slug || user.famId;
|
||||
|
||||
if (!name) {
|
||||
return { success: true, famSlug, username: handleOf(user.username) };
|
||||
return { success: true, famSlug, username: handleOf(user.username || '') };
|
||||
}
|
||||
|
||||
const { otp, joinUrl } = await issueAccess({
|
||||
@@ -84,11 +92,57 @@ export const actions = {
|
||||
name
|
||||
});
|
||||
|
||||
return { success: true, code: otp, joinUrl, famSlug, username: handleOf(user.username) };
|
||||
return { success: true, code: otp, joinUrl, famSlug, username: handleOf(user.username || '') };
|
||||
},
|
||||
|
||||
// Step 3 — apply an access code (or skip via client-side navigation).
|
||||
access: async (event: RequestEvent) => {
|
||||
const user = requireUser(event);
|
||||
const fd = await event.request.formData();
|
||||
const code = ((fd.get('code') as string) || '').trim();
|
||||
|
||||
if (!code) {
|
||||
return { ok: true, skipped: true };
|
||||
}
|
||||
|
||||
const result = await applyAccessCode(user.famId, code);
|
||||
if (result.error) return fail(400, { error: result.error });
|
||||
|
||||
return { ok: true, ...result };
|
||||
},
|
||||
|
||||
// Step 4 — choose a plan, start embedded checkout.
|
||||
choose: async (event: RequestEvent) => {
|
||||
const user = requireUser(event);
|
||||
const fd = await event.request.formData();
|
||||
const plan = fd.get('plan') as PlanId;
|
||||
|
||||
if (!['monthly', 'yearly'].includes(plan)) {
|
||||
return fail(400, { error: 'Unknown plan' });
|
||||
}
|
||||
|
||||
const fam = await pbAdmin.getOne('fams', user.famId);
|
||||
const parents = await pbAdmin.getList('users', `famId = '${user.famId}' && role = 'parent'`);
|
||||
const email = (parents[0] as { email?: string } | undefined)?.email || null;
|
||||
|
||||
try {
|
||||
const { clientSecret, sessionId } = await createEmbeddedCheckoutSession({
|
||||
plan,
|
||||
famId: user.famId,
|
||||
famSlug: fam.slug,
|
||||
email,
|
||||
customerId: fam.stripeCustomerId || null,
|
||||
origin: event.url.origin
|
||||
});
|
||||
return { success: true, clientSecret, sessionId, plan };
|
||||
} catch (e) {
|
||||
if (e instanceof redirect) throw e;
|
||||
return fail(500, { error: e instanceof Error ? e.message : 'Failed to start checkout' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function requireUser(event: RequestEvent) {
|
||||
if (!event.locals.user) throw redirect(303, '/signup');
|
||||
return event.locals.user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user