08 - Form Specifications (Houses Module)
- 1. Form map
- 2. Client validation (mirror of DTO)
- 3. Conflict handling (the critical path)
- 4. Update-mode quirk (PATCH semantics)
- 5. Submission flow
- 6. Idempotency note
- 7. a11y & motion
The house form (create + edit share one form, 05 §3 / 06 §3). Field rules are a client mirror of
CreateHouseDto(create-house.dto.ts:4-22). Form behavior conventions from 00-shared/03 and validation patterns from 00-shared/07.
1. Form map
| Field | Widget | Required | DTO rule | Server behavior |
|---|---|---|---|---|
| Name | AppTextField | ✅ | @IsString() (create-house.dto.ts:5-7) | trimmed (house.schema.ts:9-10) |
| Code | AppTextField | ✅ | @IsString() (create-house.dto.ts:9-11) | trimmed (house.schema.ts:12-13); unique per tenant (house.schema.ts:23) |
| Color | ColorPickerField | ❌ | @IsOptional() @IsString() (create-house.dto.ts:13-16) | stored verbatim, any string (house.schema.ts:15-16) |
| Motto | AppTextField (multiline) | ❌ | @IsOptional() @IsString() (create-house.dto.ts:18-21) | stored verbatim (house.schema.ts:18-19) |
2. Client validation (mirror of DTO)
| Field | Client rule | Error copy |
|---|---|---|
| Name | non-empty after trim | "Name is required." |
| Code | non-empty after trim | "Code is required." |
| Color | #RRGGBB or preset token | "Use a hex color like #BA1A1A." |
| Motto | ≤ 120 chars (soft cap, not server-enforced) | - |
- Server returns 400 for DTO violations (class-validator) - client mirrors first;
any server 400 maps to the field by
message/property(00-shared/07 §4).
3. Conflict handling (the critical path)
- Duplicate code on create → 409
House code "X" already exists.(houses.service.ts:19-21). Rendered byConflictFieldErrorunder Code (07 §6), verbatim; focus moves to Code. - Duplicate code on update → same service path? No:
updatedoes not re-check duplicates (houses.service.ts:42-49) - it relies on the unique index{tenantId, code}(house.schema.ts:23). MongoDBE11000surfaces as a generic server error; client must still render 409-style copy from the API error mapping (00-shared/07 §4) - QA item 14 §2.5. - Duplicate name or color → allowed by design (no check, no index); client may warn softly ("Another house uses this color") but must not block.
4. Update-mode quirk (PATCH semantics)
PATCH /houses/:id reuses CreateHouseDto as the body type
(houses.controller.ts:44); name + code are required in the request body even for
edits. The form therefore:
- Prefills all four fields from the loaded house.
- Submits the complete field set on Save (never partial).
- Documents this in code:
// houses: PATCH body = CreateHouseDto (full set), see houses.controller.ts:44.
If the API later gets a true partial DTO this behavior changes - keep it in one
submission mapper (HouseFormCubit.toDto()).
5. Submission flow
HouseFormCubit:
validate() -> fieldErrors
submit() -> create: POST /houses (houses.controller.ts:24-28)
-> update: PATCH /houses/:id (houses.controller.ts:42-46)
409 code -> conflicts['code'] = server message (single-flight, keep fields)
404 -> snackbar "House not found" + close sheet (update race)
success -> pop sheet; list/detail refetch (response-driven, 13 §6)
6. Idempotency note
- Create is not idempotent: double-tap protection via
submittingflag (single-flight inHouseFormCubit,13§3). A retry after a timed-out 201 would hit 409 - recover by "Go to house" (conflict action navigates to detail if the code now exists). - Update is naturally idempotent (
$setfull DTO).
7. a11y & motion
- Labels linked to inputs; error live-region announcements (
00-shared/09). - Submit disabled until required fields non-empty (but keep 409 handling enabled for races).
- Field error shake
m-fast(00-shared/08); focus first invalid field.