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 (Organizations Module)

Field-by-field form specs. Server rules are exact from DTOs (create-organization.dto.ts, update-organization.dto.ts, update-organization-settings.dto.ts, register.dto.ts, update-setting.dto.ts, update-feature-flag.dto.ts). Rules marked (proposed) are client-side only — the backend imposes no min/max/length on those fields. Server ValidationPipe: whitelist + forbidNonWhitelisted + transform (main.ts:50-57).


F1 — Create tenant (S8 create, CreateOrganizationDto)

FieldLabelType/KeyboardValidation (server)Client rules (proposed)Default
nameInstitution nametextrequired @IsString (create-organization.dto.ts:59-60)required, 2–120 chars, trimmed
slugTenant slugtext (mono)optional @IsString (:63-65)^[a-z0-9]+(?:-[a-z0-9]+)*$; live SlugPreview of auto-valueauto from name
domainDomaintext (mono)optional @IsString (:68-70)lowercase FQDN; unique platform-wide (409)
contact.emailContact emailemail@IsEmail (:16-17)valid email
contact.phoneContact phonephone@IsString (:20-21)7–20 digits/+/spaces
contact.websiteWebsiteurl@IsString (:24-25)http(s) URL
address.*street/city/state/country/ziptext@IsString each (:35-54)free text, 120 max
timezoneTimezonesheet picker (searchable)@IsString (:87-89)IANA name from pickerUTC (organization.schema.ts:69-70)
currencyCurrencysheet picker@IsString (:92-94)ISO code from pickerUSD (:72-73)
subscriptionPlanPlandropdown@IsEnum(SubscriptionPlan) (:100-102)enum free/basic/premium/enterprisefree
brandingBranding(link to S3)@IsObject (:106-107)deferred
metadataMetadataJSON textarea (proposed)@IsObject (:110-111)valid JSON object

Submission: CTA "Create tenant"; on success → tenant detail + slug handoff banner. Duplicate email? N/A (contact email not unique). Slug/domain conflicts → 409 inline.

F2 — Edit org profile (S2, UpdateOrganizationDto)

FieldLabelRulesNotes
nameInstitution name@IsString optional (update-organization.dto.ts:63-65)2–120 (proposed)
slugTenant slugoptional string (:68-70)changing = identity change → confirm dialog; conflict 409 (organizations.service.ts:107-113)
domainDomainoptional string (:73-75)conflict 409 if owned by other org (:115-122)
logoFileIdLogooptional string (:79-80)set via upload flow (storage (planned))
contact.*Contactoptional @IsEmail/@IsString (:85-87)email validated
address.*Addressoptional strings (:89-94)free text
timezone / currencyLocalizationoptional strings (:97-104)pickers
academicYear.startDate / endDateAcademic yearoptional Date (:109-111)IsDate via implicit transform; start < end (proposed)
academicYear.monthYear start monthoptional number (:112)1–12 (proposed)
subscriptionPlan / subscriptionStatusSubscriptionenums (:115-123)dropdowns; read-only for non-billing (proposed)
brandingBranding@IsObject (:126-128)see F3
statusStatus@IsEnum(OrganizationStatus) (:130-133)platform surface only (S8)
metadataMetadata@IsObject (:137-138)JSON

Submission: "Save changes" → PATCH :id; partial update allowed (PATCH semantics, $set dtoorganizations.service.ts:124); version increments (organizations.repository.ts:55).

F3 — Branding (S3)

FieldLabelRulesNotes
primaryColorPrimary colorhex /^#?[0-9A-Fa-f]{6}$/ (proposed)server: unvalidated Record<string,unknown> (update-organization.dto.ts:126-128); theme override per 02_Design_Tokens.md §1
secondaryColorSecondary colorhex (proposed)same
logoLogofile upload → id (storage (planned))organization.schema.ts:118; preview tile
faviconFaviconfile upload (optional):119

Submission: whole branding object → PATCH :id. Contrast check ≥4.5:1 vs white for primary as CTA bg (proposed) warning.

F4 — Organization settings (S4, UpdateOrganizationSettingsDto)

FieldLabelTypeServer ruleClient (proposed)Default
attendance.gracePeriodGrace period (min)number@IsObject container only (update-organization-settings.dto.ts:8-12)0–300, integer
attendance.lateThresholdLate threshold (min)numbersame0–300
attendance.halfDayThresholdHalf-day threshold (min)numbersame0–300; > lateThreshold hint
attendance.workingDaysWorking daysday chips (int 0–6)number[]≥1 selected; values 0–6
academic.gradingScaleGrading scaletext:18-21presets + free text
academic.passingPercentagePassing %numbersame0–100
theme.primaryColor / secondaryColorTheme colorshex:26-29same as F3

Submission — CRITICAL full-replace: PATCH :id/settings sends the complete merged object {attendance, academic, theme} — partial bodies wipe sibling groups (organizations.service.ts:134). Dirty tracking per tab; one global save.

F5 — Register (first admin) — cross-module (Auth)

FieldLabelTypeServer rule (exact)
firstNameFirst nametext@IsString (register.dto.ts:6-8)
lastNameLast nametext@IsString (:9-11)
emailEmailemail@IsEmail (:13-15); 409 if used (auth.service.ts:56-58)
passwordPasswordpassword@IsString + @MinLength(8) (:17-20); Argon2id hashed (auth.service.ts:71)
phonePhonephoneoptional @IsString (:22-25)
tenantIdTenant slugtext (mono)@IsString required (:27-29); no org-existence check (OQ-2)

Submission: rate 5/min (auth.controller.ts:31); success returns tokens directly (no redirect); autofill hints name, email, new-password (A §10).

F6 — Settings collection upsert (module config) — cross-module

UpdateSettingDto: key (required), value (required, any), group enum academic/attendance/grading/notification/theme/general (setting.schema.ts:7-14), label, description. PUT /settings upsert (settings.controller.ts:37-41); bulk via PUT /settings/bulk (:43-47). Used by S4 reference tabs (read) — write surface owned by feature modules (cross-module).

F7 — Feature flag (standalone catalog)

UpdateFeatureFlagDto: key (required string), enabled (required boolean — update-feature-flag.dto.ts:9-11), label/description/module (optional). PUT /feature-flags upsert (feature-flags.controller.ts:42-46), bulk :48-52, delete :54-58. Upsert only writes enabled + label (feature-flag.repository.ts:32-43).


Cross-form rules

  • Validation timing: on submit + after first error on change; error clears on edit (00-shared/03 AppTextField).
  • Submit loading: spinner in CTA, anti-double-submit; success haptic lightImpact, error mediumImpact (M §3).
  • Offline: forms render from last-good cache (S2/S4), submit disabled + banner; drafts kept in-memory (no offline queue — 00-shared/07 §10).
  • Conflict UX: 409 → inline on the specific field (slug/domain/email), scroll-to-field, focus.
  • Server errors: 400 details mapped per field (http-exception.filter.ts:103-107); unknown fields → "Unexpected field" (forbidNonWhitelisted).