feat(rooms): room info page with description + wiki (LC-86) #150

Merged
nrupard merged 1 commit from feat/lc-86-room-docs-wiki into main 2026-05-19 17:16:58 +02:00
Owner

Closes LC-86. Adds a /room/{id}/info page surfacing a long-form room description, a single wiki page, and (in a separate tab) the existing pinned-message list. Wiki and description bodies render through the same Markdown pipeline (views::markdown, LC-59) chat messages use, so :emoji: shortcodes resolve the same way.

Schema

  • Migration 0030 adds rooms.description, rooms.wiki_body, rooms.wiki_updated_at, rooms.wiki_updated_by (all nullable). Existing rooms unchanged. Absence of wiki_body is the "no wiki yet" signal the template branches on.

Code

  • models::Room gains the four new fields; every SELECT * FROM rooms site updated. db::chat::set_room_description and set_room_wiki write the columns. set_room_wiki stamps wiki_updated_at = datetime('now') and wiki_updated_by on save; an empty body clears all three fields.
  • New routes::room_info module: GET /room/{id}/info renders the page (?tab=docs default, ?tab=pinned). GET /room/{id}/wiki returns the view fragment (cancel button target). GET /room/{id}/wiki/edit returns the inline textarea (Mod+ only via LC-84 is_room_moderator). PATCH /room/{id}/wiki writes the body + stamps metadata + logs room_wiki_edit audit row + returns rendered HTML. Description mirrors the triplet, gated on the admin-tier room_can_manage_overrides.
  • routes::enclave::post_edit_room now also calls set_room_description so the existing enclave-admin room-edit form can set it in one round trip.
  • Header partial: #room-name block is now an <a> to /room/{id}/info, plus an explicit "Info" button in the header action row so the page is also reachable when no topic is set.

UI

  • Tab nav is plain ?tab= anchor links (keyboard / screen-reader native, no HTMX dependence for navigation). Two HTMX-swappable slots inside the docs tab handle inline editing: #description-content and #wiki-content. Save: PATCH and swap. Cancel: re-fetch view fragment and swap.

Acceptance criteria coverage

  • Room admins set/edit topic, description, wiki body (topic via existing enclave form; description + wiki via inline forms on the info page).
  • Header topic clicks open the info panel (now a link to /info).
  • Pinned + wiki coexist as two tabs.
  • Wiki edits go through the markdown pipeline (LC-59 emoji + markdown).
  • Audit log records last wiki edit (wiki_updated_at / wiki_updated_by on the row + room_wiki_edit action log).
  • HTMX inline edit posts via PATCH /room/:id/wiki and renders the updated content in place.

Out of scope (open questions, deferred)

  • Wiki revision history (append-only table like message_edits).
  • Wiki full-text search.
  • Per-enclave shared wikis.
  • Audit row for description edits.

Tests

  • routes_room_info.rs (6/6 passing): member can view the page; member 403s on PATCH wiki and description; admin can PATCH wiki (returns rendered HTML, writes audit row); admin can PATCH description; empty wiki body clears all three columns.
  • just check clean. just test / just test-saas pass aside from the pre-existing routes_uploads concurrent-binary flake (CLAUDE.md-noted, out of scope).

Migration drift: 0030 added to the seven hand-list test files (db_uploads, uploads_sweep, routes_reconnect, routes_uploads, db_enclave, admin_uploads, db_private_rooms). common::pool callers pick it up via sqlx::migrate!.

