Redesign phase D: sidebar app shell, per-frame tabs, namespaced API
Build and push server image / build-and-push (push) Successful in 43s

The web UI grows into the multi-frame world: a left sidebar lists the
user's frames (with an online dot driven by the same overdue math as
the Device panel; collapsible off-canvas with a hamburger on mobile),
and each frame gets three tabs -- Photos (album picker, now displaying,
the drag-to-reorder upcoming grid), Configuration (name/order/
orientation/refresh/quiet hours/timezone/smart crop + the firmware
card), and Stats (device telemetry, lifetime counters, battery chart).
Settings and Admin adopt the same shell. / becomes a routing hub:
first frame, empty-state onboarding page, setup/login, or the
manage-QR redirect.

The JSON API moves to /api/frames/{id}/... behind require_frame_view /
require_frame_control: any linked user (admins see all) can view; 404
for frames outside your view so ids aren't confirmed; mutations 409
with the holder's name unless you hold the soft control lock, and
POST take-control always flips it to you. Config saves are now partial
updates -- each tab posts only its own fields (checkboxes always sent
explicitly), so the split forms can't clobber each other.

All CSS moves to static/theme.css and the old 680-line inline script
block splits into static/*.js -- the Pointer Events drag-drop state
machine and the canvas battery chart ported intact, not rewritten. The
CSRF fetch wrapper now reads a <meta> tag. No build step, still vanilla.

Verified end-to-end: page/static/API suites, control-lock handoff in
both directions, partial-save field preservation, non-admin frame
isolation, and the legacy-device curl suite (still byte-identical
responses for the deployed frame).
This commit is contained in:
2026-07-21 23:56:18 -04:00
parent 683e3881b1
commit 8ac3fc0de3
25 changed files with 2147 additions and 1612 deletions
+20
View File
@@ -0,0 +1,20 @@
{% extends "app_base.html" %}
{% block title %}ESPresso Frame{% endblock %}
{% block page_title %}Welcome{% endblock %}
{% block content %}
<section class="card">
<h2 class="card-title">No frames yet</h2>
<p class="sub">Set up a frame and it'll appear in the sidebar:</p>
<p class="sub">1. Power the frame on -- it opens a WiFi network named
<code>ESPRESSO_XXXXXX</code> and shows join instructions on its panel.</p>
<p class="sub">2. Join that network and fill in your WiFi details plus this
server's address.</p>
<p class="sub">3. Your browser lands on this server's claim page and links
the frame to your account automatically.</p>
<p class="sub" style="margin-top: 12px;">Already provisioned? Ask whoever
claimed it (or an admin) to link your account, or scan the frame's
on-panel manage QR.</p>
</section>
{% endblock %}