Frontend for the DFS RAISE Act Frontier AI Company Registration Portal. AI development companies use this 6-step wizard to register with the New York State Department of Financial Services. The frontend is complete and production-ready; this document is for the backend team taking over API integration.
- Framework: React 19 + TypeScript (strict mode)
- Build: Vite 8 (dev server + production bundle)
- Routing: React Router 7
- Design System: NYSDS 1.18.1 (
businesstheme) - State: React Context +
useReducer(no Redux, no external store) - API layer: Fully stubbed — replace stubs to connect a real backend
npm install
npm run dev # Dev server on http://localhost:5173
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server with hot reload |
npm run build |
TypeScript check + production bundle |
npm run preview |
Preview production build locally |
npm run lint |
Run ESLint |
npm run screenshots |
Capture a full-page screenshot of every LFD-facing view (see below) |
- Node.js 18+
- npm 9+
No environment variables are required to run the app locally — all API calls are currently stubbed with mock responses and a simulated 500ms delay.
npm run screenshots drives a headless Chromium browser (Playwright) through every
LFD-facing view and writes a full-page @2x PNG for each to screenshots/.
The latest captures are committed to that folder; re-running the script
refreshes them. Useful for documentation, design review, and spotting visual
regressions.
Playwright is already a dev dependency (installed by npm install), but the
browser binary is downloaded separately:
npx playwright install chromium
The script captures a running instance of the app, so start the server first, then run the script in a second terminal:
npm run dev # terminal 1 — leave running
npm run screenshots # terminal 2
Output lands in screenshots/, numbered in walkthrough order:
| File | View |
|---|---|
01-auth-signin |
Sign-in |
02-auth-details |
Authorization Details |
03-auth-submitted |
Authorization Submitted Successfully |
04-disclosure-landing |
Disclosure Statement Landing Page |
05-disclosure-business-info |
Business Information |
06-disclosure-address |
Address |
07-disclosure-ownership |
Ownership |
08-disclosure-contacts |
Contacts |
09-disclosure-review |
Review and Certify |
10-disclosure-submitted |
Disclosure Statement Submitted Successfully |
To capture a production build instead of the dev server, point BASE_URL at the
preview server:
npm run build && npm run preview
BASE_URL=http://localhost:4173/project-raise-act npm run screenshots
Notes:
- The app is served under Vite's
/project-raise-act/base path, soBASE_URLincludes it. The default targets the dev server (http://localhost:5173/project-raise-act). - The app sits behind a client-side password gate (
index.html). The script bypasses it automatically by pre-seedinglocalStorage.authed, so no password is needed. - To add or reorder views, edit the
PAGESlist inscripts/screenshots.mjs.
src/
├── api/
│ └── stubs.ts # ← Replace these with real fetch calls
├── components/
│ ├── wrappers/ # @lit/react wrappers for NYSDS web components
│ ├── AppShell.tsx # Global layout (headers, footers, stepper sidebar)
│ ├── StepNavigation.tsx # Back/Continue button pair
│ ├── RepeatableFieldGroup.tsx
│ ├── ReviewSection.tsx
│ └── SuccessConfirmation.tsx
├── context/
│ └── RegistrationContext.tsx # All form state lives here
├── steps/
│ ├── 01-BusinessInfo.tsx
│ ├── 02-Addresses.tsx
│ ├── 03-Ownership.tsx
│ ├── 04-Contacts.tsx
│ ├── 05-Documents.tsx
│ ├── 06-ReviewCertify.tsx
│ └── 07-SuccessPage.tsx
├── types/
│ └── registration.ts # TypeScript interfaces — match these in your backend
└── utils/
└── validation.ts # Client-side validators (mirror these server-side)
All API calls go through src/api/stubs.ts. Replace the three stub functions with real fetch calls — the frontend code doesn't need to change anywhere else.
// Save draft at any step
export async function saveRegistration(
step: number,
data: Partial<RegistrationData>,
): Promise<SaveResponse>;
// Final submission (called on Step 6 — Review & Certify)
export async function submitRegistration(
data: RegistrationData,
): Promise<SubmitResponse>;
// Load a saved draft by ID
export async function loadRegistration(id: string): Promise<LoadResponse>;interface SaveResponse {
success: boolean;
registrationId: string; // e.g. "REG-DRAFT-001"
}
interface SubmitResponse {
success: boolean;
registrationId: string; // e.g. "REG-2024-00042" — shown on success page
submittedAt: string; // ISO 8601 datetime — shown on success page
}
interface LoadResponse {
data: Partial<RegistrationData>;
currentStep: number;
}| Method | Path | Description |
|---|---|---|
POST |
/api/registration/save |
Save draft for a given step |
POST |
/api/registration/submit |
Final submission |
GET |
/api/registration/:id |
Load saved draft |
POST |
/api/registration/:id/documents |
Upload supporting documents |
Note: The document upload endpoint is not yet stubbed — file handling will need to be added to both
stubs.tsand Step 5 (05-Documents.tsx).
Defined in src/types/registration.ts. Mirror this in your backend schema.
interface RegistrationData {
businessInfo: {
legalName: string;
additionalNames: string[];
ownershipStructure: "private" | "public" | "";
};
addresses: {
principal: Address;
nyOffices: Address[];
};
ownership: {
current: Owner[];
former: Owner[];
};
contacts: {
primary: Contact;
secondary: Contact | null;
tertiary: Contact | null;
};
documents: File[];
certification: boolean;
}
interface Address {
street: string;
suite: string;
city: string;
state: string;
zip: string;
}
interface Owner {
type: "person" | "entity";
firstName: string; // person only
lastName: string; // person only
entityName: string; // entity only
percentageOwned: number;
startDate: string; // ISO date (YYYY-MM-DD)
endDate?: string; // ISO date — required for former owners
}
interface Contact {
firstName: string;
lastName: string;
title: string;
phone: string; // 10 digits, formatting stripped client-side
email: string;
}Client-side validation is in src/utils/validation.ts. These rules should be mirrored server-side.
| Field | Rule |
|---|---|
| Required fields | Non-empty string or valid number |
Standard format (user@domain.tld) |
|
| Phone | 10 digits after stripping non-numeric characters |
| Zip code | 12345 or 12345-6789 |
| Percentage | 0–100 (numeric) |
Former owner endDate |
Must be after startDate |
| Certification | Must be true to submit |
| Step | Required |
|---|---|
| 1 — Business Info | legalName, ownershipStructure |
| 2 — Addresses | principal.street, principal.city, principal.state, principal.zip |
| 3 — Ownership | Per owner: type, name fields (based on type), percentageOwned, startDate; former owners also require endDate |
| 4 — Contacts | primary: all five fields (firstName, lastName, title, phone, email) |
| 5 — Documents | No required fields — upload is optional |
| 6 — Review & Certify | certification: true |
The app is a single-page application. All routes are client-side — your backend only needs to serve index.html for any path under /register/*.
| Route | Step |
|---|---|
/register/business-info |
Step 1 — Business Info |
/register/addresses |
Step 2 — Addresses |
/register/ownership |
Step 3 — Ownership |
/register/contacts |
Step 4 — Contacts |
/register/documents |
Step 5 — Documents |
/register/review |
Step 6 — Review & Certify |
/register/success |
Success page (shown after submission) |
The frontend uses the New York State Design System (NYSDS) v1.18.1. You do not need to interact with NYSDS for backend work, but here is context in case you need to run or modify the frontend:
- Theme:
business— set viadata-theme="business"on<html>inindex.html - Components: NYSDS ships as HTML custom elements (web components). In this React project they are wrapped with
@lit/react— seesrc/components/wrappers/ - Fonts: Proxima Nova and D Sari, loaded from
/public/fonts/— no CDN dependency - Styling: All custom styles use NYSDS CSS design tokens (
--nys-color-*,--nys-space-*, etc.) — no hardcoded values
If you need to look up a component's API or available tokens, the NYSDS documentation is at designsystem.ny.gov.
- API endpoints — Implement the three routes above (save, submit, load). See stub signatures for request/response shapes.
- Database schema — Store draft and submitted registrations. The
RegistrationDatainterface above is the shape of the data you will receive. - File storage — Step 5 collects supporting documents. The frontend currently has no upload endpoint wired up — this needs to be built on both sides.
- Registration ID generation — The success page displays the
registrationIdreturned bysubmitRegistration. Use a human-readable format (e.g.,REG-2025-00042). - Session / draft persistence — The frontend calls
saveRegistrationon each step's Continue click. The backend should associate drafts with a session or user so they can be resumed. - Authentication — Not yet implemented on the frontend. User login, session management, and authorized representative verification are out of scope for the frontend handoff.
- Email notifications — Send a confirmation email after successful submission. The
submittedAttimestamp andregistrationIdfromSubmitResponseappear on the success page and should also go in the email. - Server-side validation — Mirror the client-side rules in
src/utils/validation.ts. Never trust client-only validation for final submission.
| File | Description |
|---|---|
raise-act-prd.md |
Full product requirements — data model, business rules, gap analysis |
implementation-plan.md |
Frontend build plan (all phases complete) — useful for understanding what was built and why |
src/api/stubs.ts |
The three API stubs to replace |
src/types/registration.ts |
TypeScript data model |
src/utils/validation.ts |
All client-side validation rules |
screenshots/ |
Visual mockups for each step |