Skip to main content

Portal requirements (POR-*)

@enode/portal — the browser-first coach/admin console (responsive, no native shell). Shared behavior (auth flow, i18n, units, error engine) lives in shared.md. See the overview for the ID scheme and status legend. Test cases: portal-system-tests.md.

Areas: AUTH · DASH (dashboard) · PLAN (planner) · DEVICES · USERS · EXER (exercises) · WKT (workout templates) · TAGS · INS (insights) · PROF (profiles/performance) · HIST (history) · MIGR (migration) · EXP (export) · NOTIF (notifications) · RBAC (privilege gating).

Template/description sharing spans WKT, EXER and PLAN — the model behind those rows is in sharing.md.

Auth & entry (POR-AUTH)​

IDRequirementStatusTests
POR-AUTH-01Pro users sign in via the shared flow (SHR-AUTH-01); registration is Pro-only. A One user is blocked by the backend (314, missing canAccessPortal) with a message directing them to the One app — same policy as the Pro app (product decision 2026-07-28; previously One logins were allowed for migration).stablePOR-ST-1.1..1.3, AUTH-LOG-11
POR-AUTH-02Device-limit (310), onboarding (311), and invitation (312) responses are handled with their dedicated dialogs/redirects (SHR-AUTH-06..08).stablePOR-ST-1.6..1.8

Dashboard (POR-DASH)​

IDRequirementStatusTests
POR-DASH-01The dashboard shows time-range-scoped summaries (active athletes, completed sessions, total volume, sets) with trends vs the prior period.stablePOR-ST-2.1, POR-ST-2.2
POR-DASH-02Top-exercise and athlete-activity rankings and a completed-sessions table are shown for the selected range.stablePOR-ST-2.3, POR-ST-2.4

Planner (POR-PLAN)​

Covered in depth by the dedicated Planner manual test protocol.

IDRequirementStatusTests
POR-PLAN-01The planner offers month/week/day calendar views of scheduled and recurring workouts.stablePlanner protocol §1–§5
POR-PLAN-02Workouts can be created, edited, deleted, and rescheduled (including drag & drop); recurring schedules are supported.stablePlanner protocol §6, §8, §9 + POR-ST-3.1
POR-PLAN-03Saving a change to a shared workout plan surfaces the shared-definition conflict dialog (HTTP 313) with fork/update/cancel options. The "edit the shared definition" option is offered only when the user owns the definition or holds write privilege on it.stablePlanner protocol §7
POR-PLAN-04Picking a template the user does not own asks once, before anything is applied to the draft, whether the schedule should follow the original (the author's later edits reach it; it disappears if they stop sharing) or make its own copy (independent of later changes). Cancelling re-opens the picker and leaves the draft untouched.stable
POR-PLAN-09A schedule that follows someone else's template renders its exercises read-only, says so above them naming the author, and offers Copy to edit — taking your own copy is the only way to change the plan, and it is also what stops the schedule following the original. The appointment itself (date, time, recurrence, athletes, the schedule's own name and tags) stays editable while following.stable
POR-PLAN-08The follow control is always a single line; its consequences ("whose later changes reach this appointment", "what your own copy keeps") open from an info icon that starts collapsed every time.stable
POR-PLAN-06Because a followed plan cannot be edited in place, the save-time conflict it used to produce cannot arise from this surface. The HTTP 313 dialog remains the backstop for what the client cannot see (a definition shared with other schedules), and without write privilege it offers only "save as a private copy".stable
POR-PLAN-07The schedule drawer's template picker and the Workouts page's Explore drawer are ONE component (TemplateBrowser), differing only in whether the viewer's own templates are included and in what a row does. Both offer a self-hiding single-select Yours / Internal / Public strip, one collapsible section per author, the shared ranked search, and a preview: the card body selects in one tap, a chevron opens the read-only detail. The picker offers no way to author a template — the schedule form's own "Add exercise" is the lighter path.stable
POR-PLAN-05A calendar chip for a workout planned from someone else's template carries its provenance glyph, and states it in the chip's accessible name.stable

