Add admin-emailed one-time intake links

- intake_tokens table (server-only via service role)
- Server functions: createIntakeToken (admin), getIntakeToken (public validate),
  submitIntake (public write + mark used, 14-day one-time tokens)
- Public /intake/$token full intake form (no login) with valid/used/expired states
- Student profile (admin): generate link, copy, and email-to-parent (mailto)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 18:00:39 -04:00
co-authored by Claude Opus 4.8
parent eb94c7ec34
commit 90e3c2c188
6 changed files with 393 additions and 1 deletions
+21
View File
@@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
import { Route as AuthRouteImport } from './routes/auth'
import { Route as AuthenticatedRouteRouteImport } from './routes/_authenticated/route'
import { Route as IndexRouteImport } from './routes/index'
import { Route as IntakeTokenRouteImport } from './routes/intake.$token'
import { Route as AuthenticatedStudentsRouteImport } from './routes/_authenticated/students'
import { Route as AuthenticatedMessagesRouteImport } from './routes/_authenticated/messages'
import { Route as AuthenticatedLedgerRouteImport } from './routes/_authenticated/ledger'
@@ -41,6 +42,11 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const IntakeTokenRoute = IntakeTokenRouteImport.update({
id: '/intake/$token',
path: '/intake/$token',
getParentRoute: () => rootRouteImport,
} as any)
const AuthenticatedStudentsRoute = AuthenticatedStudentsRouteImport.update({
id: '/students',
path: '/students',
@@ -127,6 +133,7 @@ export interface FileRoutesByFullPath {
'/ledger': typeof AuthenticatedLedgerRoute
'/messages': typeof AuthenticatedMessagesRoute
'/students': typeof AuthenticatedStudentsRouteWithChildren
'/intake/$token': typeof IntakeTokenRoute
'/classes/$id': typeof AuthenticatedClassesIdRoute
'/students/$id': typeof AuthenticatedStudentsIdRoute
'/students/new': typeof AuthenticatedStudentsNewRoute
@@ -143,6 +150,7 @@ export interface FileRoutesByTo {
'/forms': typeof AuthenticatedFormsRoute
'/ledger': typeof AuthenticatedLedgerRoute
'/messages': typeof AuthenticatedMessagesRoute
'/intake/$token': typeof IntakeTokenRoute
'/classes/$id': typeof AuthenticatedClassesIdRoute
'/students/$id': typeof AuthenticatedStudentsIdRoute
'/students/new': typeof AuthenticatedStudentsNewRoute
@@ -163,6 +171,7 @@ export interface FileRoutesById {
'/_authenticated/ledger': typeof AuthenticatedLedgerRoute
'/_authenticated/messages': typeof AuthenticatedMessagesRoute
'/_authenticated/students': typeof AuthenticatedStudentsRouteWithChildren
'/intake/$token': typeof IntakeTokenRoute
'/_authenticated/classes/$id': typeof AuthenticatedClassesIdRoute
'/_authenticated/students/$id': typeof AuthenticatedStudentsIdRoute
'/_authenticated/students/new': typeof AuthenticatedStudentsNewRoute
@@ -183,6 +192,7 @@ export interface FileRouteTypes {
| '/ledger'
| '/messages'
| '/students'
| '/intake/$token'
| '/classes/$id'
| '/students/$id'
| '/students/new'
@@ -199,6 +209,7 @@ export interface FileRouteTypes {
| '/forms'
| '/ledger'
| '/messages'
| '/intake/$token'
| '/classes/$id'
| '/students/$id'
| '/students/new'
@@ -218,6 +229,7 @@ export interface FileRouteTypes {
| '/_authenticated/ledger'
| '/_authenticated/messages'
| '/_authenticated/students'
| '/intake/$token'
| '/_authenticated/classes/$id'
| '/_authenticated/students/$id'
| '/_authenticated/students/new'
@@ -229,6 +241,7 @@ export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AuthenticatedRouteRoute: typeof AuthenticatedRouteRouteWithChildren
AuthRoute: typeof AuthRoute
IntakeTokenRoute: typeof IntakeTokenRoute
}
declare module '@tanstack/react-router' {
@@ -254,6 +267,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/intake/$token': {
id: '/intake/$token'
path: '/intake/$token'
fullPath: '/intake/$token'
preLoaderRoute: typeof IntakeTokenRouteImport
parentRoute: typeof rootRouteImport
}
'/_authenticated/students': {
id: '/_authenticated/students'
path: '/students'
@@ -416,6 +436,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AuthenticatedRouteRoute: AuthenticatedRouteRouteWithChildren,
AuthRoute: AuthRoute,
IntakeTokenRoute: IntakeTokenRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)