refactor(sidebar): per-enclave admin-managed room categories (LC-79 redesign) #137
Loading…
Reference in a new issue
No description provided.
Delete branch "refactor/lc-79-enclave-scoped-categories"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Pivots LC-79 from per-user sidebar categorization (auth.db, each user organises their own sidebar in isolation) to per-enclave shared categories (chat.db, managed by enclave admin / owner / site admin, visible to every member of the enclave). Old per-user tables are dropped; existing categorizations are gone (acceptable since the feature is brand-new and only the dev instance had any).
Schema
auth/0017_drop_sidebar_categories_add_collapsed.sql: DROPs the per-usersidebar_categories+sidebar_category_rooms; CREATEscollapsed_categories (user_id, category_id)for the one piece of state that stays per-user (fold/expand is a personal view preference).chat/0026_room_categories.sql: CREATEsroom_categories (id, enclave_id, name, position, created_at)androom_category_assignments (room_id PK, category_id, position)so each room belongs to at most one category. FK CASCADEs on enclave_id, room_id, category_id keep deletion semantics correct.Routes
All mutations moved under
/enclave/{id}/sidebar/categories/...and gated byenclave_can_manage(Owner / Admin / site admin):POST /enclave/{id}/sidebar/categoriesPATCH /enclave/{id}/sidebar/categories/{cat_id}(rename)DELETE /enclave/{id}/sidebar/categories/{cat_id}PATCH /enclave/{id}/sidebar/categories/{cat_id}/rooms/{room_id}(assign; same-enclave check)DELETE /enclave/{id}/sidebar/categories/rooms/{room_id}(unassign)PATCH /enclave/{id}/sidebar/categories/positions(reorder cats)PATCH /enclave/{id}/sidebar/categories/{cat_id}/positions(reorder rooms / cross-cat upsert)Lone per-user endpoint (no admin RBAC, any enclave member may fold their own view):
PATCH /sidebar/categories/{cat_id}/collapseEvery shared-state mutation broadcasts a new
SidebarCategoriesChanged { enclave_id }WS event to all enclave members so live tabs catch up without a manual refresh.Plumbing
load_sidebar/load_chromethread two new values through every view struct (~25 sites via batch regex):can_manage_sidebar_categories: bool(compute once, gate UI affordances).sidebar_current_enclave: Option<i64>(needed by the +Add form when no categories exist yet to know which enclave to target).SidebarCategoryGroupgainsenclave_idso the template can build admin URLs without extra context.Template
can_manage_sidebar_categories.enclave_id./sidebar/categories/{id}/collapse(per-user setting).RBAC nuance
The user's planning answer picked "Enclave admin + moderator + owner", but lets-chat's enclave role model only has Owner / Admin / Member. Used
enclave_can_manage(Owner + Admin + site admin). Adding an enclave-level Moderator role is a separate feature.Test plan
just check(fmt + clippy across standalone + saas).routes_sidebar_categories.rstests: admin creates, member 403, cross-user shared visibility, per-user collapse isolation (4/4 pass).just test: green modulo the knownroutes_uploads::send_message_with_attachment_renders_inline_imageflake documented in CLAUDE.md (passes in isolation).Pivots LC-79's per-user categorization (auth.db, each user organises their own sidebar) to per-enclave shared categories (chat.db, managed by enclave admin / owner / site admin, visible to all members of the enclave). Drops the existing per-user tables and starts fresh; the old data is gone (acceptable since the feature is brand-new and only the dev instance had any categorizations). Schema: - auth migration 0017 DROPs `sidebar_categories` + `sidebar_category_rooms` and CREATEs `collapsed_categories (user_id, category_id)` for per-user collapsed state. - chat migration 0026 CREATEs `room_categories (id, enclave_id, name, position, created_at)` and `room_category_assignments (room_id PK, category_id, position)` so each room belongs to at most one category. FK CASCADEs on enclave_id, room_id, category_id keep deletion semantics correct. DB module (`db::sidebar_categories`): - Functions now take `enclave_id` instead of `user_id` for shared state (`list_categories_for_enclave`, `create_category`, `rename_category`, `delete_category`, `set_category_positions`, `room_assignments_for_enclave`, `enclave_of_category`). - `assign_room`, `unassign_room`, `set_room_positions` work on room_id directly (no per-user dimension). - Per-user collapsed helpers (`list_collapsed_for_user`, `set_collapsed`) live alongside, hitting the auth.db table. Routes: - All mutating endpoints moved under `/enclave/{id}/sidebar/categories/...` and gated by `enclave_can_manage` (Owner / Admin / site admin). Membership-checks the room id against the same enclave on every assignment. - `PATCH /sidebar/categories/{id}/collapse` is the lone per-user endpoint: any member of the enclave that owns the category may fold their own view. - Every shared-state mutation broadcasts a new `SidebarCategoriesChanged { enclave_id }` event to all members so live tabs catch up without a manual refresh. load_sidebar / load_chrome now thread two new outputs through every view struct: - `can_manage_sidebar_categories: bool` (compute once, gate UI affordances). - `sidebar_current_enclave: Option<i64>` (needed by the +Add form when no categories exist yet). SidebarCategoryGroup gains `enclave_id` so the template can build admin URLs without extra context. Template: - Admin affordances (rename, delete, +Add) gated on `can_manage_sidebar_categories`. - Per-room move dropdown likewise gated; URLs now include the category's `enclave_id` instead of the per-user form. - Category-section drag handles only rendered when admin (members can't reorder shared state). - The collapse form is unconditional (per-user setting). WS: - `ChatEvent::SidebarCategoriesChanged` added; ws.rs match arm re-renders the sidebar for the recipient on receipt. `ws_fragments::render_event` adds it to the per-recipient (no-fragment) bucket. Tests: - Old per-user tests replaced; the new file covers admin-create / member-403 / cross-user shared-visibility / per-user-collapse-isolation. 4/4 pass. - 18 hand-rolled test files updated with auth migration 0017 + chat migration 0026 entries (CLAUDE.md test-maintenance discipline). `just check` + `just test` green modulo the known routes_uploads concurrent-binary flake (documented in CLAUDE.md; passes in isolation).