feat(dashboard): edit in place over the live dashboard with a bottom widget drawer
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 7s
CI / integration (push) Successful in 2m16s
CI / publish (push) Successful in 58s

Milestone 72 phase D — the dashboard view IS the edit surface (operator ask):
- /d/<id> renders the grid via Gridstack in STATIC mode — positioned exactly like
  before, live HTMX widget bodies keep polling. An "Edit" button flips the same
  grid interactive (grid.setStatic(false)) in place, so you drag/resize over real
  data. "Done" flips it back. Layout autosaves on change.
- The widget picker is now a bottom drawer (position:fixed overlay) revealed by
  body.dash-editing — so the dashboard width is identical entering/leaving edit.
- Add: POST returns a single grid item; JS inserts it + grid.makeWidget +
  htmx.process so it loads live data. Remove: POST 204 + grid.removeWidget.
  Per-panel drag handle + remove ✕ are in the DOM for editors, shown only while
  editing.
- Gridstack loads for everyone (viewers get the static grid); edit wiring is
  gated on can_edit. Mobile collapses to one column.
- Removed the separate /d/<id>/edit route + edit.html + _edit_panels.html
  (rule 22). Dashboard-list Edit and new-dashboard create now deep-link
  /d/<id>?edit=1 which opens edit mode on load.

Browser-only behaviour — CI can't exercise it; needs an operator visual check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 15:42:10 -04:00
parent 988c13d51f
commit 88dca32d3c
8 changed files with 257 additions and 270 deletions
+2 -2
View File
@@ -32,7 +32,7 @@
</div>
</div>
<div style="display:flex;align-items:center;gap:0.4rem;flex-shrink:0;">
<a href="/d/{{ dash.id }}/edit" class="btn btn-ghost btn-sm">Edit</a>
<a href="/d/{{ dash.id }}?edit=1" class="btn btn-ghost btn-sm">Edit</a>
<details style="position:relative;">
<summary class="btn btn-ghost btn-sm" style="cursor:pointer;list-style:none;">Rename</summary>
@@ -99,7 +99,7 @@
</form>
{# Admins can also edit system dashboards #}
{% if user_role == 'admin' %}
<a href="/d/{{ dash.id }}/edit" class="btn btn-ghost btn-sm">Edit</a>
<a href="/d/{{ dash.id }}?edit=1" class="btn btn-ghost btn-sm">Edit</a>
{% endif %}
</div>
</div>