Devices (POR-DEVICES)​

IDRequirementStatusTests
POR-DEVICES-01Connected device sessions (stations) can be listed, searched, and removed.stablePOR-ST-4.1, POR-ST-4.2

Users & roster (POR-USERS)​

IDRequirementStatusTests
POR-USERS-01Users are listed in a searchable table filterable by role (admin/coach/athlete).stablePOR-ST-5.1
POR-USERS-06The signed-in user appears in the roster only when they are trainable — whatever their role, owner included — badged "You" and excluded from bulk selection. Any other owner stays hidden.stablePOR-ST-5.8
POR-USERS-02A user can be created and edited in the user drawer, including athlete metadata; height/weight respect the metric/imperial setting. Editing someone else needs the tier privilege (writeUserAthlete / writeUserTrainer / writeUserAdmin); editing your own row needs writeSelf, whatever your role — which is how an owner, who is no tier, edits themselves. Archive and delete stay tier-only, so neither is offered on your own row.stablePOR-ST-5.2, POR-ST-5.6, POR-ST-5.8
POR-USERS-03Bulk operations: add tags to multiple users; delete multiple users (with confirmation).stablePOR-ST-5.3, POR-ST-5.4
POR-USERS-04A roster CSV can be uploaded through the wizard: dropzone → parse → column auto-mapping → validation → review → asynchronous creation with live progress. Privilege: featureUserBatchUpload.stablePOR-ST-5.5
POR-USERS-05Per-user actions include data export and viewing upcoming workouts.stablePOR-ST-5.7

Exercises (POR-EXER)​

IDRequirementStatusTests
POR-EXER-01The exercise catalogue is searchable by name/tag and filterable by equipment and muscle category; exercises can be favourited.stablePOR-ST-6.1, POR-ST-6.2
POR-EXER-02The exercise detail drawer shows catalogue data and lets the user override name/details with custom values.stablePOR-ST-6.3
POR-EXER-03The detail drawer's Naming card publishes the user's own name for a movement at one of three access levels, and updates it afterwards. Publishing requires a name of the user's own; the card states what each level does before it is picked.stable
POR-EXER-04Other people's published namings for the same movement are listed as selectable cards with their reach and author. Selecting one and confirming stages that name and description into the drawer's own fields — it is a rename of the user's exercise, creating no link to the other user's naming; their own values and history are untouched until they save.stable

Workout templates (POR-WKT)​

IDRequirementStatusTests
POR-WKT-01Workout templates are listed as cards with search and tag filtering; templates can be created and edited in a drawer and bulk-deleted. The page is an early build (in-code note: structural first pass).stablePOR-ST-7.1..7.3
POR-WKT-02The table lists the viewer's OWN templates only; what other people share reaches them through the Explore action, which appears only when something usable is actually shared with them. Search matches the template's name, its tags, the names of the exercises it contains, and the muscles and muscle groups it trains — ranked by how much of the session the searched muscle is.stable
POR-WKT-03The template drawer has an Access level card: a three-level picker (Private / Internal / Public) with a live line stating what the chosen level does, including what un-sharing would cost. It replaces the previous create-time hardcode, so a template's reach can be changed after creation.stable
POR-WKT-04A template the user does not own opens read-only — no Save, Delete, Add or Select — showing its name, tags, access level, a Source row with the author's provenance, and its exercises as inert slot cards.stable
POR-WKT-05Save a copy on a read-only template creates the user's own copy, closes the Explore drawer and swaps onto the normal editor for it. The copy is self-contained: any movement it references that the copier does not own is adopted into their catalogue. If the template stopped being shared in the meantime, the user is told so and the list refreshes.stable
POR-WKT-06The table carries a sortable Access column: the level for an own unshared template, "Shared by you" once it goes out, and "Shared by {author}" on a foreign row. It is the surface that answers "what have I actually shared".stable

