Skip to content

feat(admin): notes edit/delete UI + fix broken create flow (#71)#78

Merged
x3ek merged 6 commits into
mainfrom
feat/71-notes-admin-edit-delete
May 16, 2026
Merged

feat(admin): notes edit/delete UI + fix broken create flow (#71)#78
x3ek merged 6 commits into
mainfrom
feat/71-notes-admin-edit-delete

Conversation

@x3ek
Copy link
Copy Markdown
Contributor

@x3ek x3ek commented May 16, 2026

Summary

  • Adds the missing edit/delete UI for notes in the admin dashboard.
  • Fixes two pre-existing bugs in the same area: the create form was sending form-urlencoded to a JSON endpoint (422), and the delete button was swapping a JSON response into the DOM where the note used to be.
  • Uses HTMX (already loaded in the admin templates) — endpoints now detect HX-Request and return rendered HTML partials. JSON behavior is preserved for non-HTMX callers (curl, future API).

Approach

  • New shared partials in themes/default/admin/: _note_item.html (read-only row with Edit + Delete) and _note_edit_form.html (inline edit form). The theme loader's default-theme fallback means blue-tech and terminal reuse them automatically — no per-theme duplication.
  • New ThemeEngine.render_partial(...) returns a small HTML snippet without the heavy default render context (no nav/favicon/featured_posts).
  • admin.py gains is_htmx, parse_note_create, parse_note_update, _to_note_response, _render_note_partial helpers. POST/PUT/DELETE branch on HX-Request. Two new GET routes power the inline edit/cancel flow.
  • HTMX auth failures attach HX-Redirect: /auth/login so the browser bounces to login with no client JS.
  • Checkbox edge case: unchecked is_public in an HTMX form PUT now persists is_public=False (the parser sets it explicitly), so toggling a note from public→private actually works.
  • Added python-multipart to dependencies — required by Starlette's request.form().

Test plan

  • python scripts/run-checks.py — 4/4 checks pass (format, lint, 183 tests, pyright).
  • 13 new tests in tests/test_admin_notes.py cover the JSON path (preserved), the HTMX form path, the checkbox-off semantics, and HX-Redirect on auth failure.
  • Manual (default theme): create note → row appears at top; click Edit → inline form with current text + is_public state; modify text + uncheck public + Save → row reverts to read-only and shows "Private"; Cancel → unchanged; Delete + confirm → row disappears.
  • Manual (blue-tech): visually verified — Edit/Delete buttons styled correctly; edit/cancel flow works.
  • Manual (terminal): visually verified — Edit/Delete buttons styled correctly; edit form renders cleanly.
  • JSON contract: curl -X PUT /admin/notes/3 -H 'Content-Type: application/json' -d '{"text":"x"}' still returns NoteResponse JSON.

Follow-ups (will file after this PR is open)

  • CSRF protection for admin mutations — pre-existing gap. Admin POST/PUT/DELETE routes currently accept no CSRF token; this PR does not change that.
  • Empty-state restore after deleting the last note — minor UX nit; the "No notes yet" placeholder won't reappear after HTMX-delete of the final row.

Closes #71

🤖 Generated with Claude Code

x3ek and others added 4 commits May 16, 2026 07:27
Extract the per-note markup into themes/default/admin/_note_item.html plus a new _note_edit_form.html. Add ThemeEngine.render_partial() so the admin notes endpoints can return self-contained HTML fragments without the heavy default render context (no nav/favicon/featured_posts fetches).

Partials live only in the default theme — the AsyncHybridLoader fallback means blue-tech and terminal pick them up automatically. The three admin templates now {% include %} the shared partial.

Refs #71

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Branch /admin/notes POST/PUT/DELETE on the HX-Request header. HTMX requests get rendered HTML partials (_note_item.html); non-HTMX callers continue to get JSON (NoteResponse / {status: deleted}). Form-urlencoded payloads from HTMX forms are now accepted alongside JSON via parse_note_create / parse_note_update helpers.

Also: unchecking the is_public checkbox in the edit form now persists is_public=False (form data omits absent checkboxes, so the parser explicitly sets the field). Auth failures on HTMX requests now attach HX-Redirect: /auth/login so the browser bounces to login without any client JS.

Adds two new routes for inline editing: GET /admin/notes/{id}/edit (returns _note_edit_form.html) and GET /admin/notes/{id}/view (returns _note_item.html, used by the edit form's Cancel button).

Refs #71

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 new tests against the admin notes endpoints:

- JSON path returns NoteResponse; HTMX path returns rendered HTML partial

- Unchecked is_public checkbox persists False (both create and update)

- 404s on missing notes for PUT/DELETE/edit-form

- DELETE with HX-Request returns empty 200; without it returns {status: deleted}

- HX-Redirect header attached only on HTMX auth failures

Refs #71

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Required by Starlette/FastAPI's request.form() to parse application/x-www-form-urlencoded bodies, which the new HTMX admin notes flow relies on. Without it, PUT /admin/notes/{id} from the inline edit form returns 500 with 'The python-multipart library must be installed to use form parsing.'

Refs #71

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds inline edit/delete controls for admin notes using HTMX while preserving the existing JSON API behavior for non-HTMX callers. It also introduces partial template rendering to support HTMX swaps and adds tests for the new dual-response behavior.

Changes:

  • Adds shared note row + inline edit-form partials and updates bundled themes to include the shared note row.
  • Updates admin notes endpoints to parse either JSON or form submissions and return HTML partials for HTMX requests.
  • Adds ThemeEngine.render_partial(...), new admin routes for edit/view partials, plus tests and the python-multipart dependency.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
themes/terminal/admin/admin.html Switches note rendering to shared _note_item partial.
themes/default/admin/admin.html Switches note rendering to shared _note_item partial.
themes/default/admin/_note_item.html New shared note row partial with Edit/Delete HTMX actions.
themes/default/admin/_note_edit_form.html New shared inline edit form partial with Save/Cancel HTMX flow.
themes/blue-tech/admin/admin.html Switches note rendering to shared _note_item partial.
src/squishmark/services/theme/engine.py Adds render_partial() for lightweight partial rendering.
src/squishmark/routers/admin.py Adds HTMX detection, dual JSON/form parsing helpers, partial-render helpers, and new edit/view routes; updates notes CRUD to branch on HTMX.
tests/test_admin_notes.py Adds coverage for JSON vs HTMX behavior, checkbox semantics, and HX-Redirect auth behavior.
pyproject.toml Adds python-multipart required for request.form() parsing.

Comment thread src/squishmark/routers/admin.py Outdated
Comment thread src/squishmark/routers/admin.py Outdated
Comment thread src/squishmark/services/theme/engine.py Outdated
x3ek and others added 2 commits May 16, 2026 07:56
Three issues raised by Copilot's PR review:

1. parse_note_create/parse_note_update now catch JSON decode errors and Pydantic ValidationError, re-raising as HTTPException(422). Previously a malformed JSON body or missing required field would bubble up as 500 because the validation happened outside FastAPI's parameter binding.

2. Form parsing no longer coerces missing fields to empty strings. Absent fields are simply not included in the dict, so Pydantic enforces required-ness on create (path/text both 422 if missing) and absent text on update is treated as 'no change' (text=None) instead of overwriting with ''.

3. ThemeEngine.render_partial now saves and restores loader.current_theme in a try/finally so concurrent HTMX requests with different themes can't leak state into each other.

Adds 6 new tests covering the 422 paths, missing-field behaviors on form data, and theme-state restoration (including on render exceptions).

Refs #71, #78

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@x3ek x3ek merged commit 338deb0 into main May 16, 2026
5 checks passed
@x3ek x3ek deleted the feat/71-notes-admin-edit-delete branch May 16, 2026 13:19
x3ek added a commit that referenced this pull request May 16, 2026
Hit this on PR #78: all checks green, no required approvals, but gh pr merge returned mergeStateStatus=BLOCKED because the 3 Copilot review threads weren't resolved. Replying to a thread doesn't resolve it — it's a separate action via the resolveReviewThread GraphQL mutation. Document the resolve-all-threads one-liner so future PRs don't get stuck the same way.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add edit and delete UI for notes in admin dashboard

2 participants