Every screen of the Parents module: intent, route, composition, states, permissions,
platform behavior, events. Authoritative components in 00-shared/03 ; module-specific
components in 07_Component_Library.md .
Legend — States: idle / loading / success / empty / error(offline, 4xx, 5xx) /
permission. Analytics events {module}.{screen}.{action} (proposed; SDK open —
00-shared/10 §8 ).
Field Detail
Purpose Paginated directory of guardian profiles
Source GET /parents → data: ParentDocument[], meta:{page,limit,totalItems,totalPages,hasNext,hasPrevious} (parent.service.ts:55-67, pagination-query.dto.ts:32-39)
Entry / Exit shell → list; → detail, → /parents/new
Widgets AppSearchBar — server ignores q (parent.service.ts:58-61) → treat as client-side filter or omit (OQ-7); AppListTile rows (identity from joined User), AppSkeleton(list), AppEmptyState ("No guardians yet"), AppErrorState, RefreshIndicator
FAB "Add guardian" (extended, Icons.person_add) → /parents/new
Pagination infinite scroll via meta.hasNext (00-shared/06 §3.2 ); no sort param honored (OQ-7)
Row actions AppMenu: Open, Edit
States loading skeleton; empty; error 5xx generic + requestId; offline last-good cache + banner
Analytics parents.list.{view,search,open,create_tap}
A11y row semantics "Guardian , linked children N"; list position announced
Adaptive phone single column; tablet 2-column; desktop master-detail ≥ 840 dp
Field Detail
Purpose Full guardian profile + all linked children with per-link metadata
Source GET /parents/:id (404 "Parent not found." parent.service.ts:51) + GET /parents/:id/students → raw link docs (parent.service.ts:69-72)
Entry / Exit list → detail; → edit; → link sheet
Composition Header: AppAvatar (from User.avatarFileId/name), display name (join users), chips (occupation, company); sections: Emergency & pickup (profile flags), Linked children (per child: relationship chip, primary badge, pickup/financial/priority line, AppMenu: Unlink, Set primary)
Join note Link docs expose only studentId/parentId ObjectIds (student-parent-link.schema.ts:18-25) → client joins GET /students/:id per child (OQ-10)
404 state AppErrorState "Guardian not found" + back
Pull-to-refresh yes
Analytics parents.detail.{view,edit_tap,link_tap,unlink_tap}
Field Detail
Purpose Create or update a guardian profile
Source POST /parents (create-parent.dto.ts), PATCH /parents/:id (update-parent.dto.ts)
Create-only field userId (required @IsMongoId, create-parent.dto.ts:5-7) — picker over GET /users?q= (users.service.ts:90-115); not editable after create (update-parent.dto.ts has no userId)
Fields occupation, company, annualIncome (number), relationshipNotes, emergencyContactPriority (number), pickupAuthorization (switch, default false parent.schema.ts:28)
Duplicate 409 → inline banner + "open existing profile" (parent.service.ts:32-34)
States idle/loading/error/saving; 400 field details mapped
Analytics parents.form.{open,submit,success,duplicate,error}
Field Detail
Purpose Create one student_parent_links row
Source POST /parents/link/:studentId (link-parent.dto.ts, student-parent-link.service.ts:16-32)
Composition student picker (search by admission number/name) or preselected; AppDropdown relationship (mother/father/guardian/grandparent/relative/foster_parent — enum student-parent-link.schema.ts:7-14); AppSwitch isPrimaryGuardian; AppSwitch pickupAllowed (default true , student-parent-link.schema.ts:33-34); AppSwitch financialResponsibility; number field emergencyPriority
Warnings "Already linked" if the pair exists in loaded links (client pre-check, OQ-2); "This student already has a primary guardian — replace?" (OQ-5)
Errors 404 student (only when server validates — today it doesn't, OQ-3); 400 validation
Analytics parents.link.{open,submit,success,warn_duplicate}
Field Detail
Purpose Confirm removing a parent–student relationship
Source DELETE /parents/link/:linkId → 404 "Link not found." (student-parent-link.service.ts:38-41)
Copy "Unlink from ? The guardian profile and other links are kept." (04-Modules/Parents.md:58)
Primary case If link isPrimaryGuardian → warn "This child will have no primary guardian." (OQ-4)
Behavior destructive-style confirm (error CTA, heavyImpact); server-first (no optimistic)
Field Detail
Purpose Parent home: enumerate own linked children only (privacy boundary USER_PERSONAS.md:48-53)
Source No endpoint today (OQ-1): needs (planned) GET /parents/me (or userId filter) + links; then GET /students/:id join per child
Composition ChildSwitcherBar (avatars row), selected child summary card (name, class/grade via student.schema.ts:33-39), entry points to attendance/results/fees (other modules, student.read)
Empty AppEmptyState "No linked children — contact the school office"
Offline cached last-good children list
Field Detail
Purpose Multi-child households switch context without leaving the screen
State ChildSwitcherCubit (selectedChildId persisted in memory; 13_State_Management.md §3 )
Behavior horizontal avatar chips, selected = primaryContainer; all child-context modules read selected child
A11y each chip: "Child: , selected"; Semantics(toggled:)
Field Detail
Purpose Parent edits own guardian flags (occupation, emergency priority, pickup)
Source GET /parents/:id + PATCH /parents/:id (self-owned id resolved via (planned) my-profile endpoint)
Read-only relationship/primary/pickup-per-child (link-level, no link PATCH endpoint — OQ-4); identity fields (from users)
AppListTile, AppCard, AppAvatar, AppChips, AppBadge, AppSwitch, AppDropdown,
AppTextField, AppSearchBar, AppDialog, AppBottomSheet, AppMenu, AppSkeleton,
AppEmptyState, AppErrorState, AppOfflineBanner, AppFAB, AppSnackbar,
AppSectionHeader, AppInfoRow. Module-specific: ParentListTile, GuardianCard,
LinkedChildCard, RelationshipChip, PrimaryGuardianBadge, ChildSwitcherBar
(07_Component_Library.md ).
parents.list.{view,search,open,create_tap}, parents.detail.{view,edit_tap,link_tap,unlink_tap},
parents.form.{open,submit,success,duplicate,error}, parents.link.{open,submit,success,warn_duplicate},
parents.unlink.{confirm,success,404}, parents.my.children.{view,switch_child} (all proposed).