Files

248 lines
14 KiB
Markdown
Raw Permalink Normal View History

2026-09-19 11:31:50 -04:00
# Iteration 2: navigation, data display and interaction
2026-09-18. Builds on iteration 1 (`../screens/`, left unchanged for comparison).
Same Design System v3 tokens; the new components are in
`source/components.css`. Open **`screens/index.html`** first: it's the flow map,
with eight guided flows that deep-link into each state.
---
## 1. What was wrong with iteration 1
| # | Where | Problem | Why it matters | Iteration 2 fix |
|---|---|---|---|---|
| 1 | Nav, Dashboard | "Prints" and "All prints →" linked to `#`. Dead ends. | You lose trust in a nav item the first time it does nothing. | A real **Prints** page (history + attention queue, as on `dev`) |
| 2 | Filament → Edit | Edit navigated to a separate page | You lose your place, filter and scroll every time | **Detail panel** opens over the list; Esc/back returns you to where you were |
| 3 | Dashboard, Printers | A spool in "Running low" or an AMS slot wasn't clickable | The most natural next click went nowhere | Every spool, print and holder opens the same panel, on any page |
| 4 | Dashboard | Reported numbers but never said what to do | The kill criterion is **daily use**, so the home screen has to hand you today's jobs | **"Needs you"** queue, each item one click from its fix |
| 5 | Everywhere | Almost every value was an uppercase chip (status, provenance, warnings) | Nothing stands out when everything is loud, so a legend box was needed | Chip/label hierarchy (§5.1); provenance became quiet text with a tooltip |
| 6 | Filament table | Tare, source, gross, value, location and two actions in every row, 2–3 lines each | Too dense to scan 105 rows | **Stock / Weights** column sets; the full breakdown moves to the panel |
| 7 | All pages | No freshness, loading, empty or error state | The app polls every 10 s; stale data shown as live is the worst failure | "Live · updated 3s ago" in the header, an offline banner with the data's age, skeletons, empty states |
| 8 | Filament | "P1S · AMS 1 · **slot 2**" | **Invented.** The schema stores the holder, not the tray position | Holders show fill ("3 / 4") and contents; tray position listed under Future |
| 9 | Mobile | Nav squeezed into icon rows; no reachable primary action | Weighing happens at the shelf, on a phone | Bottom tab bar with badges, a floating **+** button, the panel as a full-screen sheet |
| 10 | Every page | A large proposal banner on top of every screen | Pushed content down; it read as part of the app | A small "Mockup" pill bottom-left, with the legend and demo controls inside |
| 11 | Printers | One card per section in three stacked sections | Two-thirds of the width empty; the lost printer was mid-page | Urgency order (lost → printing → idle) in one row, plus a "Jump to" strip |
| 12 | Printers ↔ Dashboard | "Open printer" landed at the top of the page | You had to find the right card yourself | `04-printers.html#printer=3` scrolls to that card and highlights it |
---
## 2. Navigation model
```
┌ Rail (desktop) / tab bar (phone) ─────────────────────────────┐
│ Dashboard · Filament · Prints [3] · Printers [1] │ ← badges = work waiting
│ Manage: Lists · Labels [4] · Access (admin) │ ← kept on dev, not redesigned
└───────────────────────────────────────────────────────────────┘
Top bar on every page: [ Search… ⌘K ] ● Live · updated 3s ago [◐]
```
- **Four destinations, ranked by how often they're used.** Manage items are
second-tier and grouped separately. Access is labeled "admin".
- **Badges only for work waiting**: Prints = prints missing a spool or weight
(amber); Printers = disconnected printers (red). No badge for totals, so a
badge always means "go here".
- **One page header pattern**: title, a one-line summary with the key numbers,
**one** primary button on the right, everything else under "More ▾".
- **Deep links for every state**, so browser Back, bookmarks and sharing all
work:
| URL | Opens |
|---|---|
| `02-filament.html?f=low&q=black` | Filament, Low view, filtered |
| `…#spool=47` · `#spool=47&tab=weigh` · `&tab=edit&state=conflict` | Spool panel, on a tab, optionally in a demo state |
| `03-prints.html?tab=needs` · `#print=210` | Prints tab / print panel |
| `04-printers.html#printer=3` · `#load=p3-lite` | Scroll to printer / load a holder |
| `?palette=weigh%20black` | Palette open with a query (demo hook) |
---
## 3. Core flows (most frequent first)
| Flow | Steps in iteration 2 | Iteration 1 |
|---|---|---|
| **Morning check** | Dashboard → "Needs you" → Review 3 → **Use** suggestion ×2, type 1 weight | Not possible; no queue |
| **Attribute a print** | Prints › Needs attention: suggestion shown inline → **Use**. Or batch-select → one spool | Not designed |
| **Weigh a spool** | Row **⚖ Weigh**, or palette `weigh copper` → type reading → see before → after → Save | Open the form, find the weigh box, save the whole form |
| **Weigh several** | Filament › Low → **Start weighing** → **Save & weigh next** (j/k also steps) | One at a time, reopening the form each time |
| **Load a spool** | Printers → holder **+ Load** → filter stored spools → pick → Load | Not designed |
| **Move / unload** | Panel › Move: storage or any holder; full holders disabled with the reason | Only by typing a location |
| **Add a spool** | **+ Add spool** (header, FAB, palette) → panel with 5-part name preview | Separate page |
| **Handle a 409** | Panel › Edit: side-by-side you-opened / now / your-edit → reapply or discard | Same content, but on a separate page |
| **Tare unknown** | Tare unknown view → Weigh tab explains, offers spool type or weigh-empty | Only a hint under the form field |
| **Find anything** | `/` or `⌘K` → spools, prints, printers and actions in one list | Filament search only |
---
## 4. Interaction patterns
**Panel (drawer) vs page.** Use a page for a *collection* and a panel for *one
thing*. The panel sits on the right (520 px) and becomes a full-screen sheet
under 860 px. Tabs: Overview · Weigh · Move · Edit for spools; a single view for
prints, with the fix at the top when something is missing.
- Opens with the heading focused (or the tab's main input), traps Tab, and
returns focus to the row that opened it.
- **Esc**, the ✕, clicking the backdrop or browser **Back** closes it.
- **↑ / ↓ buttons and j / k** step through the list it was opened from. The
footer's **Save & weigh next** does the same while weighing.
- **Unsaved changes**: closing Edit with changes shows an inline "Discard
unsaved changes?" bar, not a browser `confirm()`.
**Command palette.** `/` or `⌘K` from anywhere. Empty, it shows actions (Add
spool, Weigh a spool…, Prints needing attention, Switch theme) plus Go to.
Typing matches color, brand, `#id`, location, job name, gcode path or printer.
The `weigh ` prefix turns the list into "Weigh which spool?". ↑↓ ↵ Esc all work.
It uses the ARIA combobox + listbox pattern.
**Row actions.** A whole row is clickable and focusable (↵ opens it). The most
common action (**⚖ Weigh**) is visible on the row; the rest sit in a ⋯ menu,
which is always visible so touch and keyboard users can reach it. Destructive
items are last, after a separator, in red.
**Confirmation.** Only for destructive actions, and it says the consequence:
"Its print history stays… This can't be undone." Everything else saves
directly and confirms with a **toast** that names what changed ("Weighed Copper
· Sunlu: 97 g → 58 g"), with a **View** link. There's no fake Undo, because the
API has none.
**Batch.** Batch attribution follows `dev`'s AttributionQueue (select → attribute
to one spool). The weigh session is the batch pattern for weighing.
**Keyboard map.** `/` `⌘K` palette · `↵` open row · `Esc` close · `j` `k` next
and previous in the panel · `Tab` stays inside the panel while it's open.
---
## 5. Data display rules
### 5.1 Three visual tiers, used for nothing else
| Tier | Looks like | Used for | Examples |
|---|---|---|---|
| **State** | Colored dot/shape + bold word | What something *is doing* | ● Running · ● Finished · ◆ Unknown · ■ Failed · ○ Idle · ◆ Reconnecting |
| **Needs action** | Amber/red text with `!` or `⚠`, or an amber meter | Something *you* should do | `! no spool` · `⚠ 97 g` · `? tare unknown` |
| **Provenance** | Small gray icon + lowercase word, tooltip explains | *Where a number came from* | `✎ typed` · `▦ sliced` · `⎘ recalled` · `⚖ weighed bare` · `◇ community` · `≈ estimated` |
Each state has its own **shape** (dot, diamond, square, ring), so status never
relies on color alone. Chips (pills) remain only for view counts, filters and
the "Future, not built" marker.
### 5.2 Numbers
- Grams: `1,000 g` with thousands separators, always a space before the unit.
Totals over 1 kg: `95.72 kg`. Money: `$1,721.18`.
- Right-aligned tabular numbers in tables; the unit is never dropped.
- **Net is the default.** "Remaining" always means net filament. Gross appears
only with its tare beside it (`262 g tare + 812 g = 1,074 g`), so the three
can't be confused.
- **Unknown is written out** (`unknown`, `no duration`, `—` only for "not
applicable yet"). A missing tare is never 0.
- Durations: `3h 12m`. An upper bound is `≤ 3h 12m` in amber, with the gap
count beneath.
### 5.3 Time
Today and yesterday are relative words plus a clock time (`Today 08:37`,
`Yesterday 21:48`); older dates are `16 Sep 10:20`. Freshness is relative
(`updated 3s ago`, `last contact 6m ago`), because the question it answers is
"can I trust this?".
### 5.4 Meters
The remaining meter carries a **tick at the 200 g line**, so "how close to low"
reads without a number. The fill turns amber below it.
### 5.5 Progressive disclosure
| Where | Shows | Moves to the panel |
|---|---|---|
| Filament row | Spool, remaining meter, location *or* tare/gross (column set), value | Full tare/gross breakdown, source, spool type, label, version, prints from this spool |
| Print row | Job, state, duration, grams + source, spool, cost | Job key, why the source matters, per-color table, use type, archive |
| Printer card | State, now/last job, camera, holder contents | Connection details (collapsed) |
### 5.6 System states on every list
Loading → skeletons of the real layout (never a spinner over an empty page).
Empty → what's missing and one action ("Clear filters"). Stale → header dot
turns amber, and a banner gives the data's age and says saving is paused.
All-done → "✓ Nothing needs you right now."
---
## 6. Accessibility of the new pieces
- Panel: `role="dialog"`, `aria-modal`, labeled by its heading, focus trap, focus
returned on close.
- Palette: `role="combobox"` + `listbox` with `aria-activedescendant`.
- Tabs and segmented controls use `aria-selected`/`aria-pressed` **and** show a
raised fill plus bold text, not shadow alone.
- Sortable headers use `aria-sort` with a visible ▲/▼/↕.
- Meters use `role="meter"` with min, max and now.
- Toasts sit in an `aria-live="polite"` region. The offline banner is
`role="alert"`.
- Skip link, and focus rings on every interactive element (including rows).
- Motion (drawer slide, highlight flash, skeleton shimmer) turns off under
`prefers-reduced-motion`.
- All contrast pairs are unchanged from v3 (54/54 pass). No new colors were
introduced.
---
## 7. Things I fixed for honesty
- **Tray positions removed** (see §1 #8). The Move flow chooses a *holder*, and
full holders are disabled with the reason.
- **"Loaded filament: recorded by Extrudex, not read from the printer"** on
every printer card.
- **API gaps are labeled where they bite**: spool location history
(`spool_location_event` has no read endpoint) and weight source on
`/api/prints`.
- The weigh flow shows before → after and refuses, with the reason, a reading
below tare or above a full spool (the same two checks `dev` does).
## 8. Not redesigned in this iteration
Lists (taxonomy), Labels, Access administration, the Add/Edit **printer** form,
and the phone scan session (`/s/:id`, `/c/:id`). They keep their `dev` screens.
The nav links to them show a toast saying so.
## 9. New open questions
1. **Suggested spool acceptance.** On `dev` a suggestion is "not confirmed or
charged" until accepted. Iteration 2 puts **Use** one click away. Is one
click enough confirmation for a deduction, or should it ask?
1a. On click is fine as long as it's accurate. It can always be manually updated if it was an error.
2. **Weigh session order.** It follows the current list order (least remaining
first). Would shelf order be better? That needs location to be structured,
not free text.
2a. Weight would be best, but the location should also be visible, that way I can see the rolls that are lowest and use them first.
3. **Default landing page.** Dashboard, or straight to Prints › Needs attention
when it's non-empty?
3a. Dashboard
4. **Badge scope.** Should Filament get a badge for "tare unknown", or does that
become nagging, since most tares resolve from a spool type?
4a. Yes, this is good. It identifies an assumed weight and could be off.
5. **Palette actions.** Worth adding "Load ‹spool› into ‹holder›" and "Attribute
‹print› to ‹spool›" as typed commands?
5a. Makes sense to me.
## 10. Extra implementation tasks from iteration 2 (titles only, smallest first)
1. Add a freshness indicator driven by the last successful poll
2. Show an offline banner with the data's age when polling fails
3. Add a 200 g tick to the remaining meter
4. Replace provenance chips with icon + text labels and tooltips
5. Make spool rows and holder contents open a detail panel
6. Deep-link panel state in the URL (`#spool=`, `#print=`, `&tab=`)
7. Add Stock / Weights column sets to Filament
8. Add saved views with counts to Filament (All, Low, Loaded, In storage, Tare unknown)
9. Add a Weigh tab with a live before/after preview
10. Add a weigh session (Save & weigh next)
11. Add a Move tab that picks a holder, disabling full ones
12. Add "+ Load" on printer holders with a stored-spool picker
13. Add a Needs attention tab to Prints with inline suggestion acceptance
14. Add nav badges for prints needing attention and disconnected printers
15. Add a "Needs you" queue to the Dashboard
16. Add a ⌘K command palette over spools, prints, printers and actions
17. Add a mobile bottom tab bar and a floating Add button
18. Order printer cards by urgency and support `#printer=` scroll-to