feat(dashboard): edit in place over the live dashboard with a bottom widget drawer
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:
+21
-11
@@ -165,18 +165,28 @@ textarea { resize: vertical; }
|
||||
.ping-meta { min-width:120px; max-width:180px; flex-shrink:1; overflow:hidden; }
|
||||
.ping-name { font-size:0.875rem; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block; }
|
||||
a.ping-name:hover { text-decoration:underline; }
|
||||
/* Dashboard widget grid — Grafana-style 12-col layout; cells sized by drag-resize
|
||||
(Gridstack) in the edit view and rendered statically here from each widget's
|
||||
x/y/w/h. Cell height is fixed (h * cell unit) with the body scrolling, so the
|
||||
layout the operator arranged is exactly what they see. */
|
||||
.dash-grid { display:grid; grid-template-columns:repeat(12,1fr); grid-auto-rows:70px; gap:1rem; }
|
||||
.dash-grid > .dash-cell { margin-bottom:0; min-width:0; overflow:hidden; display:flex; flex-direction:column; }
|
||||
.dash-cell .dash-cell-body { flex:1; min-height:0; overflow:auto; }
|
||||
@media (max-width:820px) {
|
||||
.dash-grid { grid-template-columns:1fr; grid-auto-rows:auto; }
|
||||
.dash-grid > .dash-cell { grid-column:1 / -1 !important; grid-row:auto !important; }
|
||||
.dash-cell .dash-cell-body { overflow:visible; }
|
||||
/* Dashboard widget grid — Gridstack-rendered. Static (positioned but inert) in
|
||||
view mode; the dashboard itself becomes the edit surface when Edit is toggled,
|
||||
so drag-resize happens over live widget data. The bottom drawer is a fixed
|
||||
overlay, so the dashboard width is identical entering/leaving edit mode. */
|
||||
.grid-stack-item-content {
|
||||
background:var(--bg-card); border:1px solid var(--border-mid); border-radius:8px;
|
||||
padding:0.8rem 1rem; display:flex; flex-direction:column; overflow:hidden;
|
||||
}
|
||||
.grid-stack-item-content .dash-cell-body { flex:1; min-height:0; overflow:auto; }
|
||||
.panel-chrome { display:none; }
|
||||
.panel-drag { cursor:grab; color:var(--text-dim); user-select:none; }
|
||||
.panel-drag:active { cursor:grabbing; }
|
||||
body.dash-editing .panel-chrome { display:inline-flex; align-items:center; }
|
||||
body.dash-editing .grid-stack-item-content { outline:1px dashed var(--border-mid); outline-offset:-1px; }
|
||||
body.dash-editing main { padding-bottom:46vh; }
|
||||
.widget-drawer {
|
||||
position:fixed; left:0; right:0; bottom:0; transform:translateY(100%);
|
||||
transition:transform .25s ease; max-height:44vh; overflow-y:auto;
|
||||
background:var(--bg-card); border-top:1px solid var(--border-mid);
|
||||
box-shadow:0 -10px 30px rgba(0,0,0,0.45); z-index:60; padding:1rem 1.5rem 1.5rem;
|
||||
}
|
||||
body.dash-editing .widget-drawer { transform:translateY(0); }
|
||||
.ping-addr { display:block; color:var(--text-muted); font-size:0.75rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.ping-pills { display:flex; gap:2px; flex:1; min-width:0; overflow:hidden; align-items:center; }
|
||||
.pill { display:inline-block; width:7px; height:22px; border-radius:2px; cursor:default; transition:opacity .1s; }
|
||||
|
||||
Reference in New Issue
Block a user