08 - Form Specifications (Transport Module)
- 1. Vehicle Create Form (
create-vehicle.dto.ts:5-42) - 2. Driver Create Form (
create-driver.dto.ts:4-50) - 3. Route Create Form (
create-route.dto.ts:21-60) - 4. Assign Student to Route (
assign-route.dto.ts:4-26) - 5. Form behaviour (shared)
All create/update/assign forms. Validation mirrors DTOs exactly (
create-vehicle.dto.ts,create-driver.dto.ts,create-route.dto.ts,assign-route.dto.ts; updates are PartialTypes of the creates -update-*.dto.ts). Server is the source of truth; client validates for latency, server for correctness.
Legend: * required. All forms: keyboard-avoidance, autofill where noted,
submit button disabled until valid, loading state on submit.
1. Vehicle Create Form (create-vehicle.dto.ts:5-42)
| Field | Type | Required | Validation (DTO) | Notes |
|---|---|---|---|---|
| plateNumber | text | * | IsString (:7-8) | trim; unique/tenant (vehicle.schema.ts:50); 409 inline (transport.service.ts:38-45) |
| model | text | * | IsString (:11-12) | |
| capacity | number | * | IsNumber @Min(1) (:15-17) | step 1; warn if > 80 (client heuristic) |
| type | segmented | * | IsEnum(VehicleType) (:19-21) | bus / van / car (vehicle.schema.ts:7-11) |
| year | number | - | IsNumber @IsOptional (:24-26) | |
| color | text | - | IsString @IsOptional (:28-31) | |
| insuranceExpiry | date | - | string @IsOptional (:33-36) | note: schema prop declared String, typed Date (vehicle.schema.ts:42-43) - send ISO string |
| notes | multiline | - | IsString @IsOptional (:38-41) |
Submit: POST /transport/vehicles (transport.controller.ts:30-34).
Update: same fields all optional (update-vehicle.dto.ts:4), PATCH /transport/vehicles/:id (transport.controller.ts:48-52).
2. Driver Create Form (create-driver.dto.ts:4-50)
| Field | Type | Required | Validation (DTO) | Notes |
|---|---|---|---|---|
| firstName | text | * | IsString (:6-8) | autofill given-name |
| lastName | text | * | IsString (:10-12) | autofill family-name |
| licenseNumber | text | * | IsString (:14-16) | unique/tenant (driver.schema.ts:50); 409 inline (transport.service.ts:177-184) |
| phone | tel | * | IsString (:18-20) | unique/tenant (driver.schema.ts:51); 409 inline (:185-190) |
| - | IsEmail @IsOptional (:22-24) | autofill email | ||
| licenseExpiry | date | - | IsDateString @IsOptional (:27-29) | parsed to Date server-side (transport.service.ts:193-196) |
| address | text | - | IsString @IsOptional (:31-34) | autofill street-address |
| emergencyContact | text | - | IsString @IsOptional (:36-39) | tel keyboard |
| joinedAt | date | - | IsDateString @IsOptional (:41-44) | parsed to Date (transport.service.ts:192-193) |
| notes | multiline | - | IsString @IsOptional (:46-49) |
Submit: POST /transport/drivers (transport.controller.ts:90-94).
Update: PATCH /transport/drivers/:id (transport.controller.ts:108-112),
update-driver.dto.ts:4.
3. Route Create Form (create-route.dto.ts:21-60)
| Field | Type | Required | Validation (DTO) | Notes |
|---|---|---|---|---|
| name | text | * | IsString (:23-25) | unique/tenant (route.schema.ts:44); 409 inline (transport.service.ts:113-116) |
| startPoint | text | * | IsString (:27-29) | |
| endPoint | text | * | IsString (:31-33) | |
| stops | RouteStopEditor | - | IsArray @ValidateNested({each}) @Type(RouteStopDto) (:34-39) | items {name: IsString, order: IsNumber} (:11-19) |
| vehicleId | ResourcePicker | - | IsString @IsOptional (:41-44) | converted to ObjectId (transport.service.ts:117-119) |
| driverId | ResourcePicker | - | IsString @IsOptional (:46-49) | converted to ObjectId (transport.service.ts:119) |
| estimatedDuration | number | - | IsNumber @IsOptional (:51-54) | minutes |
| notes | multiline | - | IsString @IsOptional (:56-59) |
Submit: POST /transport/routes (transport.controller.ts:60-64).
Update: PATCH /transport/routes/:id (transport.controller.ts:78-82),
update-route.dto.ts:4; ObjectId conversion also on update
(transport.service.ts:159-161).
4. Assign Student to Route (assign-route.dto.ts:4-26)
| Field | Type | Required | Validation (DTO) | Notes |
|---|---|---|---|---|
| routeId | ResourcePicker | * | IsString (:6-8) | |
| studentId | StudentPicker | * | IsString (:10-12) | from students module; filter transportRequired toggle (student.schema.ts:53-54) |
| shift | ShiftPicker | * | IsEnum(['morning','evening','both'] as const) (:13-15) | |
| stopName | text | - | IsString @IsOptional (:17-20) | suggest from route stops |
| notes | multiline | - | IsString @IsOptional (:22-25) |
Submit: POST /transport/assign (transport.controller.ts:120-124).
Server sets assignedAt = now, status default active
(transport.service.ts:259-266, route-assignment.schema.ts:23-28).
409 duplicate (route, student) inline (transport.service.ts:252-258).
5. Form behaviour (shared)
- 409 conflict handling:
ConflictFieldErrorunder the offending field with server message verbatim (07 §8); never silently retry. - Debounce: search-as-you-type fields (student picker) 300 ms; single-flight requests (00-shared/11).
- Dirty tracking: updates send only touched fields (PartialType semantics,
update-*.dto.ts); unchanged optional dates re-sent as-is to avoid accidental clearing (transport.service.ts:159-161, 228-234re-parse only when present). - Offline: submit blocked with
AppOfflineBanner; no queue (no offline mutation contract). - Success: navigate to detail + snackbar; events
VehicleCreated,RouteCreatedfire server-side (transport.service.ts:47-57, 121-128).