Every screen in the module. Data contracts derive from organizations.controller.ts,
settings.controller.ts, feature-flags.controller.ts, DTOs, and schemas. States follow
00-shared/06 §3.1 (initial/loading/success/error) + empty + offline + permission.
Analytics events are (proposed) per 10_QA_Baseline.md §8 ({module}.{screen}.{action}).
Attribute Value
Route /organization
Purpose Read-only institution identity summary + entry to all org actions
Entry Drawer/rail "Organization"; post-login for admins; back-target of edit/branding/settings
Exit Edit/Branding/Settings/Flags/Members; back → shell home
Permission any organization.* or settings.* read
Layout (phone / tablet+): AppBar "Organization" → hero header (logo AppAvatar, name, status chip, plan chip) → AppBanner if status === onboarding → section cards:
Section Cards/rows (from organization.schema.ts) Widgets
Identity name, slug, domain, logoFileId AppCard + AppInfoRow
Contact contact.email, contact.phone, contact.website (organization.schema.ts:53-58) AppInfoRow
Address street, city, state, country, zip (:60-67) AppInfoRow
Localization timezone (default UTC), currency (default USD) (:69-73) AppInfoRow
Academic year academicYear.startDate, endDate, month (:75-80) AppInfoRow
Subscription plan (free/basic/premium/enterprise), status (active/inactive/suspended/trial) (:82-94) AppBadge plan + status
Settings summary attendance workingDays, theme primaryColor (link → tabs) AppListTile → /organization/settings
Feature flags count count of enabled keys (link → flags) AppListTile → /organization/feature-flags
Actions (top bar): Edit (organization.update), Branding shortcut.
States: loading → AppSkeleton list; error → AppErrorState + retry; empty/unknown → treated as 404 "organization not found"; offline → last-good cache + AppOfflineBanner.
Pull-to-refresh: re-fetch org (bypass cache).
Keyboard/tablet/desktop: no inputs; tablet = 2-column info grid; desktop = wider grid + breadcrumb.
A11y: one headlineMedium; status chips = icon + label (never color-only, 09 §9 ); hero logo Semantics name.
Analytics (proposed): organizations.overview.view, organizations.overview.edit.tap, organizations.overview.branding.tap.
Data: GET /api/v1/organizations/:id — id resolution: OQ-1 (no self endpoint; (planned) /me; stopgap: lookup via list q=<slug>).
Attribute Value
Route /organization/edit
Purpose Edit name, slug, domain, contact, address, timezone, currency, academic year, subscription (read-only for non-billing roles)
Entry Overview "Edit"; Exit: back (unsaved guard) / save → overview
Permission organization.update
Sections (form groups, full screen — >3 fields per 00-shared/05 §5 ):
Identity: name, slug, domain (update-organization.dto.ts:62-75); 2. Contact: email/phone/website (:85-87); 3. Address: street/city/state/country/zip (:89-94); 4. Localization: timezone (searchable sheet), currency (sheet); 5. Academic year: startDate/endDate (date pickers), month (number); 6. Subscription: plan + status dropdowns (org_admin) — read-only for custom roles without billing perms; (proposed) since no dedicated billing permission exists; 7. Branding link-out.
Widgets: AppTextField, AppDropdown, AppDatePicker, AppSearchBar (timezone/currency picker sheets).
States: load skeleton; save spinner on primary CTA "Save changes"; 409 → inline field conflict; 400 → field errors; offline → blocked; permission → CTA hidden.
Animations: section entrance stagger m-entrance (40 ms); unsaved guard dialog m-base.
Analytics (proposed): organizations.edit.save.success|error, organizations.edit.slug.conflict.
Attribute Value
Route /organization/branding
Purpose Set branding {primaryColor, secondaryColor, logo, favicon} (organization.schema.ts:114-120; DTO update-organization.dto.ts:125-128)
Entry Overview → Branding; Exit: save → overview
Permission organization.update
Widgets: live preview panel (theme card rendering current colors, (proposed) — applies primary override per 02_Design_Tokens.md §1 ), color picker (AppColorPicker — custom, (proposed)), logo upload (AppAttachmentTile upload → logoFileId, storage (planned) per IMPLEMENTATION_PLAN.md:24-34), favicon upload (optional).
States: upload progress per file; save spinner; error on upload → retry tile; offline → pick colors locally, upload blocked.
A11y: color picker also exposes hex text field (color-blind safe, 09 §9 ); preview Semantics summary.
Analytics (proposed): organizations.branding.preview, organizations.branding.save.
Attribute Value
Route /organization/settings
Purpose Edit embedded settings (attendance/academic/theme) + glance at standalone settings collection groups
Entry Overview → Settings; Exit: back
Permission organization.settings.update (save); settings.read for read-only tabs
Tabs (from update-organization-settings.dto.ts:4-30 + setting.schema.ts:7-14):
Tab Fields Source
General (standalone collection group general) setting.schema.ts:14
Attendance gracePeriod, lateThreshold, halfDayThreshold (minutes), workingDays[] (0–6) update-organization-settings.dto.ts:8-12
Academic gradingScale, passingPercentage :18-21
Grading (standalone group grading) setting.schema.ts:10
Notification (standalone group notification) :11
Theme primaryColor, secondaryColor :26-29
Widgets: AppTabs keep-alive; numeric AppTextField (number keyboard, (proposed) bounds: thresholds 0–300, workingDays 0–6 with day-chip multi-select AppChips, passingPercentage 0–100); hex color fields.
Save model: one global "Save all" → PATCH :id/settings with full merged object (full-replace semantics — organizations.service.ts:134); per-tab autosave is forbidden (would wipe siblings).
States: per-tab loading skeleton; dirty-indicator per tab; save spinner; success snackbar; 400 field errors.
Analytics (proposed): organizations.settings.tab.view, organizations.settings.save.
Attribute Value
Route /organization/feature-flags
Purpose View + toggle tenant feature map
Entry Overview → Feature flags; Exit: back
Permission feature-flags.read (view), feature-flags.update (toggle)
Content: merged view of (a) org map metadata.featureFlags (GET :id/feature-flags → Record<string,boolean>, organizations.service.ts:139-142) and (b) catalog GET /api/v1/feature-flags (feature-flags.controller.ts:23-28) for labels/descriptions/module. Rows grouped by module with collapsible sections; row = AppListTile + AppSwitch (optimistic).
Actions: toggle → optimistic update, then PATCH :id/feature-flags full map (organizations.service.ts:144-154); rollback + snackbar on error. Delete flag (standalone) → DELETE /feature-flags/:key (feature-flags.controller.ts:54-58).
Empty state: AppEmptyState "No feature flags configured" (both lists empty).
Analytics (proposed): organizations.flags.toggle.{key}.
Attribute Value
Route /organization/members
Purpose Add an organization admin (cross-module: user + membership)
Entry Overview → Members; Exit: back
Permission user.create + rbac.member.create
Content: member list (GET /api/v1/rbac/members, rbac.controller.ts:57-61) + invite form: firstName/lastName/email/phone/roles (user.create + rbac.member.create — rbac.controller.ts:63-67, add-member.dto roles). Row menu: change roles (PATCH /rbac/members/:id, rbac.controller.ts:69-73), remove (DELETE /rbac/members/:id, :75-79).
Note: exact user-creation fields follow the Users module contract (users.controller.ts:36-40) — see Users module docs. No invitation-token flow exists (OQ-2).
States: list skeleton; invite spinner; 409 duplicate email; empty "No members yet".
Analytics (proposed): organizations.members.invite, organizations.members.role.change.
Attribute Value
Route /admin/tenants
Purpose Browse all tenants (paginated)
Entry Platform drawer; Exit: back
Permission isPlatformAdmin only (OQ-4 — server guard (planned))
Content: AppSearchBar (q → name/slug regex, organizations.service.ts:78-83), sort dropdown (sort default -createdAt — :85-87; name/-name/createdAt), paginated list (page,limit default 20, pagination-query.dto.ts:5-30), infinite scroll + hasNext (buildPaginationMeta — :41-54), pull-to-refresh.
Row: logo, name, slug, status chip, plan badge, created date. Context menu: View · Edit · Suspend · Delete.
States: skeleton; empty ("No tenants match"); error+retry; offline cache + banner.
Filters (proposed): status filter chip row (organization.schema.ts:20-25).
Tablet/desktop: master-detail — tapping row loads detail in right pane (00-shared/05 §3 ).
Analytics (proposed): organizations.tenants.list.view, organizations.tenants.search.
Attribute Value
Route /admin/tenants/:id
Purpose Platform view of one tenant: identity, subscription, status actions, delete
Entry Tenants list row; Exit: back (pane)
Permission isPlatformAdmin
Content: same identity/subscription cards as S1 (id-driven), plus status switcher (PATCH :id status — update-organization.dto.ts:130-133), plan/status edit, typed-confirm delete (organizations.service.ts:156-159), link-outs to Settings/Branding/Flags (reusing S3/S4/S5 with :id).
Tenant create (/admin/tenants/new): full-screen form = CreateOrganizationDto fields (create-organization.dto.ts:57-112) — name required, optional slug/domain/contact/address/timezone/currency/plan/branding/metadata; success → detail with slug handoff banner.
States/analytics: as S7 + organizations.tenants.create.success, organizations.tenants.delete.confirmed.
Screen Primary endpoint Load pattern Key analytics (proposed)
S1 Overview GET /organizations/:idcache+revalidate (TTL 5 min, volatile) organizations.overview.view
S2 Edit GET + PATCH /organizations/:idno client cache (form) organizations.edit.save.*
S3 Branding GET + PATCH /organizations/:id (+ storage upload (planned))no client cache organizations.branding.save
S4 Settings GET :id/settings + PATCH :id/settingsno client cache organizations.settings.save
S5 Flags GET :id/feature-flags + GET /feature-flags + PATCH :id/feature-flags5-min cache organizations.flags.toggle.*
S6 Members GET/POST /rbac/members, users endpoints5-min cache organizations.members.invite
S7 Tenants GET /organizations (page/limit/sort/q)paginated cache sl:{module}:{query} organizations.tenants.list.view
S8 Tenant detail GET/PATCH/DELETE /organizations/:idno client cache organizations.tenants.delete.confirmed