feat(ui): clickable, persisting Markdown task-list checkboxes (PMS-348) #179

Merged
longjacksonle merged 1 commit from feat/PMS-348-interactive-task-checkboxes into main 2026-06-16 01:52:30 +02:00

Implements PMS-348. Stacks on #177 (PMS-347); the task-list-render commit appears here until #177 merges.

What

Clicking a task-list checkbox in a ticket or project description now toggles it and persists - no more editing raw Markdown to type an x.

How

  • utils/markdown.rs: refactored into to_html + sanitize; added render_markdown_interactive (strips disabled, tags each checkbox data-ti="<index>" in document order) and toggle_task(src, index) which flips the index-th [ ]/[x] in the source. Pure + unit-tested.
  • components/markdown.rs: Markdown gains interactive + on_toggle. dangerous_inner_html can't carry Dioxus handlers, so a single delegated web_sys click listener on the container reads the clicked checkbox's data-ti and calls on_toggle(index) (mirrors the existing theme/auth web_sys hooks).
  • tickets.rs / projects.rs: description Markdown is interactive; on_toggle flips the source via toggle_task and PUT { description } to /tickets/:id or /projects/:id (COALESCE leaves other columns), then refetches. A failed save toasts and refetches to revert.

Verification

just pre-commit (pinned rust 1.94 = CI) passes: fmt, clippy -D warnings, cargo check --target wasm32-unknown-unknown, 97 lib tests (6 new). In-browser toggle/persist behavior should be verified on the running dev app.

Acceptance criteria

  • Clicking a checkbox toggles it without opening the editor.
  • The toggle persists (flips [ ]/[x] in stored Markdown, survives reload).
  • Save failure reverts with an error toast.
  • Plain Markdown still renders sanitized; no XSS regression (type restricted to checkbox).
  • Edit-permission gating: currently mirrors the existing always-visible Edit affordance; server enforces and a 403 toasts.

🤖 Generated with Claude Code

Implements PMS-348. Stacks on #177 (PMS-347); the task-list-render commit appears here until #177 merges. ## What Clicking a task-list checkbox in a ticket or project description now toggles it and persists - no more editing raw Markdown to type an `x`. ## How - `utils/markdown.rs`: refactored into `to_html` + `sanitize`; added `render_markdown_interactive` (strips `disabled`, tags each checkbox `data-ti="<index>"` in document order) and `toggle_task(src, index)` which flips the index-th `[ ]`/`[x]` in the source. Pure + unit-tested. - `components/markdown.rs`: `Markdown` gains `interactive` + `on_toggle`. `dangerous_inner_html` can't carry Dioxus handlers, so a single delegated `web_sys` click listener on the container reads the clicked checkbox's `data-ti` and calls `on_toggle(index)` (mirrors the existing theme/auth web_sys hooks). - `tickets.rs` / `projects.rs`: description Markdown is interactive; `on_toggle` flips the source via `toggle_task` and `PUT { description }` to `/tickets/:id` or `/projects/:id` (COALESCE leaves other columns), then refetches. A failed save toasts and refetches to revert. ## Verification `just pre-commit` (pinned rust 1.94 = CI) passes: fmt, clippy `-D warnings`, `cargo check --target wasm32-unknown-unknown`, 97 lib tests (6 new). In-browser toggle/persist behavior should be verified on the running dev app. ## Acceptance criteria - [x] Clicking a checkbox toggles it without opening the editor. - [x] The toggle persists (flips `[ ]`/`[x]` in stored Markdown, survives reload). - [x] Save failure reverts with an error toast. - [x] Plain Markdown still renders sanitized; no XSS regression (type restricted to checkbox). - [ ] Edit-permission gating: currently mirrors the existing always-visible Edit affordance; server enforces and a 403 toasts. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(ui): make Markdown task-list checkboxes clickable and persist the toggle (PMS-348)
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 57s
Create release / Create release from merged PR (pull_request) Has been skipped
f2d4bfcd56
Rendered task-list checkboxes (PMS-347) were read-only; users had to edit the raw Markdown to tick an item. Clicking a checkbox in a ticket or project description now toggles it and persists.

utils/markdown.rs: refactor into to_html + sanitize; add render_markdown_interactive (strips the `disabled` attribute and tags each checkbox with data-ti="<index>" in document order) and toggle_task(src, index) which flips the index-th `[ ]`/`[x]` marker in the source. Pure and unit-tested (flip by index, nested/indented, *,+ bullets, out-of-range).

components/markdown.rs: Markdown gains `interactive` + `on_toggle`. When interactive, it renders enabled checkboxes and attaches one delegated click listener on the container (stable per-instance id); a click reads the checkbox's data-ti and calls on_toggle with that index. dangerous_inner_html can't carry Dioxus handlers, so this web_sys delegation (mirroring the theme/auth hooks) is how the click is captured.

tickets.rs / projects.rs: the description Markdown is now interactive; on_toggle flips the source via toggle_task and PUTs { description } to /tickets/:id or /projects/:id (COALESCE leaves other columns), then refetches. A failed save toasts and refetches to revert. Non-checkbox content is unchanged; sanitization still holds (type restricted to checkbox).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/PMS-348-interactive-task-checkboxes 2026-06-16 01:52:30 +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/mokosh-apps!179
No description provided.