a0dc94ce79
- Redesign onboarding into a 5-step wizard: welcome → phone → consent → code → done - Progress bar, human-readable consent scopes (INGEST/ARCHIVE/REPLICATE/DISPLAY) with descriptions - Resend-code action, back navigation, indigo theme matching member portal - "Done" step introduces portal features (digests, events, privacy) with CTA - Onboard page shell: rounded card, policy footer - Restore Thread model + Message.expiresAt/quotedPlatformMsgId/threadId + RAW/DNC statuses to schema.prisma (wiped by prior reset; matches existing add_hybrid_ai_architecture migration) — unblocks ThreadsModule registration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import { OnboardingForm } from './OnboardingForm';
|
|
|
|
interface PublicOnboardInfo {
|
|
groupName: string;
|
|
tenantName: string;
|
|
policyVersion: string;
|
|
defaultScopes: string[];
|
|
defaultRetentionDays: number;
|
|
}
|
|
|
|
export default async function OnboardPage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ token?: string }>;
|
|
}) {
|
|
const { token } = await searchParams;
|
|
if (!token) {
|
|
return (
|
|
<div className="max-w-md mx-auto mt-12 p-6 rounded-lg border border-red-200 bg-red-50">
|
|
<h1 className="text-lg font-semibold text-red-800">Invalid link</h1>
|
|
<p className="text-sm text-red-700">This onboarding link is missing the token parameter.</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
let info: PublicOnboardInfo | null = null;
|
|
let error: string | null = null;
|
|
try {
|
|
const res = await fetch(
|
|
`${process.env['API_URL'] ?? 'http://localhost:3001'}/public/onboard/${encodeURIComponent(token)}`,
|
|
{ headers: { Accept: 'application/json' }, cache: 'no-store' },
|
|
);
|
|
if (res.ok) {
|
|
info = (await res.json()) as PublicOnboardInfo;
|
|
} else {
|
|
const body = (await res.json().catch(() => ({}))) as { message?: string };
|
|
error = body.message ?? `Onboarding link rejected (${res.status})`;
|
|
}
|
|
} catch (e) {
|
|
error = e instanceof Error ? e.message : 'Network error';
|
|
}
|
|
|
|
if (error || !info) {
|
|
return (
|
|
<div className="max-w-md mx-auto mt-12 p-6 rounded-2xl border border-red-200 bg-red-50">
|
|
<h1 className="text-lg font-semibold text-red-800">Cannot start onboarding</h1>
|
|
<p className="text-sm text-red-700 mt-1">{error ?? 'Unknown error'}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-md mx-auto mt-12">
|
|
<div className="p-6 sm:p-7 rounded-2xl border border-gray-200 bg-white shadow-sm">
|
|
<OnboardingForm
|
|
token={token}
|
|
groupName={info.groupName}
|
|
tenantName={info.tenantName}
|
|
defaultScopes={info.defaultScopes}
|
|
defaultRetentionDays={info.defaultRetentionDays}
|
|
/>
|
|
</div>
|
|
<p className="text-center text-xs text-gray-400 mt-4">
|
|
Policy {info.policyVersion} · Your data, your control
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|