08 — Form Specifications (Parents Module)
- 1. Create Parent —
POST /parents(create-parent.dto.ts) - 2. Update Parent —
PATCH /parents/:id(update-parent.dto.ts) - 3. Link Parent —
POST /parents/link/:studentId(link-parent.dto.ts) - 4. Form-level rules (all)
- Client-side error priority (all forms)
Every form field-by-field. Validation mirrors class-validator decorators exactly from
src/modules/parents/dto/*.ts; server failures come back asVALIDATION_ERROR(400) with per-fielddetails(http-exception.filter.ts:104-107). Client validates inline first; server 400 shadows client.
1. Create Parent — POST /parents (create-parent.dto.ts)
| # | Field | Type | Required | Server decorator | Notes / UX |
|---|---|---|---|---|---|
| 1 | userId | ObjectId picker | ✓ | @IsMongoId() (create-parent.dto.ts:6-7) | hidden behind UserPickerField (07 §6); the only required field |
| 2 | occupation | text | no | @IsOptional() @IsString() (:10-12) | autofill organization-title |
| 3 | company | text | no | @IsOptional() @IsString() (:14-16) | autofill organization |
| 4 | annualIncome | number (currency) | no | @IsOptional() no type decorator (:19-21) | client NumberFormat parse; display as currency; no Min/Max server-side → client bounds 0–1e9 (propose server bounds) |
| 5 | relationshipNotes | text (multiline) | no | @IsOptional() @IsString() (:23-26) | maxLength 500 (client) |
| 6 | emergencyContactPriority | number (0–99) | no | @IsOptional() no type decorator (:28-30) | default 0 (parent.schema.ts:24-25); client bounds 0–99; hint "lower = called first" |
| 7 | pickupAuthorization | switch | no | @IsOptional() no type decorator (:32-34) | default false (parent.schema.ts:28); "Authorized to pick up child" |
Submit → loading → server:
- 201/200
data= parent doc (id, userId, flags,createdAt/updatedAt/versionfromBaseSchema,base.schema.ts:8-35). - 409
DUPLICATE_RESOURCE→ banner + "Open existing profile" (parent.service.ts:32-34). - 400 details → per-field (
http-exception.filter.ts:104-107).
Note on DTO typeless numerics/booleans:
annualIncome,emergencyContactPriority,pickupAuthorizationinCreateParentDtohave no@IsNumber()/@IsBoolean()decorators (create-parent.dto.ts:18-34) — strings pass validation and would be stored. Client must send proper JSON types.
2. Update Parent — PATCH /parents/:id (update-parent.dto.ts)
Same field set as §1 minus userId (not updatable) plus metadata:
| # | Field | Type | Required | Server decorator |
|---|---|---|---|---|
| 8 | occupation | text | no | @IsOptional() @IsString() (update-parent.dto.ts:6-8) |
| 9 | company | text | no | @IsOptional() @IsString() (:10-12) |
| 10 | annualIncome | number | no | @IsOptional() (:14-16) |
| 11 | relationshipNotes | text | no | @IsOptional() @IsString() (:18-20) |
| 12 | emergencyContactPriority | number | no | @IsOptional() (:22-24) |
| 13 | pickupAuthorization | boolean | no | @IsOptional() (:26-28) |
| 14 | metadata | object | no | @IsOptional() (:32-34) |
PATCH semantics: partial — only sent keys are set ($set, parent.service.ts:76);
version incremented by repository (base.repository.ts:62-65). Client sends only
changed fields. 404 "Parent not found." (parent.service.ts:77).
3. Link Parent — POST /parents/link/:studentId (link-parent.dto.ts)
| # | Field | Type | Required | Server decorator | Notes / UX |
|---|---|---|---|---|---|
| 15 | parentId | ObjectId | ✓ | @IsMongoId() (link-parent.dto.ts:6-7) | fixed by context (from parent detail) |
| 16 | relationship | dropdown | ✓ | @IsString() (:19-20) — free string, no enum | must send exactly one of mother|father|guardian|grandparent|relative|foster_parent (student-parent-link.schema.ts:7-14); client dropdown guarantees; invalid value → schema rejection → 500 (OQ-6) |
| 17 | isPrimaryGuardian | switch | no | @IsOptional() (:23-24) | default false; warn when another primary exists (OQ-5) |
| 18 | financialResponsibility | switch | no | @IsOptional() (:26-28) | default false (student-parent-link.schema.ts:31) |
| 19 | pickupAllowed | switch | no | @IsOptional() (:30-32) | default true (student-parent-link.schema.ts:33-34) — opposite of profile pickupAuthorization |
| 20 | emergencyPriority | number (0–99) | no | @IsOptional() (:34-36) | default 0 (student-parent-link.schema.ts:36-37) |
studentId comes from the route param, never the body (student-parent-link.service.ts:16-32).
Client-side pre-checks before submit:
- pair
(parentId, studentId)already in loaded links → warn duplicate (OQ-2). isPrimaryGuardiantrue while another link for this student is primary → warn (OQ-5).- student picker required when opened from parent detail; parent picker required when opened from student detail.
Errors: 400 validation; 404 student (only if server later enforces — OQ-3); success →
link doc in data.
4. Form-level rules (all)
- Double-submit disabled while pending (00-shared/08 §6).
- No optimistic writes for any parents mutation (links are consequential; server truth), except none — all server-first (00-shared/06 §3.5).
- Undo: none for unlink (irreversible server-side); snackbar confirm only.
- Keyboard:
.nextsequence, last.done;Entersubmits (desktop). - Autofill hints on identity-ish fields (occupation/company).
- Error copy: envelope
messagefor business 4xx (409/404); codes for the rest (07_API_Conventions.md §11).
Client-side error priority (all forms)
- 400
VALIDATION_ERROR→ field. - 404
RESOURCE_NOT_FOUND→ banner + context action. - 409
DUPLICATE_RESOURCE→ inline + link to existing. - 422
BUSINESS_RULE_VIOLATION→ banner (not produced by this module today). - 429
RATE_LIMITED→ countdown. - 5xx →
AppErrorState+ requestId.