Closes LC-86. Adds a `/room/{id}/info` page surfacing a long-form room description, a single wiki page, and (in a separate tab) the existing pinned-message list. Wiki and description bodies render through the same Markdown pipeline (`views::markdown`, LC-59) chat messages use, so `:emoji:` shortcodes resolve the same way. Schema - Migration 0030 adds `rooms.description`, `rooms.wiki_body`, `rooms.wiki_updated_at`, `rooms.wiki_updated_by` (all nullable). Existing rooms unchanged. Absence of `wiki_body` is the "no wiki yet" signal the template branches on. Code - `models::Room` gains the four new fields; every `SELECT * FROM rooms` site updated. `db::chat::set_room_description` and `set_room_wiki` write the columns. `set_room_wiki` stamps `wiki_updated_at = datetime('now')` and `wiki_updated_by` on save; an empty body clears all three fields. - New `routes::room_info` module: GET `/room/{id}/info` renders the page (?tab=docs default, ?tab=pinned). `GET /room/{id}/wiki` returns the view fragment (cancel button target). `GET /room/{id}/wiki/edit` returns the inline textarea (Mod+ only via LC-84 `is_room_moderator`). `PATCH /room/{id}/wiki` writes the body + stamps metadata + logs `room_wiki_edit` audit row + returns rendered HTML. Description mirrors the triplet, gated on the admin-tier `room_can_manage_overrides`. - `routes::enclave::post_edit_room` now also calls `set_room_description` so the existing enclave-admin room-edit form can set it in one round trip. - Header partial: `#room-name` block is now an `<a>` to `/room/{id}/info`, plus an explicit "Info" button in the header action row so the page is also reachable when no topic is set. UI - Tab nav is plain `?tab=` anchor links (keyboard / screen-reader native, no HTMX dependence for navigation). Two HTMX-swappable slots inside the docs tab handle inline editing: `#description-content` and `#wiki-content`. Save: PATCH and swap. Cancel: re-fetch view fragment and swap. Acceptance criteria coverage - [x] Room admins set/edit topic, description, wiki body (topic via existing enclave form; description + wiki via inline forms on the info page). - [x] Header topic clicks open the info panel (now a link to `/info`). - [x] Pinned + wiki coexist as two tabs. - [x] Wiki edits go through the markdown pipeline (LC-59 emoji + markdown). - [x] Audit log records last wiki edit (`wiki_updated_at` / `wiki_updated_by` on the row + `room_wiki_edit` action log). - [x] HTMX inline edit posts via `PATCH /room/:id/wiki` and renders the updated content in place. Out of scope (open questions, deferred) - Wiki revision history (append-only table like `message_edits`). - Wiki full-text search. - Per-enclave shared wikis. - Audit row for description edits. Tests - `routes_room_info.rs` (6/6 passing): member can view the page; member 403s on PATCH wiki and description; admin can PATCH wiki (returns rendered HTML, writes audit row); admin can PATCH description; empty wiki body clears all three columns. - `just check` clean. `just test` / `just test-saas` pass aside from the pre-existing `routes_uploads` concurrent-binary flake (CLAUDE.md-noted, out of scope). Migration drift: 0030 added to the seven hand-list test files (`db_uploads`, `uploads_sweep`, `routes_reconnect`, `routes_uploads`, `db_enclave`, `admin_uploads`, `db_private_rooms`). `common::pool` callers pick it up via `sqlx::migrate!`.
feat(rooms): room info page with description + wiki (LC-86)
Some checks failed
Check / clippy + fmt + tests (pull_request) Failing after 17s
712a4723e8
A new `/room/{id}/info` page surfaces the room's long-form description, a single wiki page, and (via a tab) the existing pinned-message list. Wiki and description bodies render through the same Markdown pipeline (`views::markdown`, LC-59) chat messages use, so custom emoji shortcodes resolve the same way. Topic remains a header-only field; this PR adds editable storage for the longer description and the wiki.

Schema
- Migration 0030 adds `rooms.description`, `rooms.wiki_body`, `rooms.wiki_updated_at`, `rooms.wiki_updated_by` (all nullable). Existing rooms come out unchanged because the ALTER does not back-fill, and absence of `wiki_body` is the "no wiki yet" signal the template branches on.

Code
- `models::Room` gains the four new fields. `db::chat::map_room` and every `SELECT * FROM rooms` site updated (single-table queries get the bare column names, joined queries qualify with `r.`).
- `db::chat::set_room_description(pool, room_id, body)` and `set_room_wiki(pool, room_id, body, actor)` write the new columns. `set_room_wiki` stamps `wiki_updated_at = datetime('now')` and `wiki_updated_by` on save; passing `None` for the body clears all three fields.
- New `routes::room_info` module: `GET /room/{id}/info` renders the page with tab nav (`?tab=docs` default, `?tab=pinned`). `GET /room/{id}/wiki` returns the rendered view fragment (used by the cancel button). `GET /room/{id}/wiki/edit` returns the inline textarea editor (Mod+ only). `PATCH /room/{id}/wiki` writes the body, stamps metadata, logs a `room_wiki_edit` mod-action row, and returns the re-rendered view fragment. Description has the same `GET / GET edit / PATCH` triplet, gated on the admin-tier `room_can_manage_overrides` predicate (same as moderator-grant authorisation).
- `routes::enclave::post_edit_room` now also calls `set_room_description` so the existing enclave-admin room-edit form can set the description in one round trip (the form already takes `name` and `topic`).
- `routes::pinned::resolve_author_labels` exposed as `pub(crate)` so the info page can build pinned-tab rows without duplicating the bulk auth-lookup helper.
- `partials/room_header.html`: the topic / room-name block is now a single link to `/room/{id}/info`, plus a new "Info" button in the header action row so the page is also reachable when there is no topic to click.

UI
- `templates/room/info.html` is the page. Tab nav is plain anchor links (`?tab=`) so the page is keyboard / screen-reader native; no HTMX dependence for navigation. Two HTMX-swappable slots inside the docs tab handle inline editing: `#description-content` and `#wiki-content`. The edit forms swap a textarea in place; save submits via `PATCH` and replaces the slot with the rendered view; cancel re-fetches the read-only fragment.

Audit log
- Wiki saves record `room_wiki_edit` rows. Description edits do not currently log (admin-tier action with no security stake beyond the visible body; we can add a row in a follow-up if anyone cares).

Out of scope (open questions, deferred)
- Wiki revision history (append-only table like `message_edits`).
- Wiki full-text search.
- Per-enclave shared wikis.

Tests
- New integration binary `routes_room_info.rs` (6/6 passing): member can view the page, member cannot PATCH wiki or description (403), admin can edit wiki (200 + audit row), admin can set description (200), and an empty wiki PATCH clears all three wiki columns.
- `just check` clean.

Migration drift: 0030 added to the seven hand-list tests (`db_uploads`, `uploads_sweep`, `routes_reconnect`, `routes_uploads`, `db_enclave`, `admin_uploads`, `db_private_rooms`). Tests under `common::pool` pick it up via `sqlx::migrate!`.
nrupard deleted branch feat/lc-86-room-docs-wiki 2026-05-19 17:16:59 +02:00
Sign in to join this conversation.
No reviewers
No labels
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
psa-systems/lets-chat!150
No description provided.