Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 — Form Specifications (Staff Module)

Field-by-field specs for every create/update form. DTO columns cite the exact validator source. Backend note: all three modules validate via class-validator; failures return 400 VALIDATION_ERROR with error.details[] (http-exception.filter.ts:28,103-107), mapped to field errors by the client (00-shared/06 §5).

F1 — Create Staff (/staff/new) → POST /staff

Source: create-staff.dto.ts:4-38; staff.controller.ts:24-25.

#FieldControlRequiredValidation (source)Server behavior
1userIdUser picker (sheet, searchable)Yes@IsMongoId() (create-staff.dto.ts:5-7)Stored as ref to User (staff.schema.ts:23-24)
2employeeNumberAppTextField, mono, maxLength 64 (proposed)Yes@IsString() (create-staff.dto.ts:9-11)Trimmed (staff.schema.ts:26-27); unique per tenant — 409 on dup (staff.service.ts:31-36, staff.schema.ts:57)
3departmentIdCatalogPickerSheet (departments)No@IsOptional() @IsMongoId() (create-staff.dto.ts:13-16)Ref to Department (staff.schema.ts:29-30)
4designationIdCatalogPickerSheet (designations)No@IsOptional() @IsMongoId() (create-staff.dto.ts:18-21)Ref to Designation (staff.schema.ts:32-33)
5employmentTypeAppDropdown: full_time, part_time, contract, internNo@IsOptional() @IsString() (create-staff.dto.ts:23-28) — no @IsEnum; enum is documentation only (staff.schema.ts:14-19)Defaults to full_time when absent (staff.service.ts:40-41)
6joiningDateAppDatePickerNo@IsOptional() @IsDateString() (create-staff.dto.ts:30-33)Converted to Date (staff.service.ts:42)
7salaryGradeAppTextField (free text)No@IsOptional() @IsString() (create-staff.dto.ts:35-38)Trimmed; reference only, not payroll (staff.schema.ts:45-46, Staff.md:60)
  • Not in this form: status (always active on create — staff.service.ts:39) and metadata (update-only, update-staff.dto.ts:40-42).
  • Helper texts: "Created as active"; "Defaults to full-time".
  • Submit: button loading: while pending, anti-double-submit (00-shared/08 §6); success → snackbar + navigate to detail (SS2).

F2 — Edit Staff (/staff/:id/edit) → PATCH /staff/:id

Source: update-staff.dto.ts:4-42; all fields optional; prefilled from GET /staff/:id. Send only changed fields (staff.service.ts:78-91changes = audit delta, line 88).

#FieldControlRequiredValidation (source)Notes
1employeeNumberAppTextFieldNo@IsOptional() @IsString() (update-staff.dto.ts:5-8)409 on duplicate (staff.service.ts:31-36 shared check only on create — rename dup-check caveat OQ-9)
2departmentIdCatalogPickerSheetNo@IsOptional() @IsMongoId() (lines 10-13)Clearing sends null? — see OQ-11
3designationIdCatalogPickerSheetNo@IsOptional() @IsMongoId() (lines 15-18)same
4employmentTypeAppDropdown (4 values)No@IsOptional() @IsString() (lines 20-23)no enum validation
5joiningDateAppDatePickerNo@IsOptional() @IsDateString() (lines 25-28)
6salaryGradeAppTextFieldNo@IsOptional() @IsString() (lines 30-33)
7statusAppDropdown: active, inactive, on_leave, terminatedNo@IsOptional() @IsString() (lines 35-38)lifecycle enum staff.schema.ts:7-12; the sanctioned way to change lifecycle state
8metadataMetadataEditor (key/value)No@IsOptional() (lines 40-42)free-form object (staff.schema.ts:51-52)

F3 — Create Department (/departments/new) → POST /departments

Source: department.dto.ts:4-18; department.controller.ts:26-27.

#FieldControlRequiredValidation (source)Server behavior
1nameAppTextFieldYes@IsString() (department.dto.ts:5-7)Trimmed (department.schema.ts:9-10); unique per tenant — 409 on dup (department.service.ts:23-25, department.schema.ts:24)
2codeAppTextField (short, uppercase hint)No@IsOptional() @IsString() (department.dto.ts:9-12)Trimmed (department.schema.ts:12-13)
3headIdHeadPicker (active staff)No@IsOptional() @IsMongoId() (department.dto.ts:14-17)Ref to Staff (department.schema.ts:15-16)
  • Server sets status: 'active' on create (department.service.ts:26-29).
  • Success → snackbar + navigate to detail (SS7); 409 inline on name.

F4 — Edit Department (/departments/:id/edit) → PATCH /departments/:id

Source: department.dto.ts:20-39; all optional.

#FieldControlValidation (source)Notes
1nameAppTextField@IsOptional() @IsString() (lines 22-24)409 on duplicate name (department.service.ts:23-25 — check runs on create; rename guard applies, verify OQ-9)
2codeAppTextField@IsOptional() @IsString() (lines 26-29)
3headIdHeadPicker@IsOptional() @IsMongoId() (lines 31-34)unset → clear
4statusAppDropdown (active/other)@IsOptional() @IsString() (lines 36-39)catalog soft-state; no enum (department.schema.ts:18-19 is a plain string)

F5 — Create Designation (/designations/new) → POST /designations

Source: designation.dto.ts:4-18; designation.controller.ts:26-27.

#FieldControlRequiredValidation (source)Server behavior
1departmentIdCatalogPickerSheet (departments)No@IsOptional() @IsMongoId() (designation.dto.ts:5-8)Ref to Department (designation.schema.ts:9-10) — reference only, not enforced (OQ-3)
2nameAppTextFieldYes@IsString() (designation.dto.ts:10-12)Trimmed (designation.schema.ts:12-13); unique per tenant — 409 (designation.service.ts:25-28, designation.schema.ts:24)
3levelAppTextField (number) or AppStepperNo@IsOptional() @IsInt() (designation.dto.ts:14-17)Defaults to 0 (designation.schema.ts:15-16); non-int → 400 details
  • Server sets status: 'active' on create (designation.service.ts:29-32).

F6 — Edit Designation (/designations/:id/edit) → PATCH /designations/:id

Source: designation.dto.ts:20-39; all optional: departmentId (22-24), name (26-29), level (@IsInt(), 31-34), status (36-39).


Cross-form rules

  • Errors: first invalid field receives focus on submit; errors clear on edit; error announced via Semantics (00-shared/09 §10, 00-shared/03 AppTextField).
  • 409 DUPLICATE_RESOURCE → inline conflict message + suggest search/refresh (00-shared/06 §5).
  • 404 on save (concurrent deletion) → not-found state, do not resubmit.
  • 429 → "Too many requests — retry in Ns" + backoff, no auto-retry (00-shared/07 §4).
  • Autofill hints on employeeNumber (autofillHints: 'organization-identifier' (proposed)); userId picker searchable.
  • Haptics: success lightImpact, error mediumImpact (00-shared/08 §3).