Tags (POR-TAGS)​

IDRequirementStatusTests
POR-TAGS-01Tags are listed with access-level filter chips; a tag can be created/edited (color, details) and bulk-deleted; tag relations are shown.stablePOR-ST-8.1..8.3

Insights — custom charts (POR-INS)​

IDRequirementStatusTests
POR-INS-01Users with the writeCustomCharts privilege can build custom charts (5 chart types) on a draggable board; charts persist per user.stablePOR-ST-9.1..9.3
POR-INS-02The builder preview, empty state, and walkthrough render synthetic example charts (educational visuals, not real data).mocked— (known behaviour)

Profiles — performance matrix (POR-PROF)​

IDRequirementStatusTests
POR-PROF-01The Profiles page shows a two-axis athlete × exercise performance matrix with list/grid views (persisted preference), pair detail, and per-athlete trend.stablePOR-ST-10.1..10.3

History & session details (POR-HIST)​

IDRequirementStatusTests
POR-HIST-01Completed sessions can be searched with filters and paginated; the default window is the last year.stablePOR-ST-11.1, POR-ST-11.2
POR-HIST-02A session opens in a drawer with a summary tab and technique board.stablePOR-ST-11.3
POR-HIST-03The session summary's zone-distribution and muscle-map cards render from the V2 history-stats DTO; marked WIP in code, zone card limited to conventional-strength exercises.wip— (known behaviour)
POR-HIST-04A dedicated date-range picker is planned for a later milestone (dev-only all-time toggle exists).wip—
POR-HIST-05An Archived filter splits the sessions table into disjoint compartments: by default sessions from archived athletes are hidden; the filter shows only those (SHR-ARCH-03). Like the page's other facets it narrows the loaded page only, so the counts are page-scoped.stablePOR-ST-17.2

Migration (POR-MIGR)​

IDRequirementStatusTests
POR-MIGR-01A wizard migrates athletes, groups, and history from enode Pro; reachable as a route and as a sidebar drawer. Privilege: featureMigration.stablePOR-ST-12.1, POR-ST-12.2
POR-MIGR-02Exercise mapping during migration is chosen manually by the coach (auto-suggestion intentionally stubbed — backend returns no targets by design).stablePOR-ST-12.3

Export (POR-EXP)​

IDRequirementStatusTests
POR-EXP-01The export flow produces data exports (including PDF); reachable as a route (deep-linkable via URL params) and as a sidebar drawer. Privilege: featureDataExport.stablePOR-ST-13.1, POR-ST-13.2

Notifications (POR-NOTIF)​

IDRequirementStatusTests
POR-NOTIF-01Notifications arrive live (stream + arrival peek + bell unread badge) and are listed in an inbox with filter segments, mark-read, delete, and deep-link expansion.stablePOR-ST-14.1..14.3
POR-NOTIF-02Long-running roster uploads report progress live (stream host + peek).stablePOR-ST-5.5

Privileges & roles (POR-RBAC)​

IDRequirementStatusTests
POR-RBAC-01Navigation entries and routes are gated by privileges (useHasPrivilege): Users pages, Insights, roster upload, migration, and export are hidden/blocked without the corresponding privilege.stablePOR-ST-15.1, POR-ST-15.2
POR-RBAC-02The role model is owner → administrator ("Facility") → coach → athlete; the owner is an all-access singleton excluded from role filters and from other users' rosters (see POR-USERS-06 for the signed-in user's own row).stablePOR-ST-15.3

Dev-only surfaces​

/dashboard/debug, /dashboard/debug/wizard, and /dashboard/debug/benchmark plus the mock-data harness render only in dev builds (render-time guards; all mock setters no-op in production). Not release-tested beyond confirming they are absent in production builds (POR-ST-16.5).