Types Directory
Location: src/types/
Purpose: TypeScript type definitions and interfaces shared across the application. Each file owns a single domain.
Structure
types/
├── campaign.ts # CampaignType (id, name, slug, banners, colors, etc.)
├── common.ts # QueryResponse — { message, error, data }
├── credentialsRecord.ts # External-service credentials records
├── dashboard.ts # Dashboard config + chart shapes for the dashboard builder
├── error.ts # Generic error wrapper type
├── form.ts # FormState — { message, error, data?, zodIssues? }
├── formComponent.ts # Shared form-component prop types
├── permissionKey.ts # PermissionKeyType
├── permissionSetsRecord.ts # Responsibilities (permission-set bundles)
├── petitions.ts # Petition + format + circulator types
├── rateRecord.ts # Rate ladder records for circulators
├── regionRecord.ts # Region (sub-area) records
├── rolesRecord.ts # Role + hierarchy_level + scope
├── signature.ts # Signature row + ESFormEntry
├── teamsRecord.ts # Team + sub-team + UserTeamsRecordType
├── transactionRecord.ts # Circulator transactions
├── turnInRecord.ts # Turn-in records
├── user.ts # UserType
└── voter.ts # Voter / person search types returned by Directus
Key shared types
QueryResponse(common.ts) — return shape for every service insrc/services/**:{ message: string; error: boolean; data: any }.FormState(form.ts) — return shape for every Server Action:{ message: string; error: boolean; data?: unknown; zodIssues?: Record<string, string[]> }.SessionContext(defined insrc/middleware/types.ts, re-used here viaTeamsContext) — surfacespermissionKeys,userTeamRole, etc., to the client.PermissionsSnapshot(defined insrc/lib/permissions/fetchPermissionsSnapshot.ts) —{ teamAccess, campaignAccess?, permissionKeys? }returned by theget-permissions-snapshotedge function.
Conventions
- Files are named after the domain noun, mostly
camelCase.ts. - Database "record" types end in
Record(e.g.rateRecord.ts). - Re-use shared envelopes (
QueryResponse,FormState) instead of inventing per-call shapes.