Drive UI create/manage/edit affordances off the permission matrix (web + Android) #61

Closed
opened 2026-08-14 11:50:24 +00:00 by nalum · 1 comment
Owner

Summary

Make every create, manage and edit affordance in the clients honor the permission matrix, instead of a coarse hand-written role flag. Consume the matrix the server now exposes (SystemService.GetPermissionMatrix, #69) so the UI shows exactly the controls a role is permitted — no more, no less. This also surfaces event editing, which the UI does not offer at all today.

Why

The web gates create and manage controls on one flag, canEdit = ADMIN || MEMBER (web/src/auth/auth.tsx). It excludes the child role wholesale, so a child sees no create controls even for events and jobs the server already permits, and — after #67 — none for the lists and meals a child may now run. Android gates on a per-membership manager flag with the same kind of drift. Both are client-side guesses that diverge from the server's real matrix.

#69 exposes the matrix over RPC. This issue makes the clients consume it.

Depends on

  • #69 — SystemService.GetPermissionMatrix (the data source).
  • #67 — the child list/meal rights this surfaces.

Scope

Web foundation (web/src/auth/):

  • Add a permission store that fetches GetPermissionMatrix once per session, caches it in localStorage (static, non-sensitive), and hydrates synchronously to avoid affordance flicker.
  • Expose can(service, method) on the auth context — true when any of the member's roles appears in that matrix entry. An unknown entry denies, matching the server default.
  • Remove canEdit. Migrate every use to the specific can(...) it needs.

Web per-page migration:

  • Lists and ListDetail create/rename/delete/uncheck: can("ItemListService", …).
  • Meals library (add and edit a meal): can("MealService", …).
  • Meals rota board (set cook, zone, overrides): can("MealRotaService", …). This splits a control today conflated under canEdit, so a child sees the library controls but not the rota controls, per ADR-0028.
  • Jobs add: can("ItemService", "Create").
  • Calendar add: can("EventService", "Create").
  • Rewards create and manage: can("RewardService", …).
  • Leave the per-item ownership checks alone (canEditJob = isManager || owner === me).

Event editing (the capability that does not exist yet):

  • Wire an edit flow on the event detail sheet, reusing the job-edit pattern: tap to open, an Edit action opens the event form prefilled, save calls mutate.events.update. EventService.Update already exists server-side.
  • Gate the Edit affordance on can("EventService", "Update").
  • Decide the occurrence-versus-series rule (SplitOccurrence then Update for one occurrence; Update on the parent for the series). A single non-recurring event edits in place.

Android:

  • Consume the same GetPermissionMatrix endpoint (or a generated helper over it) and replace the per-membership manager-flag gating for create and manage affordances, so a child sees the controls the matrix permits. Add the event-edit flow to the event sheet, mirroring the web.

Canon:

  • Update CLAUDE.md and docs/frontend-plan.html: child accounts do not see "creation affordances absent entirely". Affordance absence tracks a real matrix denial, and the UI derives affordances from the matrix, not a role bucket (system rule 5).

Definition of done

  • The web derives every create/manage/edit affordance from the shared matrix, canEdit is gone.
  • A child sees create/manage controls for events, jobs, lists and meals, and none for rewards or the rota board.
  • A member can edit an event on both surfaces; recurring edits follow the documented occurrence-versus-series rule.
  • Android matches, per surface tier (system rule 1).
  • docs/frontend-plan.html and CLAUDE.md updated. make check passes. Verified on both surfaces as a child and as a member against the live deploy.
### Summary Make every create, manage and edit affordance in the clients honor the permission matrix, instead of a coarse hand-written role flag. Consume the matrix the server now exposes (`SystemService.GetPermissionMatrix`, #69) so the UI shows exactly the controls a role is permitted — no more, no less. This also surfaces event editing, which the UI does not offer at all today. ### Why The web gates create and manage controls on one flag, `canEdit = ADMIN || MEMBER` (`web/src/auth/auth.tsx`). It excludes the child role wholesale, so a child sees no create controls even for events and jobs the server already permits, and — after #67 — none for the lists and meals a child may now run. Android gates on a per-membership `manager` flag with the same kind of drift. Both are client-side guesses that diverge from the server's real matrix. #69 exposes the matrix over RPC. This issue makes the clients consume it. ### Depends on - #69 — `SystemService.GetPermissionMatrix` (the data source). - #67 — the child list/meal rights this surfaces. ### Scope **Web foundation** (`web/src/auth/`): - Add a permission store that fetches `GetPermissionMatrix` once per session, caches it in `localStorage` (static, non-sensitive), and hydrates synchronously to avoid affordance flicker. - Expose `can(service, method)` on the auth context — true when any of the member's roles appears in that matrix entry. An unknown entry denies, matching the server default. - Remove `canEdit`. Migrate every use to the specific `can(...)` it needs. **Web per-page migration:** - Lists and ListDetail create/rename/delete/uncheck: `can("ItemListService", …)`. - Meals **library** (add and edit a meal): `can("MealService", …)`. - Meals **rota board** (set cook, zone, overrides): `can("MealRotaService", …)`. This splits a control today conflated under `canEdit`, so a child sees the library controls but not the rota controls, per ADR-0028. - Jobs add: `can("ItemService", "Create")`. - Calendar add: `can("EventService", "Create")`. - Rewards create and manage: `can("RewardService", …)`. - Leave the per-item ownership checks alone (`canEditJob = isManager || owner === me`). **Event editing (the capability that does not exist yet):** - Wire an edit flow on the event detail sheet, reusing the job-edit pattern: tap to open, an Edit action opens the event form prefilled, save calls `mutate.events.update`. `EventService.Update` already exists server-side. - Gate the Edit affordance on `can("EventService", "Update")`. - Decide the occurrence-versus-series rule (`SplitOccurrence` then `Update` for one occurrence; `Update` on the parent for the series). A single non-recurring event edits in place. **Android:** - Consume the same `GetPermissionMatrix` endpoint (or a generated helper over it) and replace the per-membership `manager`-flag gating for create and manage affordances, so a child sees the controls the matrix permits. Add the event-edit flow to the event sheet, mirroring the web. **Canon:** - Update `CLAUDE.md` and `docs/frontend-plan.html`: child accounts do not see "creation affordances absent entirely". Affordance absence tracks a real matrix denial, and the UI derives affordances from the matrix, not a role bucket (system rule 5). ### Definition of done - The web derives every create/manage/edit affordance from the shared matrix, `canEdit` is gone. - A child sees create/manage controls for events, jobs, lists and meals, and none for rewards or the rota board. - A member can edit an event on both surfaces; recurring edits follow the documented occurrence-versus-series rule. - Android matches, per surface tier (system rule 1). - `docs/frontend-plan.html` and `CLAUDE.md` updated. `make check` passes. Verified on both surfaces as a child and as a member against the live deploy.
nalum added reference refs/tags/v1.3.0 2026-08-14 11:52:44 +00:00
nalum added this to the (deleted) project 2026-08-14 12:20:29 +00:00
nalum changed title from Surface event editing on web and Android (wire EventService.Update) to Drive UI create/manage/edit affordances off the permission matrix (web + Android) 2026-08-14 14:32:52 +00:00
Author
Owner

Done — all five parts merged to main: web affordances (#70), web event edit (#71), Android affordances (#72), Android event edit (#73), recurring event edit (#74). Deferred repeat-rule editing was never in scope here.

Done — all five parts merged to main: web affordances (#70), web event edit (#71), Android affordances (#72), Android event edit (#73), recurring event edit (#74). Deferred repeat-rule editing was never in scope here.
nalum closed this issue 2026-08-14 16:58:25 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app#61
No description provided.