Tokens (light + dark, neumorphism, 54/54 WCAG AA contrast pairs), the self-hosted JetBrains Mono fonts, and interactive mockups of every screen across three iterations. Iteration 3 is final: phone, tablet and desktop tiers, device preview, and "empty spool weight" terminology. The build scripts regenerate the screens byte-identically from source. Implementation is tracked in Maestro feature UI Refresh - V3.0 (224b72a1-8d84-4970-97ea-5dafa4d8ea3b). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
143 lines
11 KiB
Markdown
143 lines
11 KiB
Markdown
# Iteration 3: the remaining screens, plus tablet and phone
|
||
|
||
2026-09-18. Built on iteration 2 (left unchanged in `../iteration-2/`). Same v3
|
||
tokens, same shared panel/palette/toast system. **Start at
|
||
`screens/index.html`** (flow map, 14 guided flows) or **`screens/devices.html`**
|
||
(every screen in live phone, tablet-portrait and tablet-landscape frames).
|
||
|
||
---
|
||
|
||
## 1. Your answers, applied
|
||
|
||
| Question | Your answer | What changed |
|
||
|---|---|---|
|
||
| One-click suggestion | Fine if accurate; fixable by hand | Kept one click. Every attributed print now has **Change spool** in its panel, and it states the real effect: *"46 g goes back to Elegoo Black #42 and comes off the spool you pick; cost is re-rated."* (That's what `SetPrintSpool` does on `dev`.) Rows attributed before deductions were tracked get the real refusal: reconcile to the same spool first (see print **Test_cube**). |
|
||
| Weigh-session order | By weight, with location visible | The session always runs **lightest first**, whatever the table sort. Each step opens with a **"Find it: Shelf C"** strip and "spool 2 of 4 · lightest first". |
|
||
| Landing page | Dashboard | Unchanged. |
|
||
| Filament badge | Yes: it flags an assumed weight that could be off | **Filament [7]** badge = empty spool weight **unverified**: 3 unknown + 4 estimated (Generic 180 g, Refill 35 g). Its tooltip splits the two. The saved view is renamed **Empty spool weight unverified**, and the Dashboard item reads "7 spools have an unverified empty spool weight: 3 can't be weighed yet, 4 could be off." |
|
||
| Palette commands | Makes sense | `load ‹spool› into ‹holder›` and `attribute ‹print› to ‹spool›`. They're typed in stages: the list narrows to spools or prints, then to holders or spools. Full holders show "full · 4/4" and refuse. Two new actions in the empty palette prefill them. |
|
||
|
||
> **Check my reading of 4a.** I took "identifies an assumed weight" to include
|
||
> *estimated* empty spool weights, not just unknown ones. If you meant unknown only, it's a
|
||
> one-line change and the badge drops to 3.
|
||
|
||
---
|
||
|
||
## 1b. Terminology: "empty spool weight", not "tare" (changed 2026-09-18)
|
||
|
||
"Tare" isn't how the 3D printing community talks, so every user-facing string
|
||
now says **empty spool weight**. The short form **empty spool** is used only
|
||
where space is tight: column headers, and the scale equation "262 g empty spool
|
||
+ 812 g filament = 1,074 g". Examples:
|
||
|
||
| Was | Now |
|
||
|---|---|
|
||
| Tare unverified (view) | Empty spool weight unverified |
|
||
| Empty spool (tare) | Empty spool / Empty spool weight |
|
||
| tare unknown | empty spool weight unknown |
|
||
| Set a tare · Save tare | Set empty spool weight · Save empty spool weight |
|
||
| Remaining = reading − 140 g tare | Remaining = reading − 140 g empty spool |
|
||
| Brand tare | Brand empty spool weight |
|
||
|
||
**Not changed:** code and API names (`tare_grams`, `tare_source`,
|
||
`effective_tare_grams`, `tare_origin`). Renaming those would be an API break for
|
||
no user benefit. CTXD's GLOSSARY.MD still defines "Tare"; I haven't edited CTXD
|
||
(read-only for this task). You may want to add "empty spool weight" there as
|
||
the user-facing term.
|
||
|
||
Iterations 1 and 2 still say "tare"; they're kept as snapshots.
|
||
|
||
---
|
||
|
||
## 2. New screens
|
||
|
||
| Screen | Who | What it's for | Built from on `dev` |
|
||
|---|---|---|---|
|
||
| **05 Lists** | operator+ | The 5 name-part lists + spool types. A name bar shows which part you're editing ("Sunlu **Rapid** PETG CF Silk"). "Used by N spools" links to Filament filtered. Remove is disabled with the reason when in use. Spool types show empty spool weight + trust level; adding one enforces "weight and source together, never 0 g". Demos: rename conflict (409), add error. | `ManageLookups.tsx`, `lookup` store rules |
|
||
| **06 Labels** | operator+ | Queue of spools without a label (preselected) → one PDF. Previews at the **real 51 × 21 mm layout** (19 mm QR · Brand / Color · taxonomy · #id). Tabs: all spools (reprint), holders (per-printer holder labels that start scan sessions). A format reference is included. | `labels.go`, `/api/spool-labels/unprinted`, `/api/filament-containers/{id}/label` |
|
||
| **07 Access** | admin | Authentik owns people and roles; Extrudex shows its local view. Role cards (reader / operator / admin, from the route policy). People with sessions (idle and hard expiry) and **Revoke…** with an inline confirm. **Machines & API tokens**: new token shown **once** (`exd_…`), per-token revoke. | `AccessAdministration.tsx`; machines/tokens have **API but no screen** on `dev` |
|
||
| **08 System** | admin | **Low-filament report**: schedule, threshold, weekly delivery history (retries, errors), **Run this week's now**. **Audit log**: who/what/when with links to the thing changed, filters, paging. **This device**: install, update-ready, offline readiness. | `webhook.go`, `audit.go`, `PWAControls.tsx`; webhook and audit have **API but no screen** on `dev` |
|
||
| **09 Scan: holder** (`/c/:id`) | phone-first | Scan a holder label → a session for this tab. Each spool scanned **loads** (from storage), **returns** (already here) or **moves here** (from another holder). No mode picker. Full → refused, nothing changes. Result card + haptic, a session log, "Done scanning". Camera sheet (demo) + type-a-number fallback. Reader view shows contents only. | `ScanSession.tsx`, `ScannedSpoolRoute` |
|
||
| **10 Scan: spool** (`/s/:id`) | phone-first | A spool label scanned with no holder session active: big remaining, **Lives in**, one-handed weigh-in (68 px input, before → after, the same two refusals), then open its holder or full details. Empty spool weight unknown → pick a spool type first. | `ScanWeighSpool.tsx` |
|
||
| **Printer panels** (on Printers) | admin | **Add / edit printer** (brand filters models from `printerCatalog.ts`; access code write-only; Elegoo has none), **Manage holders** (add AMS/AMS Lite, the external holder can't be removed, loaded holders can't be removed, print holder label), **Remove printer** (type the exact name). | `PrinterForm.tsx`, `ManagePrinterContainers.tsx`, `ConfirmRemovePrinter.tsx` |
|
||
| **Devices** | — | Every screen in 3 live frames, dark/light. | — |
|
||
|
||
The nav now reaches everything: **Manage** (Lists, Labels, Access, System) on
|
||
the rail, and a **More** tab on phones.
|
||
|
||
---
|
||
|
||
## 3. Things I checked against the code (and corrected in the design)
|
||
|
||
| Claim I almost made | What `dev` actually does | Design now says |
|
||
|---|---|---|
|
||
| "Run the report again resends it" | A delivered week returns its existing result; only a retrying week is retried (`runPeriod`) | "Already delivered Monday 08:00; nothing was sent again. Running it never sends a duplicate." |
|
||
| "Remove printer, and history is kept" | Also **refused while any holder still has spools**, and an open print is **settled as unknown** | Both shown in the remove panel; confirm disabled while loaded |
|
||
| Re-attributing a charged print is blocked | Allowed: grams are **returned** to the old spool and taken from the new one, *except* rows from before tracking (`ErrUnreconciledAttribution`) | "Change spool" with the exact effect, and the real refusal |
|
||
| Scanning a spool label always opens it | Only with no holder session active; otherwise it's loaded/moved into the active holder | Both routes designed; each page explains the other |
|
||
| Sample token `ctxd_…` | Extrudex tokens are `exd_…` | Fixed |
|
||
|
||
**Two findings for you** (not design changes):
|
||
1. **List names are case-sensitive in the schema** (`UNIQUE`, no `COLLATE
|
||
NOCASE`), so "petg" can be added beside "PETG", which is the very problem
|
||
the lists exist to prevent. The Lists screen shows a *proposed*
|
||
near-duplicate warning, labeled as a proposal.
|
||
2. **Audit log, low-filament report and machine tokens have APIs but no
|
||
screens on `dev`.** They're designed here and tagged "API exists · no screen
|
||
on dev yet".
|
||
|
||
---
|
||
|
||
## 4. Responsive specification
|
||
|
||
| | Phone < 700 px | Tablet 700–1180 px | Desktop > 1180 px |
|
||
|---|---|---|---|
|
||
| Navigation | Bottom bar: Dashboard · Filament · Prints · Printers · **More** (sheet with Manage + theme), badges on icons | **Compact rail** (88 px): icon + label, badges top-right, Manage group | Full rail with labels, Manage group, theme switch |
|
||
| Search | "Search…" pill, no shortcut hint | Full placeholder; shortcut hint hidden on touch | Full placeholder + ⌘K |
|
||
| Primary action | Floating **+** (56 px) above the bar | Header button | Header button |
|
||
| Panel | Full-screen sheet | 480 px side sheet over the page | 520 px side sheet |
|
||
| Tables | Low-priority columns dropped (`hide-m`); Lists become stacked rows | Tablet-only drops (`hide-t`): Value, Cost, Use; Weigh becomes icon-only | All columns |
|
||
| Touch | 44 px+ targets; menu items 12 px padding; `pointer: coarse` rules | Same `pointer: coarse` rules | Mouse sizes, keyboard hints |
|
||
| Scan pages | Designed for this: 64 px scan button, 68 px scale input, haptic on result | Centered 560 px column | Centered 560 px column |
|
||
| Dashboard | One column; attention buttons full-width | One column below 980 px, two above | Two columns |
|
||
| Printers | One card per row | 2 per row (portrait 1, landscape 2) | 3 per row |
|
||
|
||
---
|
||
|
||
## 5. New open questions
|
||
|
||
1. **Empty spool weight badge scope.** Unknown + estimated (as built), or unknown only? See §1.
|
||
2. **Near-duplicate list names.** Add a case-insensitive uniqueness check (a
|
||
schema change plus migration), or only warn in the UI?
|
||
3. **Holder scan session end.** `dev` keeps the active holder for the browser
|
||
session. Is an explicit **Done scanning** (as designed) wanted, and should it
|
||
also time out after a few minutes idle?
|
||
4. **Machine tokens, audit log, low-filament report.** Worth building screens
|
||
for all three now, or keep them API-only until something needs them?
|
||
5. **Label printing.** Always PDF download, or is there a specific label printer
|
||
you'd want a direct-print path for?
|
||
|
||
## 6. More implementation tasks (titles only, smallest first)
|
||
|
||
1. Rename the "Tare unknown" view to "Empty spool weight unverified" and include estimated weights
|
||
2. Add a Filament nav badge for unverified empty spool weights
|
||
3. Always run weigh sessions lightest first
|
||
4. Show the spool's location at the top of the weigh step
|
||
5. Add "Change spool" to attributed prints, stating the returned grams
|
||
6. Show the unreconciled-attribution refusal with its fix
|
||
7. Add a case-insensitive near-duplicate warning to Lists
|
||
8. Show "Used by N spools" with a link on every list value
|
||
9. Add label previews at 51 × 21 mm proportions
|
||
10. Add holder labels to the Labels screen
|
||
11. Add the "load … into …" palette command
|
||
12. Add the "attribute … to …" palette command
|
||
13. Add a More tab and sheet to the phone navigation
|
||
14. Add the compact tablet rail (700–1180 px)
|
||
15. Add a Manage holders panel with the removal rules
|
||
16. Add "Remove printer" with name confirmation and the loaded-holders refusal
|
||
17. Add a "Done scanning" control to holder scan sessions
|
||
18. Add the phone weigh-from-label layout
|
||
19. Add an audit log screen over `GET /api/admin/audit`
|
||
20. Add a low-filament report screen over the webhook endpoints
|
||
21. Add a machine and API token screen with show-once tokens
|