Exact field-by-field form contracts. Validation mirrors server DTOs
(class-validator); field sources are cited. Shared form UX: labels linked,
per-field errors under fields (00-shared/06 §5 ), double-submit guard
(00-shared/08 §6 ), keyboard types per field.
Required = server-required (server is authoritative; client pre-validates).
MongoId fields render as dropdowns/pickers fed by the owning module
(academics grade/section/academicYear/class; staff for assignment/panel) —
cross-module reads (planned) where endpoints are missing; fallback: raw-id field
hidden in "Advanced".
400 VALIDATION_ERROR details map field → message under the matching field.
Optimistic updates: never for create/submit; only for safe toggles (00-shared/06 §3.5 ).
Field Req Control Validation (server) Default Source
firstName ✅ text @IsString— create-lead.dto.ts:12-14
middleName — text optional string — create-lead.dto.ts:16-19
lastName ✅ text @IsString— create-lead.dto.ts:21-23
email ✅ text (.emailAddress, autofill email) @IsEmail; stored lowercase (lead.schema.ts:52-53)— create-lead.dto.ts:25-27
phone — text (.phone, autofill tel) optional string — create-lead.dto.ts:29-32
source — dropdown enum 6 values website (lead.schema.ts:58)create-lead.dto.ts:34-37
status — dropdown (Advanced) enum 5 values new (lead.schema.ts:61)create-lead.dto.ts:39-42
gradeId — dropdown @IsMongoId— create-lead.dto.ts:44-47
sectionId — dropdown @IsMongoId— create-lead.dto.ts:49-52
academicYearId — dropdown @IsMongoId— create-lead.dto.ts:54-57
classId — dropdown @IsMongoId— create-lead.dto.ts:59-62
notes — multiline optional string — create-lead.dto.ts:64-67
assignedTo — staff dropdown @IsMongoId— create-lead.dto.ts:69-72
Edit-only fields: closedReason (string, update-lead.dto.ts:77-80).
status = closed → reveal closedReason (client); server auto-stamps closedAt
when reason absent (crm.service.ts:94-96).
status = converted → blocked in form; Convert flow only (S4).
Placement warning when editing without grade/year/class: "Set all three to enable
conversion" (rule at crm.service.ts:145-149).
Submit → 409 duplicate email banner (crm.service.ts:42-47): "A lead with this
email already exists" + "Edit details" / "Go back".
Field Req Control Validation (server) Source
firstName ✅ text @IsStringcreate-admission.dto.ts:6-8
middleName — text optional create-admission.dto.ts:10-12
lastName ✅ text @IsStringcreate-admission.dto.ts:14-16
email ✅ text .emailAddress @IsEmail; stored lowercase (admission.schema.ts:102-103)create-admission.dto.ts:18-20
phone — text .phone optional string create-admission.dto.ts:22-25
gradeId — dropdown @IsMongoIdcreate-admission.dto.ts:27-30
academicYearId — dropdown @IsMongoIdcreate-admission.dto.ts:32-35
classId — dropdown @IsMongoIdcreate-admission.dto.ts:37-40
notes — multiline optional string create-admission.dto.ts:42-45
Update form (PATCH /crm/admissions/:id, update-admission.dto.ts = PartialType)
reuses this table; submit blocked client-side when status closed
(server 400: admission.service.ts:257-269).
Note: no admission status field — server always creates submitted
(admission.service.ts:43).
Field Req Control Validation Source
note ✅ multiline @IsStringcreate-follow-up.dto.ts:5-7
scheduledAt ✅ datetime picker @IsDateString (ISO)create-follow-up.dto.ts:9-11
completedAt — toggle + datetime @IsDateString optionalcreate-follow-up.dto.ts:13-16
Display uses followUps[] (lead.schema.ts:24-39); createdBy set server-side
from token (crm.service.ts:111).
Field Req Control Validation Default Source
name ✅ text @IsString— create-campaign.dto.ts:5-8
description — multiline optional — create-campaign.dto.ts:10-13
type — dropdown enum 5 email (campaign.schema.ts:30)create-campaign.dto.ts:15-18
status — dropdown enum 4 draft (campaign.schema.ts:33)create-campaign.dto.ts:20-23
startDate — date picker @IsDateString— create-campaign.dto.ts:25-28
endDate — date picker @IsDateString— create-campaign.dto.ts:30-33
Client: endDate ≥ startDate (no server check today — flagged).
No edit form (no PATCH endpoint) — (planned).
Field Req Control Validation Source
type ✅ dropdown enum tc/marksheet/certificate/photo/other create-admission-document.dto.ts:5-8; admission.schema.ts:27-33
fileId ✅ file pick → storage upload (storage provider; upload endpoint (planned)) @IsStringcreate-admission-document.dto.ts:10-12
filename — auto from file optional string, defaults to fileId server-side (admission.service.ts:107) create-admission-document.dto.ts:14-17
Server appends doc + auto-transitions submitted → documents_pending
(admission.service.ts:112-114).
Field Req Control Validation Default Source
scheduledAt ✅ datetime picker @IsDateString— schedule-interview.dto.ts:11-14
panel — staff multi-pick chips array of @IsMongoId — schedule-interview.dto.ts:16-20
mode — segmented online/offline enum offline (admission.schema.ts:76-78)schedule-interview.dto.ts:22-25
feedback — multiline optional string — schedule-interview.dto.ts:27-30
Field Req Control Validation Source
decision ✅ radio/segmented enum approve/reject/waitlist admission-decision.dto.ts:4-8,10-13
comment — multiline optional string admission-decision.dto.ts:15-18
Offered only from decidable statuses (admission.schema.ts:20-25); server 400
otherwise (admission.service.ts:141-145).
Reject/waitlist warnings per 06 §S9 .
Lead/admission identity fields: given-name, family-name, email, tel
autofill hints; labels linked; group headers (Applicant / Contact / Placement /
Assignment); first-invalid focus; 409 banner in live region.