diff --git a/src/app/intake/[token]/p/[projectId]/page.tsx b/src/app/intake/[token]/p/[projectId]/page.tsx new file mode 100644 index 0000000..5094c7f --- /dev/null +++ b/src/app/intake/[token]/p/[projectId]/page.tsx @@ -0,0 +1,71 @@ +import { notFound } from "next/navigation"; + +import { getAdminPb } from "@/lib/pb/admin"; +import { IntakePublic } from "@/components/intake/intake-public"; +import { resolveIntakeLink } from "@/lib/intake-links"; +import { resolveProjectForClient } from "@/lib/intake-links/client-projects"; +import type { ProjectRecord } from "@/lib/pb/types"; + +/** + * The wizard for a Phase 9 client-bound link. + * + * Resolves the token to a link, the URL's projectId to a project on + * that link's client. Both checks fail closed: unknown token, unknown + * project, project on the wrong client, project not opted in, project + * archived → 404 with the same wording as the picker so a hostile + * body cannot probe. + * + * What ends up rendered is the same `` Phase 8 wrote, with + * `projectId` and `token` plumbed through. The wizard component itself + * does not change. + */ +export default async function IntakeWizardPage({ + params, +}: { + params: Promise<{ token: string; projectId: string }>; +}) { + const { token, projectId } = await params; + + const link = await resolveIntakeLink(token); + if (!link || !link.active) notFound(); + if ( + link.expires_at && + link.expires_at < new Date().toISOString().slice(0, 10) + ) { + notFound(); + } + + // Phase 8 backwards compat: per-project link → ignore URL projectId, + // use the link's own project. + let resolvedProjectId = projectId; + if (link.project && !link.client) { + resolvedProjectId = link.project; + } else if (!link.client) { + notFound(); + } else { + const project = await resolveProjectForClient(link.client, projectId); + if (!project) notFound(); + resolvedProjectId = project.id; + } + + // Read the project for the header text. + const pb = await getAdminPb(); + const project = await pb + .collection("projects") + .getOne(resolvedProjectId); + + return ( +
+
+

+ Request to {project.name} +

+
+ +
+ ); +}