Forms & Validation
Field-level specifications for every form in the NIVAA platform — data types, validation rules, required/optional status, example values, and rendering components.
Validation Rules Summary
These cross-cutting rules apply across multiple forms and are enforced on both frontend and backend unless noted otherwise.
| Rule | Detail | Enforcement |
|---|---|---|
| Email format | Must match ^[^\s@]+@[^\s@]+\.[^\s@]+$ | Backend + frontend |
| Password minimum | At least 8 characters | Backend + frontend |
| Postal code | Exactly 6 digits | Backend (where applicable) |
| Time slots | end_time must be after start_time | Backend (session log) |
| Read-only fields | nric_name, dob, gender, race — locked after first save on tutor profiles | Backend enforced |
| Role restriction | Self-registration limited to Parent or Tutor only | Backend enforced |
Educator Onboarding
Multi-step onboarding wizard for new tutors. Route: /apply/onboard. Component: src/routes/apply/onboard/+page.svelte. Step 1 calls POST /auth/register to create the account, then Steps 2–5 collect profile and intake data submitted via POST /intake/tutor.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
display_name | string | Yes | Min 1 character | Alex Tan |
email | string | Yes | Valid email format | [email protected] |
password | string | Yes | Min 8 characters | s3cur3Pa$$ |
phone | string | No | Digits only | 91234567 |
subjects | array of {stage, level, subject} | Yes | Min 1 entry required | [{stage:"Primary", level:"P3", subject:"Math"}] |
eip_years | number | No | Integer >= 0, default 0 | 3 |
moe_years | number | No | Integer >= 0, default 0 | 5 |
intl_school_years | number | No | Integer >= 0, default 0 | 2 |
employment_type | string | No | Enum: Full-time / Part-time / Other / Current Allied Educator | Part-time |
area | string | No | Plain text | Bukit Timah |
postal_code | string | No | 6 digits | 588888 |
languages | string[] | No | From: English, Mandarin, Malay, Tamil, Other | ["English", "Mandarin"] |
gender | string | No | Enum: Male / Female. Locked after first save | Female |
race | string | No | Enum: Chinese / Malay / Indian / Other. Locked after first save | Chinese |
introduction | string | No | Textarea, free text | I have 5 years of experience... |
special_needs_experience | string[] | No | From: ASD, ADHD, Dyslexia, GDD, Dyscalculia, Other | ["ASD", "ADHD"] |
past_student_profiles | string | No | Textarea, free text | Taught P3–P6 students with learning differences |
The onboarding flow spans 5 wizard steps. Account creation (Step 1) is separate from intake submission (Step 5). gender and race become read-only after the first successful save.
Login
Standard credential-based login. Route: /login. Component: src/routes/login/+page.svelte. Backend: POST /auth/login.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
email | string | Yes | Valid email format | [email protected] |
password | string | Yes | Min 1 character | s3cur3Pa$$ |
Login password validation is min 1 character (presence check only). The stricter 8-character minimum applies only at registration.
Register
Role-aware self-registration for parents and tutors. Route: /register. Component: src/routes/register/+page.svelte. Backend: POST /auth/register. Coordinator educator setup links (/register?role=tutor&uid={user_id}) use GET /auth/register-prefill and can complete first-time password setup via POST /auth/complete-invite.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
display_name | string | Yes | Min 1 character | Sarah Lim |
email | string | Yes | Valid email format | [email protected] |
password | string | Yes | Min 8 characters | myP@ssw0rd |
role | string | Yes | Enum: Parent / Tutor | parent |
Only Parent and Tutor roles are available for self-registration. Coordinator and Admin accounts are created internally via the admin people page.
The setup-link role query is case-insensitive (role=tutor or role=Tutor). For this setup path, name/email fields are prefilled and locked, and the Singpass info panel is hidden.
Parent Posting
Form for parents to create a new tutoring request. Route: /parent/postings/new. Component: src/routes/parent/postings/new/+page.svelte. Backend: POST /postings.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
child_id | number | Yes | Must be an existing child belonging to the parent (dropdown) | 12 |
subject | string | Yes | From level-based subject picker | Math |
grade | string | No | Auto-prefilled from child's current level | P3 |
session_duration_mins | number | Yes | Enum: 60 / 90 / 120 | 90 |
timing_preferences | array of {day, start_time, end_time} | No | end_time must be after start_time | [{day:"Monday", start_time:"15:00", end_time:"16:30"}] |
location_area | string | No | Plain text | Bukit Timah |
language_preference | string | No | Enum: No preference / English / Mandarin / Malay / Tamil | No preference |
gender_preference | string | No | Enum: No preference / Male / Female | No preference |
special_needs_summary | string | No | Textarea, free text | Child has mild ASD, prefers visual learning |
other_requirements | string | No | Coordinator-only notes, not shown to tutors | Family prefers weekend mornings |
The grade field is auto-prefilled from the selected child's current_level. The subject picker options change dynamically based on the child's curriculum system and level.
Tutor Profile
Editable tutor profile for updating qualifications, availability, and personal details. Route: /tutor/profile. Component: src/routes/tutor/profile/+page.svelte. Backend: POST /intake/tutor.
Legacy TimeTap tutors may have sparse tutor_profiles rows. The read path now falls back to users.display_name, users.phone, and users.address_data.full_address so /tutor/profile still shows core name/contact/location data for older imported accounts.
Operational source-of-truth rule: if rollout sheets disagree with TimeTap on tutor email or educator code, use /home/ubuntu/tmp/nivaa/TimeTap System -NEW.xlsx as canonical. Staged tutor rollouts should be tracked in tutor_profiles.launch_cohort (for example Wave1Demo).
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
subjects | array of {stage, level, subject} | Yes | Min 1 entry | [{stage:"Secondary", level:"Sec 2", subject:"Science"}] |
eip_years | number | No | Integer >= 0 | 3 |
moe_years | number | No | Integer >= 0 | 5 |
intl_school_years | number | No | Integer >= 0 | 0 |
employment_type | string | No | Enum: Full-time / Part-time / Other / Current Allied Educator | Full-time |
area | string | No | Plain text | Tampines |
postal_code | string | No | 6 digits | 520123 |
languages | string[] | No | From: English, Mandarin, Malay, Tamil, Other | ["English"] |
gender | string | No | Locked after first save. Enum: Male / Female | Male |
race | string | No | Locked after first save. Enum: Chinese / Malay / Indian / Other | Indian |
nric_name | string | No | Locked after first save | TAN WEI MING |
dob | date | No | Locked after first save. YYYY-MM-DD | 1995-03-15 |
introduction | string | No | Textarea | Passionate about inclusive education... |
special_needs_experience | string[] | No | From: ASD, ADHD, Dyslexia, GDD, Dyscalculia, Other | ["Dyslexia"] |
past_student_profiles | string | No | Textarea | Primarily secondary school students |
soft_constraints | string | No | Free text preferences | Prefer not to travel more than 30 min |
schedule | array of {day, start_time, end_time} | No | end_time must be after start_time | [{day:"Tuesday", start_time:"14:00", end_time:"18:00"}] |
Fields nric_name, dob, gender, and race are permanently locked after the first save. These values cannot be changed by the tutor — admin intervention is required for corrections.
Parent Profile
Parent profile now covers family particulars and address together. Route: /parent/profile. Component: src/routes/parent/profile/+page.svelte. Backend: GET /profile/parent/me returns display_name, email, phone, address, and created_at; PATCH /profile/parent/me updates display_name, phone, and address.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
address_data.block | string | No | From OneMap autocomplete | 123 |
address_data.street | string | No | From OneMap autocomplete | Bukit Timah Road |
address_data.unit | string | No | Free text | #05-12 |
address_data.postal_code | string | No | 6 digits | 588888 |
address_data.building_name | string | No | From OneMap autocomplete | The Cascadia |
address_data.lat | number | No | Auto-populated from OneMap | 1.3321 |
address_data.lng | number | No | Auto-populated from OneMap | 103.7890 |
address_data.formatted | string | No | Auto-generated full address string | 123 Bukit Timah Road #05-12, 588888 |
Address fields are populated via OneMap autocomplete. The lat, lng, and formatted fields are auto-generated — not manually editable. The address is used for location-based tutor matching. Full parent particulars such as display name and phone still need a backend contract expansion before the profile page can render them as first-class editable fields.
Child Creation
Add a new child to a parent's account. Route: /parent/children/new. Component: src/routes/parent/children/new/+page.svelte. Backend: POST /children.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
display_name | string | Yes | Min 1 character | Amelia |
legal_name | string | No | Free text | Amelia Tan Wei Ling |
dob | date | No | YYYY-MM-DD format | 2016-05-20 |
school_name | string | No | Free text | Nanyang Primary School |
curriculum_system | string | Yes (frontend) | Enum: Singapore / SPED / International / IB / AP / British | Singapore |
current_level | string | Conditional | Required if curriculum is not SPED. Values depend on curriculum system | P3 |
overall_notes | string | No | Textarea, free text | Needs extra help with reading comprehension |
current_level is conditionally required — it must be provided for all curriculum systems except SPED. The available level options change dynamically based on the selected curriculum_system.
Readiness Report
Classroom readiness questionnaire filled by parents, scoring children across developmental domains. Route: /parent/children/[childId]/readiness/new. Component: src/routes/parent/children/[childId]/readiness/new/+page.svelte. Backend: POST /readiness.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
child_id | number | Yes | Derived from URL parameter | 12 |
scores | object | Yes | Keys are domain_item codes, values 0–3 (Not Yet / With Help / Mostly / Independently) | {"B1":2, "B2":3, "C1":1} |
notes_b | string | No | Domain B (Emotional Regulation) notes | Gets frustrated with difficult tasks |
notes_c | string | No | Domain C (Communication & Social) notes | Shy in group settings |
notes_d | string | No | Domain D (Learning Readiness) notes | Strong visual learner |
notes_e | string | No | Domain E (Independence) notes | Needs prompting for task transitions |
notes_g | string | No | Domain G (Literacy & Numeracy) notes | Reading at grade level |
notes_h | string | No | Domain H (Sensory Processing) notes | Sensitive to loud noises |
coping_mechanisms | string | No | Free text | Deep breathing, fidget tools |
likes | string | No | Free text | Drawing, Lego, animals |
triggers | string | No | Free text | Sudden schedule changes |
what_works | string | No | Free text | Visual timers, clear instructions |
status | string | Yes | Enum: Draft / Completed | Completed |
Active domains are: B (Emotional Regulation), C (Communication & Social), D (Learning Readiness), E (Independence), G (Literacy & Numeracy), H (Sensory Processing). Each item within a domain is scored 0–3. Reports can be saved as Draft and completed later.
Session Log
Tutors log completed sessions for approval. Supports single and batch logging. Route: /tutor/sessions/log. Component: src/routes/tutor/sessions/log/+page.svelte. Backend: POST /appointments/log.
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
match_id | number | Yes | From active matches dropdown | 7 |
date | string | Yes | YYYY-MM-DD format | 2026-03-25 |
start_time | string | Yes | HH:MM format, must be before end_time | 14:00 |
end_time | string | Yes | HH:MM format, must be after start_time | 15:30 |
tutor_notes | string | No | Free text | Covered fractions chapter 5 |
billed_as_addon | boolean | No | Default false. Marks session as ad-hoc (exam prep) | false |
Batch logging is supported — the payload accepts a sessions[] array for multiple sessions at once. Each session in the batch follows the same field rules. Ad-hoc sessions (billed_as_addon = true) are tracked separately in analytics and billing.
Add People (Admin)
Admin form for adding coordinators or parents to the platform. Educators are not directly created from admin people tools; they enter through the educator onboarding flow and are reviewed through /admin/tutors. Route: /admin/people. Component: src/routes/admin/people/+page.svelte. Backends: POST /admin/people/add-coordinator, POST /admin/people/add-parent.
Common Fields (all types)
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
display_name | string | Yes | Min 1 character | John Lee |
email | string | Yes | Valid email format | [email protected] |
phone | string | No | Digits only | 81234567 |
Parent-specific Fields
| Field | Type | Required | Validation | Example |
|---|---|---|---|---|
child_name | string | No | Creates a child record linked to the parent | Emma |
Admin-created accounts generate a temporary password. The user must reset their password on first login. Coordinator accounts can only be created by admins — there is no self-registration path for coordinators.