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

04 — Information Architecture (Feature Flags Module)

Where the Feature Flags screens live in the shell (00-shared/05). The module owns a Settings sub-section (/settings/feature-flags, admin-only) plus the global gating service that influences every role's surface (rendered elsewhere; owned by FeatureFlagsCubit, see 13). Everything is derived from feature-flags.controller.ts (routes) and the shared shell.


1. Placement in the shell

App
 └─ AppShell (role-aware)
     ├─ NavigationBar / Rail / Drawer  (00-shared/05 §2)
     └─ /settings                       [admin]
         └─ Settings hub
            └─ /settings/feature-flags          Feature Flags (this module)
               ├─ /settings/feature-flags       list (all / by module / enabled)
               ├─ /settings/feature-flags/:key  flag detail
               ├─ /settings/feature-flags/:key/edit  flag editor (full-screen, >3 fields)
               └─ (sheet) bulk update
     └─ global: FeatureFlagsCubit gates routes & widgets across all modules
        (biometric, notifications/channels, payments — (forward-looking) consumers)
  • Settings is an existing top-level destination (00-shared/05 §2: "Settings | /settings | admin"), so the flags section is a settings sub-page — same pattern as Auth's Security hub under /settings (auth/04 §1).
  • The route prefix is module-owned and proposed (no client exists; 00-shared/12 D1/A2): /settings/feature-flags. Alternative (proposed): nest under /settings/integrations once the integrations module lands (blueprint lists it separately, COLLECTIONS.md:805-818).

2. Route table (module-owned; extends 00-shared/05 §4)

RouteScreenAuthPerm (target)Source API
/settings/feature-flagsFlags listJWTfeature-flags.readGET /feature-flags (+?module=)
/settings/feature-flags?tab=enabledEnabled-only listJWTfeature-flags.readGET /feature-flags/enabled
/settings/feature-flags/:keyFlag detailJWTfeature-flags.readGET /feature-flags/:key
/settings/feature-flags/:key/editFlag editorJWTfeature-flags.updatePUT /feature-flags
/settings/feature-flags/bulkBulk update (sheet)JWTfeature-flags.updatePUT /feature-flags/bulk
(menu) deleteJWTfeature-flags.delete (target)DELETE /feature-flags/:key

Guard note: permissions are not enforced server-side today (rbac.guard.ts:29, feature-flags.controller.ts:18) — the routes are spec'd as if they will be (OQ-5).

3. Screen hierarchy

  • Flags list is the hub: rows → detail; module filter chips + enabled tab; FAB / bulk button; row menu (edit, delete).
  • Detail → Edit (full-screen page — 5 fields incl. description; > 3 fields → page per 00-shared/05 §5); Edit returns → Detail (refresh).
  • Bulk update is a bottom sheet over the list (selection state lives in the list cubit).
flowchart TD
    SET[/settings/] --> FF[/settings/feature-flags/]
    FF --> ENABLED[/settings/feature-flags?tab=enabled/]
    FF --> DET[/settings/feature-flags/:key/]
    FF --> BULK(("bulk sheet"))
    DET --> EDT[/settings/feature-flags/:key/edit/]
    EDT --> DET
    DET --> FF
    BULK --> FF
    FF -. gates .-> OTHER[every gated module screen via FeatureFlagsCubit]

4. Modal hierarchy (module)

Following 00-shared/05 §5:

  1. Snackbar/toast — "Feature enabled", "3 of 5 flags updated", "Flag deleted", copy.
  2. Bottom sheet — bulk update (selection + apply), key quick-copy.
  3. Dialog — delete confirm (destructive; typed confirm not required — soft delete is reversible at DB level but re-creation is blocked today, OQ-4 → use standard destructive confirm with the re-create caveat in copy).
  4. Full-screen page — flag editor (5 fields), flags list on phone.
  5. Loader — button spinners + list skeletons; never full-screen dual column.

5. Screen relationships

  • Flags list → detail returns with changed state (toggle flipped) — detail cubit refreshes on return; list refreshes on detail pop (RefreshIndicator + cubit Refresh).
  • Bulk sheet applies → list refetches → per-row result banner.
  • Delete → row removed (server-confirm, no optimistic removal for delete; toggle IS optimistic).
  • Global: whenever FeatureFlagsCubit refreshes (app.resume, pull-to-refresh, TTL), every gated widget rebuilds via BlocBuilder/BlocSelector — no route change needed.

6. Nested nav

  • Tablet/desktop ≥ 840 dp: master-detail — list left pane, detail right (00-shared/04 §6).
  • Phone: push (list → detail → edit).
  • The bulk sheet keeps list selection state in FlagsListCubit (sheet is a view of it).

7. Quick actions / FAB / context menus

  • FAB (extended): "New flag" — opens the flag editor in create mode (upsert with a new key). One FAB per screen (00-shared/03 D).
  • Row AppMenu: Edit, Delete (destructive).
  • Module filter chips + "Enabled only" FilterChip row under the AppBar.
  • Keyboard (desktop): Ctrl+K search n/a (list is short); N new flag; Esc closes sheet; space toggles focused switch (00-shared/08 §2).
PatternTargetNote
studylyon://settings/feature-flagsFlags listadmin
studylyon://settings/feature-flags/:keyFlag detailsupports support-links
studylyon://settings/feature-flags?module=biometricFiltered listprefill filter

9. Accessibility of IA

  • One headlineMedium per screen; list rows ordered: name → module badge → state switch.
  • Focus begins on first filter chip (desktop); switch rows announce state (Semantics(toggled:), 09_Accessibility_Baseline.md §5).
  • Gated screens: hidden content is removed from the semantics tree, never "disabled-but- announced" (screen readers must not read what the tenant can't use).

10. Peripheral: what the module owns vs the shell

  • Shell-owned: Settings hub entry, global 403/404 screens, offline banner.
  • Module-owned: the four flag screens + the bulk sheet + the global gating contract (FeatureFlagsCubit) that other modules consume — gating widgets live in shared, driven by this module's state (13_State_Management.md).