initial commit

This commit is contained in:
cubecraft-agents[bot]
2026-04-25 18:51:05 +00:00
commit 230c3b295d
78 changed files with 8093 additions and 0 deletions

View File

@@ -0,0 +1,344 @@
# Filament Inventory List — Screen Specification
> **Screen ID:** FIL-001
> **Source of Truth:** [Material Design 3](https://m3.material.io/)
> **Tone:** Modern Industrial/Maker
> **Theme:** Dark Mode, High-Contrast
> **Last Updated:** 2026-04-20
---
## 1. Objective
Provide a single, scannable view of all filament spools in inventory. Users (shop operators) must be able to:
- Rapidly assess stock levels at a glance (which spools are low/critical).
- Search by material type, brand, color, or internal tracking ID.
- Filter by status (Available, In Use, Low Stock, Depleted).
- Navigate to spool detail or initiate the Smart Intake workflow.
This is the **primary landing screen** for both the kiosk and the mobile PWA.
---
## 2. Screen Inventory
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Top App Bar | `md-top-app-bar` (medium) | Title + search toggle + intake FAB |
| Search Bar | `md-search-bar` | Expandable on tap; collapses to icon on kiosk |
| Filter Chips | `md-chip-set` (filter) | Status filters: All / Available / In Use / Low / Depleted |
| Inventory List | `md-list` (3-line) | Spool cards as list items |
| Low-Stock Badge | `md-badge` | Tonal badge on list items |
| Extended FAB | `md-fab` (extended) | "Smart Intake" CTA |
| Navigation Rail | `md-navigation-rail` | Kiosk only — Inventory / Printers / Jobs / Settings |
| Bottom Navigation | `md-navigation-bar` | Mobile only — same destinations |
| Empty State | Illustration + `md-text-button` | When no spools match filter |
---
## 3. Layout Specification
### Title
**"Filament Inventory"** — displayed in the Top App Bar headline slot.
### Sections
#### A. Top App Bar (56dp mobile / 64dp kiosk)
- **Leading:** Menu icon (kiosk) / Back arrow (mobile, if deep-linked)
- **Title:** "Filament Inventory"
- **Trailing actions:** Search icon (toggles search bar), Overflow menu (Sort by, Export)
#### B. Search Bar (0dp collapsed → 56dp expanded)
- Triggered by search icon tap or pull-down gesture on mobile
- Placeholder: "Search spools, brands, colors…"
- Auto-focus on expand; dismiss on back/clear
- Real-time filtering as user types
#### C. Filter Chip Row (52dp height, horizontal scroll)
- Chips: `All` | `Available` | `In Use` | `Low Stock` | `Depleted`
- Default selection: `All`
- Multi-select disabled — single filter active at a time
- Chip styling: Tonal variants per status color
- Available: `green` tonal
- In Use: `blue` tonal (primary)
- Low Stock: `yellow` tonal (warning)
- Depleted: `red` tonal (error)
#### D. Inventory List (flex, scrollable)
- Each item is a **3-line list item** with leading/trailing elements:
```
┌──────────────────────────────────────────────────┐
│ [Color Swatch] PLA Basic - Matte Black │
│ Bambu Lab • Slot A3 • 842g left │
│ ▓▓▓▓▓▓▓▓▓▓░░ 67% [Low] badge │
├──────────────────────────────────────────────────┤
│ [Color Swatch] PETG Basic - Transparent │
│ Polymaker • Shelf B2 • 210g left │
│ ▓▓▓░░░░░░░░░ 21% [Critical] ⚠ │
└──────────────────────────────────────────────────┘
```
- **Leading:** Circular color swatch (40dp) — matches filament color
- **Line 1:** Material name (MaterialBase + Finish + Modifier) — `titleMedium`, `onSurface`
- **Line 2:** Brand • Location • Remaining weight — `bodyMedium`, `onSurfaceVariant`
- **Line 3:** Linear progress indicator + percentage text — `labelSmall`, `onSurfaceVariant`
- **Trailing:** Status badge (tonal chip) + chevron icon
- **Divider:** Full-width between items
**Sort order (default):** Low stock first, then alphabetical by material name.
#### E. Extended FAB (Bottom-end, mobile; Bottom-end, kiosk)
- Label: **"Smart Intake"** + `qr_code_scanner` icon
- Container: `primaryContainer` color
- On tap → navigates to Smart Intake Scan State
#### F. Navigation (Screen-level)
- **Kiosk:** Navigation Rail on leading edge (80dp wide)
- Destinations: Inventory (active), Printers, Jobs, Settings
- **Mobile:** Bottom Navigation Bar (80dp height)
- Same destinations
### Primary CTA
**Smart Intake** (Extended FAB) — always visible, anchored bottom-end.
### Secondary Actions
- Tap list item → Spool Detail View
- Overflow → Sort options (Name A-Z, Name Z-A, Weight Low→High, Weight High→Low, Recently Used)
- Overflow → Export inventory (CSV)
### Key Components
- Search bar with real-time filtering
- Filter chip set (single-select)
- 3-line list items with progress indicators
- Color swatch leading element
- Status tonal badges
- Extended FAB
### States
| State | Visual |
|-------|--------|
| **Loading** | `md-circular-progress` centered, list skeleton (shimmer) |
| **Empty (no spools)** | Spool icon illustration + "No spools in inventory" + "Add your first spool" text button |
| **Empty (no filter matches)** | "No spools match your filter" + Clear filter chip button |
| **Error** | Error illustration + "Couldn't load inventory" + Retry button |
| **Low Stock item** | Yellow tonal badge "Low" + progress bar < 25% yellow |
| **Critical item** | Red tonal badge "Critical" + progress bar < 10% red + pulsing dot |
| **Depleted item** | Red tonal badge "Depleted" + progress bar 0% + strikethrough title |
---
## 4. UX Rationale
1. **Progress bars beat numbers alone.** A visual progress indicator communicates remaining spool life faster than parsing "210g / 1000g" — critical in a busy workshop where decisions are made in seconds.
2. **Low stock first (default sort).** Operators need to see what's running out before what's plentiful. This prevents the "surprise empty spool" problem mid-print.
3. **Color swatches as leading elements.** Filament identification is primarily visual — operators scan for color before reading text. The swatch leverages pre-attentive processing.
4. **Single-select filters over multi-select.** Workshop operators don't construct complex queries. They want one quick filter tap. Multi-select adds cognitive load for minimal practical benefit.
5. **Smart Intake FAB, not a menu item.** Intake is the most frequent action (new spools arrive regularly). It deserves the highest-affordance control on screen — a FAB is unmissable.
6. **Search collapses by default.** On kiosk, screen space is precious. The search icon expands the bar only when needed, preserving vertical list space.
7. **Empty states with action.** An empty inventory list should immediately offer the path forward — "Add your first spool" links directly to Smart Intake.
---
## 5. Visual Direction
### Typography (MD3 Type Scale)
| Role | Token | Size | Weight | Line Height |
|------|-------|------|--------|-------------|
| App Bar Title | `titleLarge` | 22sp | 400 | 28sp |
| List Item Line 1 | `titleMedium` | 16sp | 500 | 24sp |
| List Item Line 2 | `bodyMedium` | 14sp | 400 | 20sp |
| List Item Line 3 | `labelSmall` | 11sp | 500 | 16sp |
| Filter Chip Text | `labelLarge` | 14sp | 500 | 20sp |
| FAB Label | `labelLarge` | 14sp | 500 | 20sp |
| Empty State Title | `headlineSmall` | 24sp | 400 | 32sp |
| Empty State Body | `bodyMedium` | 14sp | 400 | 20sp |
### Spacing (MD3 8dp grid)
| Element | Spacing |
|---------|---------|
| App Bar internal padding | 16dp horizontal, 12dp vertical |
| Filter chip row padding | 16dp horizontal, 8dp vertical |
| Chip gap | 8dp |
| List item padding | 16dp horizontal, 12dp vertical (top/bottom of 3-line) |
| List item internal gap | 16dp (leading to content), 16dp (content to trailing) |
| FAB margin | 16dp from edges |
| Content area padding | 0dp (list is edge-to-edge with dividers) |
### Color (MD3 Dark Theme — "Industrial Maker")
Based on a **blue-grey** primary seed for the industrial feel, with **teal** as secondary (maker accent).
| Role | Token | Value (Dark) | Usage |
|------|-------|-------------|-------|
| Background | `surface` | `#1C1B1F` | Screen background |
| On Background | `onSurface` | `#E6E1E5` | Primary text |
| Surface Variant | `surfaceContainer` | `#211F26` | App bar, list items |
| On Surface Variant | `onSurfaceVariant` | `#CAC4D0` | Secondary text, chip outlines |
| Primary | `primary` | `#A8CEDA` | FAB container, active states |
| On Primary | `onPrimary` | `#00303E` | FAB label text |
| Primary Container | `primaryContainer` | `#004D63` | FAB container (tonal), active chip fill |
| On Primary Container | `onPrimaryContainer` | `#A8CEDA` | Active chip text |
| Secondary | `secondary` | `#B1CCC7` | Navigation rail active icon |
| Tertiary | `tertiary` | `#EFB8C8` | Accent (not used here) |
| Error | `error` | `#F2B8B5` | Depleted badge, critical states |
| Error Container | `errorContainer` | `#8C1D18` | Critical badge background |
| On Error Container | `onErrorContainer` | `#F2B8B5` | Critical badge text |
| **Custom: Warning** | — | `#FFD580` | Low stock indicator |
| **Custom: Warning Container** | — | `#5D4200` | Low stock badge background |
| **Custom: Success** | — | `#8BD0A0` | Available badge text |
| **Custom: Success Container** | — | `#00522E` | Available badge background |
| Outline | `outline` | `#938F99` | Dividers, chip outlines |
| Outline Variant | `outlineVariant` | `#49454F` | Subtle borders |
### Color Swatches
Spool color swatches use the actual filament color as a circular filled element (40dp diameter) with a 2dp `outlineVariant` border for visibility on dark backgrounds. For transparent/clear filaments, use a diagonal hatch pattern fill.
---
## 6. Responsiveness
### Kiosk (800×480, Raspberry Pi 5 Touchscreen)
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ Filament Inventory [🔍] [⋮] │
│ RAIL │──────────────────────────────────────────────│
│ │ [All] [Available] [In Use] [Low] [Depleted] │
│ 📦 │──────────────────────────────────────────────│
│ 🖨️ │ ● PLA Basic - Matte Black │
│ 📋 │ Bambu Lab • Slot A3 • 842g │
│ ⚙️ │ ▓▓▓▓▓▓▓▓▓▓░░ 67% [Low] │
│ │──────────────────────────────────────────────│
│ │ ● PETG Basic - Transparent │
│ │ Polymaker • Shelf B2 • 210g │
│ │ ▓▓▓░░░░░░░░░ 21% [Critical] │
│ │──────────────────────────────────────────────│
│ │ ● ABS Basic - White │
│ │ eSUN • Slot B1 • 950g │
│ │ ▓▓▓▓▓▓▓▓▓▓▓▓ 95% [Available] │
│ │ [+ Smart │
│ │ Intake] ↗ │
└──────┴──────────────────────────────────────────────┘
```
- **Navigation Rail:** 80dp wide, pinned left
- **List area:** 720dp × 420dp usable
- **Visible items:** ~4-5 spools without scrolling
- **Touch targets:** All interactive elements ≥ 48dp (exceeds 44dp minimum for workshop glove use)
- **FAB:** Bottom-right, 16dp margin from rail and screen edge
### Mobile PWA (375×812, e.g., iPhone 14)
```
┌──────────────────────────────────┐
│ Filament Inventory [🔍] [⋮] │
│──────────────────────────────────│
│ [All] [Avail] [In Use] [Low] [×]│
│──────────────────────────────────│
│ ● PLA Basic - Matte Black │
│ Bambu Lab • Slot A3 • 842g │
│ ▓▓▓▓▓▓▓▓▓▓░░ 67% [Low] │
│──────────────────────────────────│
│ ● PETG Basic - Transparent │
│ Polymaker • Shelf B2 • 210g │
│ ▓▓▓░░░░░░░░░ 21% [Critical] │
│──────────────────────────────────│
│ ● ABS Basic - White │
│ eSUN • Slot B1 • 950g │
│ ▓▓▓▓▓▓▓▓▓▓▓▓ 95% [Avail] │
│──────────────────────────────────│
│ │
│ [+ Smart Intake] ↗ │
│──────────────────────────────────│
│ 📦 🖨️ 📋 ⚙️ │
│ Inventory Printers Jobs Settings │
└──────────────────────────────────┘
```
- **Bottom Navigation:** 80dp, pinned bottom
- **List area:** 375dp × ~600dp usable
- **Visible items:** ~5-6 spools without scrolling
- **FAB:** Floating above bottom nav, right-aligned
- **Filter chips:** Horizontally scrollable when more than screen width
### Key Adaptations
| Property | Kiosk (800×480) | Mobile (375×812) |
|----------|-----------------|-------------------|
| Navigation | Rail (left) | Bottom bar |
| Search | Collapsed icon by default | Same, but swipe-down gesture also expands |
| List item density | Comfortable (3-line, 72dp) | Same density |
| FAB position | Bottom-right, above list | Bottom-right, above nav bar |
| Filter chips | All visible at once | Horizontally scrollable |
| Sort access | Overflow menu | Overflow menu |
---
## 7. Developer Handoff Notes
### Angular Material Components
| UI Element | Angular Material Component | Notes |
|-----------|--------------------------|-------|
| Top App Bar | `<mat-toolbar>` | Use `@angular/material/toolbar`. Medium emphasis variant. |
| Search | Custom wrapper + `<input matInput>` | No native mat-search-bar in Angular Material; implement custom expandable search with `@ViewChild` animation. |
| Filter Chips | `<mat-chip-listbox>` | Use `mat-chip-option` with `selected` binding. Single-select: deselect others on select. |
| List Items | `<mat-list>` + `<mat-list-item>` | 3-line variant with `matListItemTitle`, `matListItemLine`, `matListItemMeta`. |
| Progress Bar | `<mat-progress-bar>` | Use `mode="determinate"` with `[value]` binding. Custom color classes for warning/error thresholds. |
| Badge | `<mat-badge>` | Overlay on list items. Use `matBadgeColor` for status colors. |
| Extended FAB | `<button mat-fab extended>` | Use `@angular/material/button`. `extended` attribute for label. |
| Nav Rail | `<mat-sidenav>` styled as rail | No native nav-rail in Angular Material yet; implement as styled sidenav with icon buttons. |
| Bottom Nav | `<mat-bottom-nav>` or custom | Use `<nav mat-tab-nav-bar>` positioned at bottom as a workaround. |
| Empty State | Custom component | Illustration (SVG) + `<button mat-button>`. |
| Loading | `<mat-spinner>` | Use `mode="indeterminate"` centered in list area. |
### Interaction Notes
1. **Search expansion:** Animate height from 0→56dp with `@angular/animations` (`expandCollapse` trigger, 200ms ease-out).
2. **Filter chip selection:** Single-select logic in component — on chip click, set `selectedChip = chip.value`, deselect all others.
3. **List item tap → navigation:** Use `routerLink="/spools/:id"` on each `mat-list-item`.
4. **FAB → Smart Intake:** Navigates to `/intake/scan`.
5. **Progress bar color:** Dynamically set based on percentage:
- ≥ 25%: `primary` (teal-blue)
- 10-24%: Custom `warn-yellow` class
- < 10%: `warn` (red)
6. **Skeleton loading:** Use `@angular/material` skeleton pattern or custom shimmer CSS animation on list items during initial load.
7. **Pull-to-refresh (mobile):** Implement with Angular CDK `@cdk/drag-drop` or a custom gesture handler to refresh inventory data.
8. **Infinite scroll / pagination:** Load 20 spools initially, load more on scroll-to-bottom.
### Accessibility
| Requirement | Implementation |
|-------------|---------------|
| Screen reader | Each list item: `aria-label="PLA Basic Matte Black, Bambu Lab, Slot A3, 842 grams remaining, 67 percent, Low stock"` |
| Color swatch | `aria-label="Color: Matte Black"` + `role="img"` |
| Progress bar | `aria-valuenow`, `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-label="Remaining filament: 67 percent"` |
| Filter chips | `role="listbox"`, each chip `role="option"` + `aria-selected` |
| Search | `role="search"`, `aria-label="Search filament inventory"` |
| FAB | `aria-label="Smart Intake: scan new spool"` |
| Keyboard nav | Tab through list items, Enter to select. Escape to dismiss search. |
| Focus management | On search expand, auto-focus input. On search collapse, return focus to search icon. |
| Motion reduction | Respect `prefers-reduced-motion` — disable shimmer, use opacity fade for loading. |
| Contrast | All text meets WCAG AA (4.5:1) against dark surface. Verified: `onSurface` (#E6E1E5) on `surface` (#1C1B1F) = 11.2:1 ✓ |
### SignalR Integration
- Subscribe to `SpoolUpdated` hub event on screen init.
- On event: update the corresponding list item in-place (no full refresh).
- On `SpoolAdded`: insert at sorted position with highlight animation (300ms `primaryContainer` background flash).
- On `SpoolDepleted`: move to Depleted filter group, show toast notification.
### Performance Notes
- Use `TrackByFunction` on `*ngFor` to prevent full list re-render on updates.
- Virtual scrolling via `<cdk-virtual-scroll-viewport>` if inventory exceeds 100 spools.
- Debounce search input at 300ms.

View File

@@ -0,0 +1,424 @@
# Spool Detail View — Screen Specification
> **Screen ID:** FIL-002
> **Source of Truth:** [Material Design 3](https://m3.material.io/)
> **Tone:** Modern Industrial/Maker
> **Theme:** Dark Mode, High-Contrast
> **Last Updated:** 2026-04-20
---
## 1. Objective
Present comprehensive, actionable information about a single filament spool. Users (shop operators) must be able to:
- View all spool metadata (material, brand, color, weight, location, dates).
- See real-time remaining weight with visual progress indicator.
- Manage QR code (view, reprint label).
- View consumption history / usage timeline.
- Perform quick actions: edit details, move location, mark depleted, initiate print.
- Navigate back to inventory or forward to related entities (printer, location).
This is the **drill-down screen** from the Filament Inventory List.
---
## 2. Screen Inventory
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Top App Bar | `md-top-app-bar` (small) | Back nav + title + actions |
| Hero Section | Custom card | Color swatch + material name + status badge |
| Progress Ring | `md-circular-progress` (determinate) | Circular remaining-weight indicator |
| Metrics Grid | CSS Grid in `md-card` | Key metrics in 2-col grid |
| QR Code Card | `md-card` (elevated) | QR display + reprint button |
| Location Section | `md-card` (outlined) | Current location + move action |
| Usage Timeline | `md-list` (2-line) | Recent consumption events |
| Action Row | `md-button` row | Primary + secondary actions |
| Snackbar | `md-snackbar` | Confirmation for destructive actions |
| Dialog | `md-dialog` | Move location, confirm depletion |
---
## 3. Layout Specification
### Title
**Spool detail name** — displayed in the Top App Bar headline slot (material short name, e.g., "PLA Basic — Matte Black").
### Sections
#### A. Top App Bar (64dp)
- **Leading:** Back arrow → returns to Inventory List
- **Title:** Material name (scrolls into collapsed bar on scroll)
- **Trailing actions:** Edit icon (pencil), More (overflow: Delete spool, Transfer data)
#### B. Hero Section (200dp mobile / 180dp kiosk)
A prominent visual header that establishes identity at a glance:
```
┌──────────────────────────────────────────────────┐
│ │
│ ┌──────┐ │
│ │ COLOR│ PLA Basic — Matte Black │
│ │SWATCH│ Bambu Lab │
│ │ 80dp │ │
│ └──────┘ [Available] │
│ │
│ ◉ 842g / 1000g │
│ 67% remaining │
│ │
└──────────────────────────────────────────────────┘
```
- **Left:** Large circular color swatch (80dp) with `outlineVariant` border
- **Right of swatch:**
- Line 1: Full material name (`headlineMedium`) — MaterialBase + Finish + Modifier
- Line 2: Brand name (`bodyLarge`, `onSurfaceVariant`)
- **Below swatch row:** Status badge (tonal chip, same as inventory)
- **Bottom:** Large circular progress indicator (96dp ring)
- Track: `surfaceContainerHighest`
- Indicator: Dynamic color (≥25% primary, 10-24% warning, <10% error)
- Center text: Remaining weight + percentage (`headlineSmall`)
#### C. Metrics Grid (2-column)
```
┌────────────────────┬────────────────────┐
│ Total Weight │ Remaining │
│ 1,000g │ 842g (67%) │
├────────────────────┼────────────────────┤
│ Diameter │ Density │
│ 1.75mm │ 1.24 g/cm³ │
├────────────────────┼────────────────────┤
│ Date Added │ Last Used │
│ Mar 12, 2026 │ Apr 18, 2026 │
└────────────────────┴────────────────────┘
```
- Each cell: Label (`labelMedium`, `onSurfaceVariant`) + Value (`titleMedium`, `onSurface`)
- Grid gap: 1dp `outlineVariant` lines between cells
- Container: `surfaceContainer` background, `rounded-xl` (16dp radius)
- Cell padding: 16dp
#### D. Location Card (Outlined, 72dp)
```
┌──────────────────────────────────────────────────┐
│ 📍 AMS Unit 2, Slot A3 [Move]│
│ Bambu Lab P1S — Left spool holder │
└──────────────────────────────────────────────────┘
```
- **Leading:** Location pin icon
- **Line 1:** Location name (`titleSmall`, `onSurface`)
- **Line 2:** Printer/host description (`bodySmall`, `onSurfaceVariant`)
- **Trailing:** "Move" text button (`primary`) → opens Move Location dialog
#### E. QR Code Card (Elevated, 200dp × 200dp QR)
```
┌──────────────────────────────────────────────────┐
│ ┌──────────────┐ │
│ │ │ │
│ │ QR CODE │ │
│ │ (160dp) │ │
│ │ │ │
│ └──────────────┘ │
│ EXT-2026-PLA-0042 │
│ [Reprint] │
└──────────────────────────────────────────────────┘
```
- QR code: 160dp × 160dp, white on dark surface
- Below QR: Internal tracking ID in monospace (`labelMedium`, `onSurfaceVariant`)
- Trailing: "Reprint" text button → sends to Bluetooth thermal printer
- Card: `surfaceContainerHigh` background, elevation 1
#### F. Usage Timeline (Expandable section)
```
│ Recent Usage │
│──────────────────────────────────────────────────│
│ 📦 Print #1847 — Benchy v3 -32g Apr 18│
│ 📦 Print #1842 — Housing Cap -18g Apr 17│
│ 📦 Print #1839 — Gear Test -8g Apr 16│
│ 📦 Added to inventory +1000g Mar 12│
│ │
│ [View Full History →] │
└───────────────────────────────────────────────────┘
```
- Each entry: 2-line list item
- Line 1: Print name or event (`bodyMedium`, `onSurface`)
- Line 2: Weight change + date (`bodySmall`, `onSurfaceVariant`)
- Negative weight: `error` color
- Positive weight: custom `success` color
- Trailing: "View Full History" link → separate screen (future)
#### G. Action Row (Sticky bottom, 64dp)
```
┌──────────────────────────────────────────────────┐
│ [Mark Depleted] [Move Location] [Print] │
└──────────────────────────────────────────────────┘
```
- **Mark Depleted:** Outlined button, `error` color → confirmation dialog
- **Move Location:** Tonal button, `secondary` → Move Location dialog
- **Print:** Filled button, `primary` → Navigate to start print job (pre-select this spool)
On kiosk, these are larger touch targets (56dp height buttons).
### Primary CTA
**Print** (filled button) — the most likely next action after viewing spool details.
### Secondary Actions
- Edit (Top App Bar icon) → Edit spool details form
- Reprint label → Send QR to thermal printer
- Move Location → Dialog with location picker
- Mark Depleted → Confirmation dialog → status update
- Delete spool → Overflow menu → confirmation dialog
### Key Components
- Large circular color swatch
- Determinate circular progress indicator
- 2-column metrics grid
- QR code display card
- Location card with move action
- Usage timeline list
- Sticky bottom action row
### States
| State | Visual |
|-------|--------|
| **Loading** | `md-circular-progress` indeterminate centered, skeleton sections |
| **Available (normal)** | Primary color progress ring, "Available" badge |
| **In Use** | Primary color progress ring, "In Use" badge, pulsing dot |
| **Low Stock** | Warning (yellow) progress ring, "Low Stock" badge |
| **Critical** | Error (red) progress ring, "Critical" badge, pulsing dot |
| **Depleted** | Error (red) progress ring at 0%, "Depleted" badge, strikethrough weight, "Mark Depleted" button becomes "Reactivate" |
| **No Location** | Location card shows "Unassigned" + "Assign" button |
| **QR Missing** | QR card shows "No QR generated" + "Generate" button |
| **Move Dialog** | Bottom sheet (mobile) / Center dialog (kiosk) with location tree/grid selector |
---
## 4. UX Rationale
1. **Hero section for instant recognition.** When an operator scans a spool or taps into it, they need immediate visual confirmation: "Is this the spool I think it is?" The large color swatch + material name + brand answers this in <1 second.
2. **Circular progress over linear.** For a single spool, a circular progress ring is more impactful and glanceable than a linear bar. It works as a "fuel gauge" metaphor that operators intuitively understand.
3. **Metrics grid, not a list.** A 2-column grid presents data-dense information efficiently. Each metric is a quick scan without vertical scrolling. This is the "dashboard gauge" pattern adapted for a single entity.
4. **QR card always visible.** The QR code is the physical-digital bridge. Operators need to see it to verify labels match, and the reprint action is frequent (labels get damaged/lost on spools).
5. **Timeline for consumption context.** "How fast am I going through this spool?" The recent usage list answers this without requiring a separate analytics screen. It's contextual, not analytical.
6. **Sticky action row.** The three most common actions (deplete, move, print) should never be hidden by scroll. Pinning them ensures they're always one tap away.
7. **"Mark Depleted" is destructive but necessary.** It gets an outlined (not filled) style and requires a confirmation dialog to prevent accidental taps — but it's prominent because operators genuinely need this action often.
---
## 5. Visual Direction
### Typography (MD3 Type Scale)
| Role | Token | Size | Weight | Line Height |
|------|-------|------|--------|-------------|
| App Bar Title | `titleLarge` | 22sp | 400 | 28sp |
| Hero Material Name | `headlineMedium` | 28sp | 400 | 36sp |
| Hero Brand | `bodyLarge` | 16sp | 400 | 24sp |
| Progress Center Value | `headlineSmall` | 24sp | 400 | 32sp |
| Progress Center Sub | `labelMedium` | 12sp | 500 | 16sp |
| Metrics Label | `labelMedium` | 12sp | 500 | 16sp |
| Metrics Value | `titleMedium` | 16sp | 500 | 24sp |
| Card Title | `titleSmall` | 14sp | 500 | 20sp |
| Card Body | `bodyMedium` | 14sp | 400 | 20sp |
| Timeline Entry | `bodyMedium` | 14sp | 400 | 20sp |
| Timeline Detail | `bodySmall` | 12sp | 400 | 16sp |
| Action Button | `labelLarge` | 14sp | 500 | 20sp |
| Tracking ID | `labelMedium` (monospace) | 12sp | 500 | 16sp |
### Spacing (MD3 8dp grid)
| Element | Spacing |
|---------|---------|
| Hero section padding | 24dp horizontal, 20dp vertical |
| Swatch to text gap | 16dp |
| Hero to metrics gap | 16dp |
| Metrics grid cell padding | 16dp |
| Metrics grid gap (divider) | 1dp |
| Section gap (between cards) | 12dp |
| Card internal padding | 16dp |
| QR code padding | 24dp (centered) |
| Timeline item padding | 16dp horizontal, 12dp vertical |
| Action row padding | 16dp horizontal, 12dp vertical |
| Action button gap | 12dp |
### Color (MD3 Dark Theme — "Industrial Maker")
Same base palette as FIL-001. Screen-specific additions:
| Role | Token | Value (Dark) | Usage |
|------|-------|-------------|-------|
| Hero Background | `surfaceContainerLow` | `#1D1B20` | Hero section fill |
| Metrics Background | `surfaceContainer` | `#211F26` | Grid card |
| QR Card Background | `surfaceContainerHigh` | `#2B2930` | Elevated card |
| Location Card Background | `surfaceContainer` | `#211F26` | Outlined card |
| Progress Track | `surfaceContainerHighest` | `#36343B` | Ring background track |
| Depleted Strikethrough | `error` | `#F2B8B5` | Weight text decoration |
### Circular Progress Colors (Dynamic)
| Threshold | Color Token | Visual |
|-----------|------------|--------|
| ≥ 25% | `primary` | `#A8CEDA` (teal-blue) |
| 1024% | Custom `warning` | `#FFD580` (amber) |
| < 10% | `error` | `#F2B8B5` (red) |
| 0% (Depleted) | `error` | `#F2B8B5` + pulsing animation |
---
## 6. Responsiveness
### Kiosk (800×480)
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ ← PLA Basic — Matte Black [✏] [⋮] │
│ RAIL │──────────────────────────────────────────────│
│ │ ┌──────┐ │
│ │ │SWATCH│ PLA Basic — Matte Black [Avail] │
│ │ │ 80dp │ Bambu Lab │
│ │ └──────┘ │
│ │ ◉ 842g / 1000g (67%) │
│ │──────────────────────────────────────────────│
│ │ Total: 1000g │ Remaining: 842g │ Diam: 1.75│
│ │ Dens: 1.24 │ Added: Mar 12 │ Last: Apr18│
│ │──────────────────────────────────────────────│
│ │ 📍 AMS Unit 2, Slot A3 [Move] │
│ │──────────────────────────────────────────────│
│ │ [Depleted] [Move Location] [Print] │
└──────┴──────────────────────────────────────────────┘
```
- **Horizontal layout advantage:** Metrics grid uses 3 columns on kiosk to fill width.
- **QR code and timeline:** Accessible via scroll or tab section below the fold.
- **Scrollable content area:** Hero + Metrics + Location fit above fold; QR + Timeline scroll into view.
- **Action row:** Sticky at bottom of scroll area, above Navigation Rail.
### Mobile PWA (375×812)
```
┌──────────────────────────────────┐
│ ← PLA Basic — Matte Black [✏] │
│──────────────────────────────────│
│ ┌──────┐ │
│ │SWATCH│ PLA Basic — Matte │
│ │ 80dp │ Black [Avail] │
│ └──────┘ Bambu Lab │
│ │
│ ◉ 842g / 1000g (67%) │
│──────────────────────────────────│
│ Total Weight │ Remaining │
│ 1,000g │ 842g (67%) │
│──────────────────────────────────│
│ Diameter │ Density │
│ 1.75mm │ 1.24 g/cm³ │
│──────────────────────────────────│
│ Date Added │ Last Used │
│ Mar 12, 2026 │ Apr 18, 2026 │
│──────────────────────────────────│
│ 📍 AMS Unit 2, Slot A3 [Move] │
│──────────────────────────────────│
│ ┌──────────┐ │
│ │ QR CODE │ │
│ │ 160dp │ EXT-2026-PLA-0042│
│ └──────────┘ [Reprint] │
│──────────────────────────────────│
│ Recent Usage │
│ Print #1847 — Benchy -32g │
│ Print #1842 — Housing -18g │
│ [View Full History →] │
│──────────────────────────────────│
│ [Depleted] [Move] [Print] │
│──────────────────────────────────│
│ 📦 🖨️ 📋 ⚙️ │
└──────────────────────────────────┘
```
- **Vertical scroll layout:** All sections stack vertically.
- **Metrics grid:** 2 columns on mobile (3 on kiosk).
- **Action row:** Sticky at bottom, above bottom navigation.
- **QR code:** Full-width card, scroll into view.
### Key Adaptations
| Property | Kiosk (800×480) | Mobile (375×812) |
|----------|-----------------|-------------------|
| Metrics columns | 3 (compact) | 2 (standard) |
| Hero section height | 180dp (compact) | 200dp (standard) |
| QR card position | Below fold, scroll | Below fold, scroll |
| Action row style | Larger buttons (56dp h) | Standard buttons (40dp h) |
| Timeline items visible | 2-3 | 3-4 |
| Move dialog | Center `mat-dialog` | Bottom sheet (`mat-bottom-sheet`) |
---
## 7. Developer Handoff Notes
### Angular Material Components
| UI Element | Angular Material Component | Notes |
|-----------|--------------------------|-------|
| Top App Bar | `<mat-toolbar>` | Collapsible title on scroll using `@angular/cdk/scrolling` |
| Color Swatch | Custom `div` | Circular, 80dp, `[style.backgroundColor]` binding |
| Circular Progress | `<mat-progress-spinner>` | `mode="determinate"`, `[value]` binding, custom color classes |
| Metrics Grid | CSS Grid inside `<mat-card>` | 2-col (mobile) / 3-col (kiosk) via CSS breakpoints |
| QR Code Card | `<mat-card>` elevated | Use `appearance="elevated"`. QR rendered via `qrcode` npm package or canvas |
| Location Card | `<mat-card>` outlined | Use `appearance="outlined"`. |
| Timeline | `<mat-list>` + `<mat-list-item>` | 2-line items with `matListItemIcon` for type icon |
| Action Buttons | `<button mat-button>` variants | Depleted: `mat-button` with `color="warn"`. Move: `mat-flat-button` with `color="accent"`. Print: `mat-raised-button` with `color="primary"` |
| Move Dialog | `<mat-dialog>` / `<mat-bottom-sheet>` | Responsive: dialog on kiosk, bottom sheet on mobile |
| Confirmation Dialog | `<mat-dialog>` | With `mat-dialog-actions`: Cancel + Confirm |
| Snackbar | `<mat-snack-bar>` | For success/error feedback on actions |
### Interaction Notes
1. **Collapsible title:** As user scrolls past hero, App Bar title animates from hidden to visible (standard M3 scroll behavior). Use `@angular/material` scroll dispatching.
2. **Progress ring animation:** On load, animate from 0 to actual value over 600ms with `ease-out`.
3. **QR code generation:** If `spool.qrCode` is null, show "Generate" button. On tap, call API to generate QR, then display.
4. **Reprint label:** Calls Bluetooth thermal printer service. Show snackbar: "Label sent to printer ✓" or "Printer not connected ✗".
5. **Move location dialog:** Show location tree (AMS units → slots, shelves). Selected location highlighted. Confirm → API call → snackbar confirmation.
6. **Mark Depleted:** Dialog: "Mark PLA Basic — Matte Black as depleted? This will update the spool's status and remove it from available inventory." Buttons: Cancel (text) / Mark Depleted (error filled).
7. **Delete spool:** Dialog: "Permanently delete PLA Basic — Matte Black? This cannot be undone." Buttons: Cancel (text) / Delete (error filled).
8. **Real-time updates:** Subscribe to `SpoolUpdated` SignalR event. If this spool's weight changes, animate the progress ring and update metrics in-place.
### Accessibility
| Requirement | Implementation |
|-------------|---------------|
| Screen reader (hero) | `aria-label="PLA Basic Matte Black by Bambu Lab, Available, 842 grams remaining of 1000 grams, 67 percent"` |
| Circular progress | `role="progressbar"`, `aria-valuenow="67"`, `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-label="Remaining filament"` |
| Metrics grid | Semantic `<dl>` with `<dt>` (label) + `<dd>` (value), each with `aria-label` |
| QR code | `alt="QR code for spool EXT-2026-PLA-0042"`, `role="img"` |
| Color swatch | `aria-label="Filament color: Matte Black"`, `role="img"` |
| Action buttons | Clear `aria-label`: "Mark as depleted", "Move to new location", "Start print with this spool" |
| Confirmation dialogs | `aria-labelledby` for title, `aria-describedby` for description. Focus trap. Escape to close. |
| Keyboard nav | Tab order: Back → Edit → Overflow → Content sections → Action row. Enter to activate. |
| Focus on open | When navigating from inventory, focus lands on back button, then natural tab flow |
### SignalR Integration
- Subscribe to `SpoolUpdated` hub event, filter by current spool ID.
- On weight change: animate progress ring delta, update metrics, prepend usage timeline entry.
- On status change: update badge, adjust progress ring color, update action row buttons.
- On location change: update location card.
### Performance Notes
- Use `OnPush` change detection strategy for this component.
- QR code should be generated once and cached; don't regenerate on every change detection cycle.
- Usage timeline: load last 5 entries on init, lazy-load full history on "View Full History" tap.

View File

@@ -0,0 +1,738 @@
# Smart Intake Workflow — Screen Specification
> **Screen ID:** FIL-003
> **Source of Truth:** [Material Design 3](https://m3.material.io/)
> **Tone:** Modern Industrial/Maker
> **Theme:** Dark Mode, High-Contrast
> **Last Updated:** 2026-04-20
---
## 1. Objective
Design the primary filament intake experience — the "Smart Intake" workflow — optimized for both the kiosk (USB barcode scanner + touchscreen) and the mobile PWA (device camera + touch). This is the **most critical user flow** in Extrudex: every new spool enters the system through this workflow.
The workflow follows a strict **3-state linear progression**:
```
SCAN ──→ IDENTIFY ──→ UPDATE ──→ ✓ Complete
```
Users must be able to:
- **Scan:** Capture a barcode/QR code via camera (mobile) or USB scanner (kiosk).
- **Identify:** Confirm or correct the scanned spool's identity (material, brand, color).
- **Update:** Set initial weight and status, assign a location, and complete intake.
The workflow must feel **fast, confident, and forgiving** — operators process many spools in a session and can't afford friction or ambiguity at any step.
---
## 2. Screen Inventory
### Shared Across All States
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Top App Bar | `md-top-app-bar` (small) | Title + close/dismiss |
| Step Indicator | Custom linear stepper | 3 dots/steps with active state |
| Bottom Sheet | `md-bottom-sheet` | Contextual info/errors on mobile |
### Scan State (State 1 of 3)
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Camera Viewport | Custom + `video` element | Full-bleed camera with scanning overlay |
| Viewport Frame | Custom overlay | Animated scanning rectangle |
| Scan Result Chip | `md-chip` (assist) | Shows last scanned code |
| Manual Entry Link | `md-text-button` | Fallback for unreadable codes |
| USB Scanner Badge | `md-chip` (status) | Kiosk only — "Scanner Connected ✓" |
### Identification State (State 2 of 3)
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Spool Identity Card | `md-card` (filled) | Material, brand, color display |
| Confidence Indicator | Custom bar | Match confidence (High/Medium/Low/Unknown) |
| Material Selector | `md-dropdown-select` | If multiple matches or no match |
| Brand Selector | `md-dropdown-select` | Brand selection |
| Color Picker | Custom swatch grid | Filament color selection |
| Finish + Modifier | `md-chip-set` (choice) | Material finish and modifier |
| "Confirm" Button | `md-filled-button` | Primary CTA — proceed to Update |
| "Rescan" Button | `md-outlined-button` | Go back to Scan state |
### Update State (State 3 of 3)
| Element | MD3 Component | Notes |
|---------|--------------|-------|
| Weight Input | `md-filled-text-field` + stepper | Grams input with +/- buttons |
| Location Selector | `md-dropdown-select` | Assign to AMS slot or shelf |
| Status Toggle | `md-chip-set` (choice) | Available / In Use |
| QR Preview | `md-card` | Generated QR code preview |
| "Complete Intake" Button | `md-filled-button` | Primary CTA — finalize |
| "Print Label" Toggle | `md-switch` | Auto-print QR label on completion |
| Summary Card | `md-card` (outlined) | Final summary before submission |
---
## 3. Layout Specification
### Shared Layout
#### Top App Bar (56dp mobile / 64dp kiosk)
- **Leading:** Close (✕) → returns to Inventory List with unsaved-work confirmation if mid-flow
- **Title:** "Smart Intake" (all states)
- **Trailing:** None (deliberate — no distractions during intake)
#### Step Indicator (40dp, below App Bar)
```
● ───── ○ ───── ○
Scan Identify Update
```
- 3-step linear indicator
- Active step: `primary` filled circle + `primary` connecting line
- Completed step: `primary` filled circle + checkmark icon
- Future step: `outline` circle + `outlineVariant` connecting line
- Labels: `labelSmall` below each dot
- Animation: Line fills with `primary` color as each step completes (300ms ease-out)
---
### STATE 1: SCAN
**Title:** "Scan Spool"
#### Layout — Mobile (Camera-based)
```
┌──────────────────────────────────┐
│ ✕ Smart Intake │
│──────────────────────────────────│
│ ● ──── ○ ──── ○ │
│ Scan Identify Update │
│──────────────────────────────────│
│ │
│ ┌──────────────────────────┐ │
│ │ │ │
│ │ ┌────────────────┐ │ │
│ │ │ ╔════════════╗│ │ │
│ │ │ ║ VIEWPORT ║│ │ │
│ │ │ ║ FRAME ║│ │ │
│ │ │ ║ (animated) ║│ │ │
│ │ │ ╚════════════╝│ │ │
│ │ │ │ │ │
│ │ │ ▲ Align barcode │ │ │
│ │ │ within frame │ │ │
│ │ └────────────────┘ │ │
│ │ │ │
│ │ LIVE CAMERA FEED │ │
│ │ │ │
│ └──────────────────────────┘ │
│ │
│ [Last scan: 8901234567890] │
│ │
│ Can't scan? Enter manually │
│ │
└──────────────────────────────────┘
```
- **Camera viewport:** Full-bleed video feed with 16:9 or 4:3 aspect ratio
- **Scanning frame:** Animated rectangle (200dp × 120dp) centered in viewport
- Corner brackets: `primary` color, 24dp corner length, 3dp stroke
- Scan line: Horizontal `primary` line that sweeps top-to-bottom repeatedly (2s cycle)
- On successful scan: Frame flashes `success` color (green) + brief haptic (mobile)
- **Instruction text:** Below frame, `bodyMedium`, `onSurfaceVariant`
- **Scan result chip:** Appears below camera when a code is detected
- Shows scanned code value in monospace
- Auto-advances to Identification state after 1.5s (with option to cancel)
- **Manual entry link:** Text button below camera area for fallback
#### Layout — Kiosk (USB Scanner-based)
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ ✕ Smart Intake │
│ RAIL │──────────────────────────────────────────────│
│ │ ● ──── ○ ──── ○ │
│ │ Scan Identify Update │
│ │──────────────────────────────────────────────│
│ │ │
│ │ ┌──────────────────────┐ │
│ │ │ │ │
│ │ │ 📷 SCANNER READY │ │
│ │ │ │ │
│ │ │ USB Scanner: ✓ │ │
│ │ │ │ │
│ │ │ Scan a barcode or │ │
│ │ │ QR code now... │ │
│ │ │ │ │
│ │ │ ═══════════════ │ │
│ │ │ (pulse animation) │ │
│ │ │ │ │
│ │ └──────────────────────┘ │
│ │ │
│ │ Or: [Enter Barcode Manually] │
│ │ │
└──────┴──────────────────────────────────────────────┘
```
- **No camera on kiosk** — USB HID barcode scanner is the primary input
- **Scanner status card:** Shows connected/disconnected state
- Connected: `success` icon + "Scanner Connected ✓"
- Disconnected: `error` icon + "Scanner Not Found" + "Troubleshoot" button
- **Pulse animation:** A horizontal line that pulses `primary` to indicate "waiting for scan"
- **On scan:** The scanned code appears in a chip, auto-advances to Identification
- **Manual entry:** Full keyboard (virtual) for typing barcode numbers
#### Scan State Elements
| Element | Description |
|---------|-------------|
| Primary CTA | None (scanning is automatic) — the camera/scanner IS the CTA |
| Secondary Actions | Manual entry, close/dismiss |
| Key Components | Camera viewport, scanning frame animation, scanner status (kiosk), scan result chip |
| States | See below |
| State | Visual |
|-------|--------|
| **Initializing camera** | Shimmer loading in viewport + "Starting camera…" label |
| **Ready (waiting for scan)** | Live feed + animated scan line + "Align barcode within frame" |
| **Scanning (code detected)** | Frame flashes green + scan result chip appears + "Identified!" text |
| **No match found** | Frame flashes yellow + chip + "Unknown barcode" → still advances to Identify (manual) |
| **Camera denied** | Error card: "Camera access denied" + "Enter manually" button + permission settings link |
| **Scanner disconnected (kiosk)** | Error card: "Scanner not detected" + "Reconnect scanner" + "Enter manually" |
| **Multiple codes detected** | Shows list of detected codes as selectable chips → user picks correct one |
---
### STATE 2: IDENTIFY
**Title:** "Identify Spool"
#### Layout (Both platforms)
```
┌──────────────────────────────────┐
│ ✕ Smart Intake │
│──────────────────────────────────│
│ ✓ ────● ──── ○ │
│ Scan Identify Update │
│──────────────────────────────────│
│ │
│ ┌──────────────────────────┐ │
│ │ Scanned: 8901234567890 │ │
│ │ Match: HIGH CONFIDENCE │ │
│ │ ▓▓▓▓▓▓▓▓▓▓▓▓░░ 92% │ │
│ └──────────────────────────┘ │
│ │
│ Material │
│ ┌──────────────────────────┐ │
│ │ PLA Basic ▾│ │
│ └──────────────────────────┘ │
│ │
│ Brand │
│ ┌──────────────────────────┐ │
│ │ Bambu Lab ▾│ │
│ └──────────────────────────┘ │
│ │
│ Color │
│ ● ● ● ● ● ● ● ● ● [Custom] │
│ Bla Red Blu Grn Yel Org Pur Wh │
│ │
│ Finish Modifier │
│ [Basic][Matte][Silk] [None] │
│ │
│ [Rescan] [Confirm →] │
│ │
└──────────────────────────────────┘
```
#### Confidence Indicator
A horizontal bar showing match confidence from the barcode lookup:
| Confidence | Visual | Color | Behavior |
|-----------|--------|-------|----------|
| **High** (≥80%) | Full green bar | `success` | Pre-fills all fields; user just confirms |
| **Medium** (40-79%) | Yellow bar | `warning` | Pre-fills material/brand; color may need correction |
| **Low** (<40%) | Orange bar | Custom orange | Pre-fills partial; more fields need manual input |
| **Unknown** (0%) | Red bar | `error` | "Barcode not found" — all fields manual |
#### Material Selector
- `mat-select` dropdown with searchable options
- Options sourced from normalized taxonomy (MaterialBase list)
- If confidence is High: pre-selected, field is in confirmed state (slight green tint)
- If confidence is Low/Unknown: field is empty, highlighted for input
#### Brand Selector
- `mat-select` dropdown with common brands
- Searchable with "Other…" option that allows free-text entry
#### Color Picker
- **Swatch grid:** 2 rows of circular color swatches (24dp each)
- Common colors: Black, White, Red, Blue, Green, Yellow, Orange, Purple, Grey, Brown, Pink, Transparent
- Tap swatch → selected (ring indicator)
- "Custom" option → opens free-text color name input
- Selected color stored as both name + hex value
#### Finish + Modifier Chips
- **Finish (required):** `mat-chip-listbox` with single-select
- Options: Basic, Matte, Silk, Sparkle, Metallic, Translucent
- Default: "Basic"
- **Modifier (optional):** `mat-chip-listbox` with single-select
- Options: None, Carbon Fiber, Wood Fill, Glow-in-Dark, Marble, Gradient
- Default: "None"
#### Identify State Elements
| Element | Description |
|---------|-------------|
| Primary CTA | **"Confirm"** filled button → advances to Update state |
| Secondary Actions | **"Rescan"** outlined button → returns to Scan state |
| Key Components | Confidence bar, Material dropdown, Brand dropdown, Color picker grid, Finish chips, Modifier chips |
| Validation | Material and Brand are required. Color is required. Confirm button disabled until all required fields filled. |
---
### STATE 3: UPDATE
**Title:** "Update Spool"
#### Layout (Both platforms)
```
┌──────────────────────────────────┐
│ ✕ Smart Intake │
│──────────────────────────────────│
│ ✓ ────✓ ──── ● │
│ Scan Identify Update │
│──────────────────────────────────│
│ │
│ Initial Weight │
│ ┌──────────────────────────┐ │
│ │ [-] 1000 [+] grams │ │
│ └──────────────────────────┘ │
│ Quick: [250] [500] [750] [1000] │
│ │
│ Assign Location │
│ ┌──────────────────────────┐ │
│ │ Select location... ▾│ │
│ └──────────────────────────┘ │
│ │
│ Status │
│ [● Available] [○ In Use] │
│ │
│ ┌──────────────────────────┐ │
│ │ Summary │ │
│ │ PLA Basic - Matte Black │ │
│ │ Bambu Lab • 1000g │ │
│ │ AMS 2, Slot A3 • Avail │ │
│ │ ID: EXT-2026-PLA-0043 │ │
│ │ │ │
│ │ ┌──────┐ Print Label │ │
│ │ │ QR │ [═══●═══] ON │ │
│ │ │Preview│ │ │
│ │ └──────┘ │ │
│ └──────────────────────────┘ │
│ │
│ [◀ Back] [✓ Complete Intake] │
│ │
└──────────────────────────────────┘
```
#### Weight Input
- **Stepper control:** Number input flanked by and + buttons
- Step size: 50g (adjustable via long-press to 10g/100g)
- Minimum: 50g, Maximum: 5000g
- Direct numeric keyboard input also available
- **Quick-select chips:** Common spool sizes (250g, 500g, 750g, 1000g) — tap to set weight instantly
- Active chip: `primaryContainer` fill
- On kiosk: Larger chips (48dp height) for easy tapping
- **Unit label:** "grams" suffix, non-editable, `onSurfaceVariant`
#### Location Selector
- `mat-select` dropdown organized hierarchically:
- **AMS Units** → Slot A1, A2, A3, A4
- **External Holders** → Shelf B1, B2, B3
- **Unassigned** (default)
- Only shows available (unoccupied) locations
- Selected location shows printer/host info as hint text
#### Status Toggle
- `mat-chip-listbox` single-select:
- **Available** (default): Green tonal chip + "Available" label
- **In Use**: Blue tonal chip + "In Use" label
- Most new spools are "Available" on intake — this is the safe default
#### Summary Card
- **Outlined card** showing all intake details for final review:
- Material name, brand, weight, location, status, tracking ID
- QR code preview (small, 80dp) — auto-generated from tracking ID
- "Print Label" toggle switch (default: ON on kiosk, OFF on mobile)
- When ON: Bluetooth thermal printer will produce label after completion
- Kiosk default ON because label printer is always connected
- Mobile default OFF because printer may not be available
#### Update State Elements
| Element | Description |
|---------|-------------|
| Primary CTA | **"Complete Intake"** filled button → finalizes spool creation |
| Secondary Actions | **"Back"** outlined button → returns to Identify state |
| Key Components | Weight stepper, Quick-select chips, Location dropdown, Status toggle, Summary card, Print label switch, QR preview |
| Validation | Weight is required (>0). Location is optional. Confirm button always enabled once weight is set. |
#### Completion Flow
1. User taps "Complete Intake"
2. API call creates spool record
3. If "Print Label" is ON: send QR to thermal printer
4. **Success state:** Step indicator animates all 3 steps complete + confetti-free success animation + "Spool Added ✓" snackbar
5. **Two options appear:**
- "Add Another Spool" → resets to Scan state (fastest path for batch intake)
- "View Spool" → navigates to Spool Detail View for the newly created spool
6. Auto-return to Inventory List after 10s if no action taken (kiosk only — prevents abandoned sessions)
### States
| State | Visual |
|-------|--------|
| **Default** | All fields at defaults, "Complete Intake" enabled once weight is set |
| **Weight invalid** | Error text "Enter a weight between 50g and 5000g", button disabled |
| **Location unavailable** | Dropdown shows "No available locations" + "Add as Unassigned" option |
| **Submitting** | "Complete Intake" shows `mat-spinner` inline, disabled. Other elements non-interactive. |
| **Success** | Green check animation, "Spool Added ✓", two option buttons |
| **Error (API)** | Error snackbar + "Retry" action button. Form state preserved. |
| **Error (Printer)** | Success still shows, but printer error snackbar: "Label print failed — reprint from spool detail" |
---
## 4. UX Rationale
### Scan State
1. **Camera-first on mobile.** The phone's camera is the fastest barcode scanner available. No typing, no selecting — just point and scan. The animated viewport frame provides clear guidance on where to aim.
2. **USB scanner-first on kiosk.** The kiosk doesn't need a camera — the USB HID scanner is faster and more reliable than camera-based scanning. The "Scanner Ready" state gives immediate confidence that the hardware is working.
3. **Auto-advance after scan.** When a barcode is detected, the system should advance automatically. The 1.5s delay lets the user see what was scanned, but doesn't require a manual "next" tap. Speed matters when processing 20 spools in a row.
4. **Manual entry always available.** Barcodes get damaged. Camera angles are sometimes wrong. The manual fallback prevents workflow dead-ends.
### Identification State
5. **Confidence indicator reduces anxiety.** When a barcode is scanned, the user needs to know: "Did the system recognize this?" The confidence bar answers this instantly. High confidence → "just confirm." Low confidence → "you'll need to fill in details." This sets expectations.
6. **Pre-fill everything possible.** If the barcode matches a known product (UPC database or previous spools), pre-fill material, brand, and color. The user should confirm, not construct. Every pre-filled field is saved time.
7. **Color as swatches, not dropdown.** Filament color is visual — selecting from a text dropdown ("Red" vs "Crimson" vs "Scarlet") is ambiguous. Circular swatches are unambiguous and faster to scan.
8. **Finish/Modifier as chips.** These are small, mutually exclusive option sets. Chips are more glanceable and tappable than dropdowns for 2-6 options.
### Update State
9. **Weight stepper + quick-select.** Operators know spool weights (they're standard sizes). Quick-select chips (250g, 500g, 750g, 1000g) cover 90% of cases in a single tap. The stepper handles the remaining 10% where weight is non-standard.
10. **Location as optional.** New spools may not be immediately assigned to a printer. Forcing location assignment would create friction — some operators prefer to intake first, assign later.
11. **Summary card for confidence.** Before committing, the user sees everything in one place. This is the "receipt" pattern — it catches errors before they become data problems. The QR preview is especially important: operators verify the physical label will match.
12. **"Add Another" as primary post-completion action.** In a batch intake session (which is common), the operator wants to immediately scan the next spool. "Add Another" should be the most prominent post-completion option.
13. **Print label default differs by platform.** On kiosk, the thermal printer is connected and labels are expected. On mobile, the printer may be remote. The defaults reflect reality.
---
## 5. Visual Direction
### Typography (MD3 Type Scale)
| Role | Token | Size | Weight | Line Height |
|------|-------|------|--------|-------------|
| App Bar Title | `titleLarge` | 22sp | 400 | 28sp |
| Step Labels | `labelSmall` | 11sp | 500 | 16sp |
| Section Headers | `titleSmall` | 14sp | 500 | 20sp |
| Field Labels | `bodyMedium` | 14sp | 400 | 20sp |
| Field Values | `bodyLarge` | 16sp | 400 | 24sp |
| Weight Display | `headlineMedium` | 28sp | 400 | 36sp |
| Confidence Label | `labelLarge` | 14sp | 500 | 20sp |
| Confidence % | `titleMedium` | 16sp | 500 | 24sp |
| Scanned Code | `labelMedium` (monospace) | 12sp | 500 | 16sp |
| Summary Lines | `bodyMedium` | 14sp | 400 | 20sp |
| Tracking ID | `labelMedium` (monospace) | 12sp | 500 | 16sp |
| CTA Button | `labelLarge` | 14sp | 500 | 20sp |
| Instruction Text | `bodyMedium` | 14sp | 400 | 20sp |
| Quick-Select Chip | `labelLarge` | 14sp | 500 | 20sp |
### Spacing (MD3 8dp grid)
| Element | Spacing |
|---------|---------|
| App Bar padding | 16dp horizontal, 12dp vertical |
| Step indicator padding | 24dp horizontal, 12dp vertical |
| Camera viewport | Full-bleed (0dp margin), 16dp rounded corners, overflow hidden |
| Section gap | 16dp vertical |
| Field label to input | 8dp |
| Color swatch grid gap | 12dp |
| Quick-select chip gap | 8dp |
| Weight stepper internal | 12dp between elements |
| Summary card padding | 16dp |
| Button row gap | 12dp |
| Bottom padding (before nav bar) | 16dp (mobile) / 0dp (kiosk — no bottom nav) |
### Color (MD3 Dark Theme — "Industrial Maker")
Same base palette as FIL-001 and FIL-002. Screen-specific additions:
| Role | Token | Value (Dark) | Usage |
|------|-------|-------------|-------|
| Camera Viewport BG | `surfaceContainerHighest` | `#36343B` | Camera area background (before camera starts) |
| Scan Frame | `primary` | `#A8CEDA` | Animated scanning rectangle |
| Scan Success Flash | Custom `success` | `#8BD0A0` | Flash on successful scan |
| Confidence High | Custom `success` | `#8BD0A0` | High confidence bar |
| Confidence High Container | Custom `successContainer` | `#00522E` | High confidence bar background |
| Confidence Medium | Custom `warning` | `#FFD580` | Medium confidence bar |
| Confidence Medium Container | Custom `warningContainer` | `#5D4200` | Medium confidence bar background |
| Confidence Low | Custom orange | `#FFB784` | Low confidence bar |
| Confidence Low Container | Custom orange container | `#5D3A00` | Low confidence bar background |
| Confidence Unknown | `error` | `#F2B8B5` | Unknown/no match bar |
| Confidence Unknown Container | `errorContainer` | `#8C1D18` | Unknown bar background |
| Pre-filled Field Tint | `primaryContainer` | `#004D63` | Subtle green-blue tint on pre-filled inputs |
| Quick-Select Active | `primaryContainer` | `#004D63` | Active chip fill |
| Scanner Connected | Custom `success` | `#8BD0A0` | Kiosk scanner status |
| Scanner Disconnected | `error` | `#F2B8B5` | Kiosk scanner status |
### Scan Frame Animation
The scanning rectangle uses a **sweep line** animation:
- Horizontal line travels from top to bottom of the frame over 2 seconds
- Line color: `primary` (#A8CEDA) with 40% opacity
- Line width: 2dp
- Frame corner brackets: 3dp stroke, `primary` color
- On code detected: Frame corners pulse green (#8BD0A0) once, scan line stops
---
## 6. Responsiveness
### Kiosk (800×480) — Scan State
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ ✕ Smart Intake │
│ RAIL │──────────────────────────────────────────────│
│ │ ● ──── ○ ──── ○ │
│ │ Scan Identify Update │
│ │──────────────────────────────────────────────│
│ │ │
│ │ ┌──────────────────────────────────┐ │
│ │ │ │ │
│ │ │ 📷 SCANNER CONNECTED ✓ │ │
│ │ │ │ │
│ │ │ Scan a barcode or QR code... │ │
│ │ │ │ │
│ │ │ ═══════════════════ │ │
│ │ │ (pulsing line) │ │
│ │ │ │ │
│ │ └──────────────────────────────────┘ │
│ │ │
│ │ Or: [Enter Barcode Manually] │
│ │ │
└──────┴──────────────────────────────────────────────┘
```
### Kiosk (800×480) — Identify State
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ ✕ Smart Intake │
│ RAIL │──────────────────────────────────────────────│
│ │ ✓ ────● ──── ○ │
│ │ Scan Identify Update │
│ │──────────────────────────────────────────────│
│ │ Scanned: 8901234567890 HIGH ▓▓▓▓▓▓▓▓ 92% │
│ │──────────────────────────────────────────────│
│ │ Material: [PLA Basic ▾] │
│ │ Brand: [Bambu Lab ▾] │
│ │ Color: ● ● ● ● ● ● ● ● ● [C] │
│ │ Finish: [Basic][Matte][Silk] Mod: [None] │
│ │ │
│ │ [Rescan] [Confirm →] │
└──────┴──────────────────────────────────────────────┘
```
- **Two-column layout for Material/Brand** on kiosk (side by side to save vertical space)
- **Finish/Modifier on same row** to save space
- **Color picker horizontal** with wrapping
### Kiosk (800×480) — Update State
```
┌──────┬──────────────────────────────────────────────┐
│ NAV │ ✕ Smart Intake │
│ RAIL │──────────────────────────────────────────────│
│ │ ✓ ────✓ ──── ● │
│ │ Scan Identify Update │
│ │──────────────────────────────────────────────│
│ │ Weight: [-] 1000 [+] g │
│ │ Quick: [250] [500] [750] [1000] │
│ │ Location: [AMS 2, Slot A3 ▾] │
│ │ Status: [● Available] [○ In Use] │
│ │──────────────────────────────────────────────│
│ │ ┌──────────────────────────────────┐ │
│ │ │ PLA Basic - Matte Black │ │
│ │ │ Bambu Lab • 1000g • Slot A3 │ │
│ │ │ EXT-2026-PLA-0043 QR ████ │ │
│ │ │ Print Label [═══●═══] ON │ │
│ │ └──────────────────────────────────┘ │
│ │ │
│ │ [◀ Back] [✓ Complete Intake] │
└──────┴──────────────────────────────────────────────┘
```
- **All content fits above fold** on kiosk — no scrolling needed
- **Summary card compact** — inline with QR preview
- **Buttons full-width** in action row
### Mobile PWA (375×812) — Scan State
As shown in Section 3 (full-bleed camera). Key differences:
- Camera takes ~60% of screen height
- Step indicator + instruction text in remaining space
- Scan result chip floats above camera viewport
### Mobile PWA (375×812) — Identify State
As shown in Section 3. Key differences:
- **Single column** layout — all fields stack vertically
- **Scrollable** — Identification form exceeds screen height on small phones
- **Color picker** wraps to 2 rows
- **Buttons** fixed at bottom (sticky)
### Mobile PWA (375×812) — Update State
As shown in Section 3. Key differences:
- **Scrollable** content
- **Summary card** full-width
- **Buttons** fixed at bottom (sticky)
### Key Adaptations
| Property | Kiosk (800×480) | Mobile (375×812) |
|----------|-----------------|-------------------|
| Scan input method | USB HID scanner | Device camera |
| Scanner status card | Yes (connected/disconnected) | No (camera instead) |
| Camera viewport | None | Full-bleed, ~60% height |
| Form layout | Two-column where possible | Single column, scrollable |
| Color picker | Horizontal with wrapping | Horizontal with wrapping (narrower) |
| Quick-select chips | Larger (48dp height) | Standard (36dp height) |
| Print label default | ON | OFF |
| Post-completion auto-return | 10s → Inventory | No auto-return |
| Bottom navigation | No (has rail) | Yes (80dp) |
| Sticky buttons | At bottom of content | At bottom above nav bar |
---
## 7. Developer Handoff Notes
### Angular Material Components
| UI Element | Angular Material Component | Notes |
|-----------|--------------------------|-------|
| Top App Bar | `<mat-toolbar>` | Simple, non-collapsible. Close button on left. |
| Step Indicator | Custom component | 3-dot stepper with animated connecting lines. Use `@angular/animations` for line fill. |
| Camera Viewport | `<video>` + overlay `<div>` | Use `@angular/cdk/overlay` for scanning frame. Camera via `navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })`. |
| Scanning Frame | Custom overlay | SVG or absolute-positioned divs for corner brackets. CSS animation for sweep line. |
| Confidence Bar | Custom component | Horizontal bar with dynamic color + width. `<div>` with `[style.width]` binding. |
| Material Selector | `<mat-select>` | With `<mat-option>`. Add `matSelectSearch` for searchable dropdown. |
| Brand Selector | `<mat-select>` | Same as Material, plus "Other…" option with conditional `<input matInput>`. |
| Color Picker | Custom component | Grid of circular `<button>` elements. Selected state: ring border. |
| Finish Chips | `<mat-chip-listbox>` | Single-select. `mat-chip-option` elements. |
| Modifier Chips | `<mat-chip-listbox>` | Single-select. Include "None" as default option. |
| Weight Stepper | Custom component | `<button mat-icon-button>` (/+) flanking `<input matInput type="number">`. |
| Quick-Select Chips | `<mat-chip-listbox>` | Single-select. Values: 250, 500, 750, 1000. |
| Location Selector | `<mat-select>` | Hierarchical with `<mat-optgroup>` for AMS/Shelves. |
| Status Toggle | `<mat-chip-listbox>` | Two options: Available, In Use. |
| Summary Card | `<mat-card>` outlined | Read-only display. Binding from form values. |
| QR Preview | `qrcode` npm package | Small preview (80dp). Canvas or SVG rendering. |
| Print Label Switch | `<mat-slide-toggle>` | Default ON (kiosk) / OFF (mobile). |
| Complete Intake Button | `<button mat-raised-button>` | `color="primary"`. Shows `<mat-spinner>` inline when submitting. |
| Rescan / Back | `<button mat-stroked-button>` | Navigates to previous state. |
| Success State | Custom component | Green checkmark animation + option buttons. |
| Snackbar | `<mat-snackbar>` | For success/error messages. Duration: 4s. |
| Confirmation Dialog | `<mat-dialog>` | "Discard intake? Unsaved data will be lost." Cancel / Discard. |
### Barcode/QR Scanning Implementation
**Mobile (Camera):**
- Use `@zxing/browser` or `html5-qrcode` library for camera-based barcode detection
- Configure for: EAN-13, UPC-A, Code-128, QR, Data Matrix
- Continuous scan mode (not snapshot) for faster detection
- On detection: debounce 500ms to prevent duplicate reads
- Vibrate on successful scan (if `Vibration API` available)
**Kiosk (USB HID Scanner):**
- USB HID barcode scanners appear as keyboard input — they type the code + Enter
- Listen for rapid key sequence ending in Enter on the Scan state
- Buffer keystrokes; when Enter detected, treat buffered text as scanned code
- Reset buffer on any pause >100ms between keystrokes
- Debounce: ignore duplicate codes within 2s window
### Interaction Notes
1. **Auto-advance from Scan → Identify:** 1.5s delay after successful scan with a cancel affordance (tap scan result chip to cancel auto-advance).
2. **Confidence-based pre-fill:** On entering Identify state, call API with scanned code. If match found, pre-fill form fields. Mark pre-filled fields with subtle `primaryContainer` background tint.
3. **Weight stepper long-press:** Long-press on +/- changes step size (50→10 or 50→100). Visual feedback: tooltip showing current step size.
4. **Quick-select chip sync:** Tapping a quick-select chip updates the stepper value and vice versa. They're two inputs to the same model.
5. **Form validation (Identify):** "Confirm" button is disabled until Material and Color are set. Brand is strongly recommended but not blocking (can be "Unknown").
6. **Form validation (Update):** "Complete Intake" button is disabled until Weight > 0.
7. **Discard confirmation:** Back/close mid-flow triggers dialog: "Discard intake? This spool won't be saved." Cancel / Discard.
8. **Post-completion "Add Another":** Resets all form state, returns to Scan state. Does NOT re-initialize camera (keep it running throughout session for speed).
9. **Printer error handling:** If label printing fails, show snackbar but don't block the success state. The spool is already saved; label can be reprinted from Spool Detail.
10. **Camera permission denied:** Show clear error with "Open Settings" link. Also show "Enter manually" as fallback.
### Accessibility
| Requirement | Implementation |
|-------------|---------------|
| Step indicator | `role="progressbar"`, `aria-valuenow="1/2/3"`, `aria-valuemin="1"`, `aria-valuemax="3"`, `aria-label="Step 1 of 3: Scan"` |
| Camera viewport | `aria-label="Camera view for barcode scanning"`, `role="application"`, `aria-live="polite"` for scan result announcements |
| Scanner status (kiosk) | `aria-live="polite"` — announces "Scanner connected" or "Scanner disconnected" |
| Scan result | `aria-live="assertive"` — "Barcode 8901234567890 detected" |
| Confidence bar | `role="progressbar"`, `aria-valuenow="92"`, `aria-label="Match confidence: 92 percent, High"` |
| Material/Brand selects | Standard `mat-select` accessibility — `aria-label`, keyboard navigable |
| Color picker | Each swatch: `aria-label="Black"`, `role="radio"`, `aria-checked="true/false"`. Radio group semantics. |
| Weight stepper | `aria-label="Weight in grams"`, `role="spinbutton"`, `aria-valuenow`, `aria-valuemin="50"`, `aria-valuemax="5000"` |
| Quick-select chips | `role="radiogroup"`, each chip `role="radio"`, `aria-label="500 grams"` |
| Complete Intake button | `aria-label="Complete spool intake"` — changes to "Submitting…" during submission |
| Close confirmation | `aria-labelledby` dialog title, `aria-describedby` dialog content. Focus trap. |
| Motion reduction | Disable scan line animation, use static frame. Disable auto-advance (require manual "Next"). |
| Keyboard flow | Tab: Close → Step indicator → Content → Buttons. Enter to activate. Escape to dismiss/close. |
### SignalR Integration
- After "Complete Intake" API call succeeds, the new spool is pushed to all connected clients via `SpoolAdded` hub event.
- The Inventory List (FIL-001) will receive this event and add the spool with highlight animation.
- If the user navigates to Inventory after intake, the new spool is already in the list — no manual refresh needed.
### Performance Notes
- Camera stream: request `640×480` resolution for barcode scanning (sufficient, saves battery/CPU)
- Keep camera stream alive across "Add Another" cycles — only initialize once per session
- Debounce API calls in Identify state: don't call the barcode lookup API more than once per 2s
- Use `OnPush` change detection for all Smart Intake components
- QR preview generation should be non-blocking — render in a `requestAnimationFrame` callback
### Routing
```
/intake → Redirect to /intake/scan
/intake/scan → Scan State
/intake/identify → Identification State
/intake/update → Update State
```
Each state is a separate route. This enables:
- Browser back button works naturally (Update → Identify → Scan)
- Deep-linking to specific states (e.g., for testing)
- URL reflects current step (clear for the user and for analytics)

BIN
design/hardware_setup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

510
design/homepage-spec.md Normal file
View File

@@ -0,0 +1,510 @@
# Extrudex — Homepage (Main Hub) UI/UX Specification
> **Author:** Sketch (Design Lead)
> **Date:** April 20, 2026
> **Version:** 1.0
> **Status:** Ready for Review → Implementation Handoff
---
## 1. Objective
**Who is it for?**
Workshop operators and print managers who interact with Extrudex primarily on a Raspberry Pi 5 kiosk touchscreen (arm's length, greasy hands, ambient workshop lighting) and secondarily via a mobile PWA browser for remote monitoring.
**What task are they completing?**
The Homepage serves as the **command center** — the single screen where a user can, at a glance:
1. **Assess the fleet** — See the real-time status of every printer (printing, idle, paused, error, offline).
2. **Spot problems fast** — Instantly identify which machines need attention (errors, paused jobs, low filament).
3. **Take action** — Navigate to a specific printer, spool, or print job with minimal taps.
4. **Monitor inventory** — Get a quick read on filament stock levels and recent consumption.
5. **Recent activity** — See the last few completed or failed prints for situational awareness.
**Success metric:** A user walking past the kiosk can determine fleet health in under 2 seconds without touching the screen.
---
## 2. Screen Inventory
The Homepage/Main Hub is a **single screen** that serves as the root of the navigation tree. It does not itself contain sub-screens, but it is the gateway to:
| Destination Screen | Triggered By | Priority |
|---|---|---|
| **Printer Detail** | Tapping any printer card | Primary |
| **Spool Inventory** | Nav tab "Spools" | Primary |
| **Print Jobs** | Nav tab "Prints" | Primary |
| **Materials DB** | Nav tab "Materials" | Secondary |
| **Settings** | Nav tab "Settings" | Secondary |
| **Quick Scan** | Floating action button (FAB) | Primary |
The Homepage itself contains these **in-page sections** (not separate screens):
1. **Status Summary Bar** — Fleet-wide health at a glance
2. **Printer Fleet Grid** — Live status cards for each printer
3. **Filament Stock Snapshot** — Inventory overview with low-stock alerts
4. **Recent Activity Feed** — Last 5 print events
---
## 3. Layout Specification
### Page Title
**"Extrudex"** — Always visible in the top app bar. No subtitle needed; this is the root screen.
### Navigation Structure
**Primary Navigation: Bottom Navigation Bar (Mobile/Kiosk)**
Material Design 3 bottom navigation with 5 destinations:
| Tab | Icon (Material Symbols) | Label | Badge? |
|---|---|---|---|
| **Hub** | `dashboard` | Hub | — (active by default) |
| **Printers** | `print` | Printers | Error count badge |
| **Spools** | `inventory_2` | Spools | Low-stock badge |
| **Prints** | `receipt_long` | Prints | — |
| **Settings** | `settings` | Settings | — |
**Rationale for bottom nav:**
- Touch-first: bottom nav is the easiest reach zone on a touchscreen kiosk or phone.
- 5 tabs is the MD3 maximum — keeps things scannable.
- "Hub" is the homepage; the other four are top-level destinations.
- Badges on Printers/Spools draw attention to problems without requiring navigation.
**Desktop/Navigation Rail (Future)**
On wider screens (browser dashboard), the bottom nav converts to a Material 3 Navigation Rail on the left edge with the same 5 destinations. This is a responsive transformation, not a separate navigation system.
### Main Sections (Top to Bottom)
#### A. Top App Bar
- **Leading:** Extrudex logo mark (gear icon + "EXTRUDEX" in Inter Bold)
- **Trailing:**
- Connection status indicator (green dot = SignalR connected, red dot = disconnected)
- Clock (kiosk mode — always visible so users know the time without looking at their phone)
- **Style:** MD3 medium top app bar, `surface` color background, no elevation (flat)
- **Height:** 64px kiosk / 56px mobile
#### B. Status Summary Bar
A horizontal strip below the app bar showing fleet-wide metrics in a single row:
| Metric | Format | Color Logic |
|---|---|---|
| Printers Active | `4 / 7` | Text only, neutral |
| Printers in Error | `1` | Red if > 0, hidden if 0 |
| Low Filament Spools | `2` | Yellow if > 0, hidden if 0 |
| Prints Today | `12` | Neutral |
- **Layout:** Horizontal flex row, evenly spaced, each metric in a compact chip/badge
- **Kiosk override:** Larger font (20px), more spacing between metrics
- **Mobile:** Compact chips with icons, horizontally scrollable if overflow
- **Tap behavior:** Tapping "Printers in Error" navigates to Printers tab filtered to errors. Tapping "Low Filament" navigates to Spools tab filtered to low stock.
#### C. Printer Fleet Grid
The **heart of the homepage**. A responsive grid of printer status cards.
**Card Layout (each printer):**
```
┌─────────────────────────────┐
│ [Status Dot] Printer Name │ ← Header row
│─────────────────────────────│
│ Current Job: Benchy #3 │ ← Job name or "Idle"
│ [████████░░] 72% │ ← Progress bar + percentage
│ Filament: PLA Silk │ ← Active material
│ Remaining: ~142g │ ← Spool remaining
│ ETA: 0h 23m │ ← Time remaining
│─────────────────────────────│
│ [View Detail →] │ ← Tap target / CTA
└─────────────────────────────┘
```
**Status Dot Colors:**
- 🟢 Green (`#4ADE70` kiosk / `#16A34A` light) — Printing / Active
- 🟡 Yellow (`#FACC15` kiosk / `#CA8A04` light) — Paused
- 🔴 Red (`#F87171` kiosk / `#DC2626` light) — Error / Failed
- ⚪ Gray (`#64748B`) — Idle / Offline
**Grid Behavior:**
- Kiosk (800×480 Pi 5): 2 columns, 34 rows (scrollable)
- Tablet (768px+): 3 columns
- Mobile (< 480px): 1 column, list view
**Card Dimensions:**
- Kiosk: Full-width within column, min-height 180px
- Mobile: Full-width, min-height 140px, slightly denser
**Important States:**
- **Printing (active):** Progress bar animates, card has subtle left-border accent in green
- **Paused:** Yellow left border, progress bar frozen, "PAUSED" badge overlaid
- **Error:** Red left border, card background shifts to `error-container` token, error message shown
- **Idle:** Gray left border, dimmed progress area, "Ready" label
- **Offline:** Fully dimmed card, "OFFLINE" badge, no real-time data
#### D. Filament Stock Snapshot
A compact section showing inventory health:
**Layout:** Horizontal scrolling row of small cards or a mini-list
```
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ PLA │ │ PETG │ │ TPU │
│ ████████░░ 80% │ │ ████░░░░░ 40% │ │ ██░░░░░░░ 20% │
│ 23 spools │ │ 8 spools │ │ 3 spools ⚠️ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```
- Each card shows: Material base name, stock percentage bar, spool count
- Cards with < 25% stock get a `⚠️` warning indicator and yellow-tinted background
- **Kiosk:** 23 cards visible, swipe to see more
- **Mobile:** Horizontal scroll carousel
- **Tap:** Navigates to Spools tab filtered by that material
#### E. Recent Activity Feed
The last 5 print events in a compact list:
```
┌────────────────────────────────────────────────────┐
│ ✓ Benchy #3 · PLA Silk · 23g · 0h45m │
│ ✓ Gear Set v2 · PETG Basic · 87g · 2h12m │
│ ✗ Phone Case · TPU Basic · — · Failed │
│ ✓ Calibration · PLA Basic · 4g · 0h05m │
│ ✓ Bracket x4 · ASA Matte · 156g · 4h30m │
└────────────────────────────────────────────────────┘
```
- Each row: Status icon, job name, material, weight used, duration
- Failed prints show `✗` in red with "Failed" label
- **Kiosk:** Larger text, comfortable row height (56px)
- **Mobile:** Standard list density (48px rows)
- **Tap a row:** Navigates to that print's detail view
- **"View All" link:** At bottom, navigates to Prints tab
### Primary CTA
**Floating Action Button (FAB):** "Quick Scan" — a prominent FAB in the bottom-right corner with a `qr_code_scanner` icon. This triggers the barcode/USB scanner workflow for spool check-in/check-out.
- Kiosk: Extended FAB with label "Scan" + icon, 56×56px minimum
- Mobile: Standard FAB, icon only, 56×56px
- Positioned above the bottom nav, with 16px margin from edges
### Secondary Actions
- Tap any printer card → Printer Detail
- Tap any stock card → Spools filtered
- Tap any activity row → Print detail
- Tap error/low badges in summary bar → Filtered navigation
### Key Components (MD3)
| Component | Usage | Notes |
|---|---|---|
| Bottom Navigation | Primary nav | 5 destinations |
| Top App Bar (Medium) | App header | Logo + status + clock |
| Cards (Elevated) | Printer status | Real-time content |
| Cards (Filled/outlined) | Filament stock | Compact, scrollable |
| List | Activity feed | 3-line items |
| FAB (Extended/Regular) | Quick Scan | Always accessible |
| Badge | Nav tabs, status | Error count, low stock |
| Linear Progress Indicator | Print progress | On printer cards |
| Chip | Status summary | Compact metrics |
### Important States
| State | Visual Treatment |
|---|---|
| Loading (initial) | Skeleton screens for printer cards, shimmer animation |
| SignalR disconnected | Red dot in app bar, banner: "Live updates paused — reconnecting..." |
| No printers registered | Empty state illustration + "Add your first printer" CTA |
| No spools registered | Stock section shows "No inventory — add spools to start tracking" |
| All printers idle | Fleet grid shows all gray cards, summary bar shows "0 active" |
| Critical error (printer) | Card pulses red border once, then steady red left border |
---
## 4. UX Rationale
### Why This Layout Supports the Task
1. **Fleet health at a glance:** The Status Summary Bar + Printer Fleet Grid are visible immediately on load. No scrolling required on kiosk to see if something is wrong. Color-coded status dots and left-border accents make the visual scan instantaneous.
2. **Progressive detail:** The homepage gives you "enough" — status, progress, material. You only tap into a printer card when you need details (temps, G-code, history). This avoids information overload while keeping critical data surface-level.
3. **Inventory awareness without navigation:** The Filament Stock Snapshot means operators don't need to leave the homepage to know if they're running low. The warning state (yellow + ⚠️) is visible without interpreting numbers.
4. **Activity context:** The Recent Activity Feed gives "what just happened" situational awareness — useful when returning to the kiosk after being away. It answers "did that print finish?" without requiring navigation.
5. **Scan-first workflow:** The FAB for Quick Scan is always accessible from the homepage, which is the most common action for spool check-in during workshop operations. Placed bottom-right — the natural resting thumb zone.
### Hierarchy (What matters most → least)
1. **Printer errors** (red, draws the eye immediately)
2. **Active printers with progress** (green accent, animated progress bars)
3. **Paused printers** (yellow accent, needs attention)
4. **Low filament warnings** (yellow indicators in stock section)
5. **Idle printers** (gray, calm, no action needed)
6. **Recent activity** (informational, below the fold on mobile)
### Tradeoffs
| Decision | Benefit | Cost | Mitigation |
|---|---|---|---|
| Bottom nav over side nav | Touch-friendly, MD3 standard, always visible | Takes screen space at bottom | Acceptable on touch-first device |
| Cards over table | Glanceable, status-colorable, touch-friendly | Less dense (can't see 7 printers at once) | Grid layout maximizes visible count; scroll for 6+ printers |
| Summary bar metrics | Fleet health without scrolling | Adds cognitive load if too many metrics | Keep to 4 max, hide zero-value metrics |
| Horizontal scroll for stock cards | Saves vertical space, works well for 36 materials | Hidden cards require swipe | First 3 most-used materials visible; swipe for rest |
| FAB for scan | Always accessible, one-tap | Covers content behind it | Standard FAB behavior; scrolls away on mobile |
| No sidebar on kiosk | Maximizes kiosk screen real estate | No persistent deep-nav | Bottom nav is sufficient for 5 top-level destinations |
---
## 5. Visual Direction
### Tone
**Modern Industrial/Maker** — Dark mode by default on kiosk. Professional workshop vibe. High contrast. Think: a CNC control panel, not a social media app. The aesthetic should feel like a tool you trust, not an app you browse.
### Dark Mode Palette (Kiosk Default)
| Token | Value | Usage |
|---|---|---|
| `md.sys.color.background` | `#0F172A` | Page background |
| `md.sys.color.surface` | `#1E293B` | Card backgrounds, nav bar |
| `md.sys.color.surface-container` | `#334155` | Elevated cards, containers |
| `md.sys.color.surface-container-high` | `#475569` | Hover/pressed states |
| `md.sys.color.primary` | `#60A5FA` | Primary actions, links, active tab |
| `md.sys.color.on-primary` | `#0F172A` | Text on primary |
| `md.sys.color.primary-container` | `#1E3A5F` | Subtle primary tinted containers |
| `md.sys.color.error` | `#F87171` | Error states, failed prints |
| `md.sys.color.error-container` | `#450A0A` | Error card backgrounds |
| `md.sys.color.on-error` | `#FFFFFF` | Text on error |
| `md.sys.color.on-surface` | `#F1F5F9` | Primary text |
| `md.sys.color.on-surface-variant` | `#94A3B8` | Secondary text |
| `md.sys.color.outline` | `#334155` | Borders, dividers |
| `md.sys.color.outline-variant` | `#1E293B` | Subtle borders |
### Light Mode Palette (Dashboard/Future)
| Token | Value | Usage |
|---|---|---|
| `md.sys.color.background` | `#F8FAFC` | Page background |
| `md.sys.color.surface` | `#FFFFFF` | Card backgrounds |
| `md.sys.color.primary` | `#2563EB` | Primary actions |
| `md.sys.color.on-surface` | `#0F172A` | Primary text |
| `md.sys.color.on-surface-variant` | `#475569` | Secondary text |
### Typography
| Role | Font | Weight | Size (Kiosk) | Size (Mobile) |
|---|---|---|---|---|
| App Title | Inter | Bold | 22px | 20px |
| Card Title (Printer Name) | Inter | SemiBold | 20px | 18px |
| Section Heading | Inter | SemiBold | 18px | 16px |
| Body Text | Inter | Regular | 18px | 16px |
| Metric Value | Inter | Bold | 28px | 24px |
| Metric Label | Inter | Medium | 14px | 12px |
| Mono Values (weight, time, %) | JetBrains Mono | Medium | 18px | 16px |
| Caption / Timestamp | Inter | Regular | 14px | 12px |
**Rationale for oversized metrics:** The kiosk is viewed at arm's length or further. A 28px bold metric is legible from 34 feet away. Monospace for numeric values ensures columns align and feels "instrument-like."
### Spacing / Density
| Token | Kiosk | Mobile |
|---|---|---|
| Page padding | 24px | 16px |
| Card padding | 20px | 16px |
| Grid gap | 16px | 12px |
| Section gap | 32px | 24px |
| Row height (lists) | 56px | 48px |
| FAB margin from edge | 16px | 16px |
| Touch target minimum | 48px | 44px |
### Component Behavior
- **Printer cards:** Update in real-time via SignalR. Progress bar animates smoothly (CSS transition 300ms). Status changes trigger a brief highlight flash (200ms background shift) to draw attention.
- **Bottom nav:** Active tab has `primary` color icon + label. Inactive tabs use `on-surface-variant`. Badges are small circles positioned at the icon's top-right.
- **FAB:** Elevated with shadow-3. On tap, ripple effect. Extended label appears on kiosk (wider screen), icon-only on mobile.
- **Status summary bar:** Metric chips are non-scrolling on kiosk (fits in one row at 800px width), horizontally scrolling on mobile if needed.
- **Activity feed:** List items have subtle divider lines (`outline-variant`). Failed items have `error-container` background tint.
- **Skeleton loading:** Cards show shimmer placeholder during initial load (1.5s max before content appears).
### Color Usage Guidelines
1. **Color is information, not decoration.** Every colored element must communicate state.
2. **Red = immediate attention.** Only use for errors, failures, disconnections. Never for decoration.
3. **Yellow = needs attention soon.** Paused printers, low filament, warnings.
4. **Green = operating normally.** Active printing, completed, connected.
5. **Blue = interactive.** Links, buttons, active navigation, primary CTAs.
6. **Gray = neutral/idle.** Offline printers, inactive states, secondary info.
7. **Never use color alone to convey meaning.** Always pair with icon + text label (accessibility).
---
## 6. Responsiveness
### Breakpoints
| Breakpoint | Width | Layout |
|---|---|---|
| Kiosk | 480800px (Pi 5 typical: 800×480) | 2-col grid, bottom nav, extended FAB |
| Mobile (compact) | < 480px | 1-col list, bottom nav, standard FAB |
| Tablet | 7681024px | 3-col grid, bottom nav, extended FAB |
| Desktop | 1024px+ | 3-col grid, nav rail, extended FAB |
### Layout Changes by Device
#### Pi 5 Kiosk (800×480, Landscape)
- **Top app bar:** 64px height, clock always visible
- **Status summary:** 4 metrics in a single row (fits 800px)
- **Printer grid:** 2 columns × N rows, scrollable vertically
- **Stock snapshot:** 3 cards visible, no scroll needed (most common materials first)
- **Activity feed:** 3 rows visible, "View All" link
- **Bottom nav:** Full 5 tabs
- **FAB:** Extended with "Scan" label
#### Mobile PWA (< 480px, Portrait)
- **Top app bar:** 56px height, no clock (use phone clock)
- **Status summary:** 23 metrics visible, horizontal scroll for more
- **Printer grid:** 1 column list view (cards become horizontal list items)
- **Stock snapshot:** Horizontal carousel, 2 cards visible
- **Activity feed:** 35 rows, compact density
- **Bottom nav:** 5 tabs (may collapse labels on very narrow screens)
- **FAB:** Icon only, standard 56px
#### Tablet / Desktop Browser
- **Bottom nav → Navigation Rail** (left side, 80px wide)
- **Printer grid:** 3 columns
- **Stock snapshot:** 45 cards visible in row
- **Activity feed:** 5 rows
- **Additional whitespace** — dashboard mode can breathe more
### Kiosk-Specific Considerations
- **No hover states** — all interactions are tap/click only
- **No tooltips** — information must be visible inline
- **Larger touch targets** — 48px minimum (4px above MD3 default)
- **No right-click context menus** — all actions are explicit buttons/links
- **Screen wake** — kiosk should not sleep; CSS `animation` on a subtle element to prevent screen burn-in (shift a 1px element every 60s)
- **Overshoot scroll** — prevent pull-to-refresh or browser gestures (PWA `overscroll-behavior: none`)
---
## 7. Developer Handoff Notes
### Reusable Components to Build
| Component | Type | Props/Inputs | Notes |
|---|---|---|---|
| `<ex-printer-card>` | Standalone | `printer: Printer`, `status: PrinterStatus`, `job?: PrintJob` | Left-border color computed from status. SignalR drives re-renders. |
| `<ex-status-dot>` | Atom | `status: 'active' \| 'paused' \| 'error' \| 'idle' \| 'offline'` | Small colored circle (12px) with aria-label for accessibility |
| `<ex-metric-chip>` | Atom | `label: string`, `value: string \| number`, `variant: 'default' \| 'error' \| 'warning'` | Used in Status Summary Bar |
| `<ex-stock-card>` | Standalone | `material: string`, `percentage: number`, `spoolCount: number`, `lowStock: boolean` | Horizontal scroll child |
| `<ex-activity-item>` | List Item | `job: PrintEvent` | 3-line list item with status icon |
| `<ex-progress-bar>` | Atom | `value: number`, `status: PrinterStatus` | MD3 linear progress, color matches status |
| `<ex-fleet-summary>` | Composite | `printers: Printer[]`, `spools: Spool[]` | Renders metric chips row |
| `<ex-connection-indicator>` | Atom | `connected: boolean` | Green/red dot in app bar |
### Angular Material Components Used
- `MatBottomNavigation` (or custom bottom nav — MD3 bottom nav is not yet in Angular Material; implement with `MatTabNav` + custom styling)
- `MatCard` → Styled with custom dark tokens
- `MatProgressBar` → Custom color per status
- `MatFabButton` / `MatMiniFabButton`
- `MatBadge` → For nav tab badges
- `MatList` → Activity feed
- `MatChip` → Status summary metrics
- `MatIcon` → Material Symbols throughout
- `MatToolbar` → Top app bar
### Interaction Notes
1. **SignalR subscriptions:** The homepage must subscribe to `PrinterHub` on mount. Each printer card re-renders on status/progress push events. Unsubscribe on navigate-away.
2. **Optimistic updates:** Progress bars should animate smoothly — use CSS transitions, not discrete jumps. If SignalR sends a progress update every 5s, interpolate between values.
3. **FAB scan action:** On tap, immediately focus a hidden text input that captures USB HID barcode scanner output ( scanners type characters + Enter). This is a capture-only input — no keyboard should appear on mobile. Use `<input readonly>` or intercept `keydown` globally.
4. **Pull-to-refresh:** Disabled on kiosk. On mobile PWA, consider enabling for manual data refresh.
5. **Navigation:** Use Angular Router with bottom nav tabs bound to routerLinkActive. Highlight active tab. Preserve scroll position when returning to Hub.
6. **Skeleton screens:** On initial load, show 6 skeleton printer cards (matching grid layout). Use `@angular/material` skeleton patterns or custom shimmer CSS.
### Accessibility
| Requirement | Implementation |
|---|---|
| Screen reader | All status indicators have `aria-label` (e.g., "Printer Elegoo-1: Printing, 72% complete") |
| Color blindness | Never color-only — always icon + text + color. Status dots have aria-labels |
| Keyboard nav | Tab order: top bar → summary metrics → printer cards (grid order) → stock → activity → FAB → bottom nav |
| Focus management | After navigation, focus lands on the page title. After FAB tap, focus returns to FAB |
| Touch target | 48px minimum on kiosk, 44px minimum on mobile — all interactive elements |
| Contrast | All text on dark background meets WCAG AA (4.5:1 for body, 3:1 for large text) |
| Motion | Respect `prefers-reduced-motion` — disable progress bar animation and shimmer if set |
| Live regions | Printer status changes announced via `aria-live="polite"` on the card region |
### Performance Notes
- **OnPush change detection** for all components receiving SignalR data
- **TrackBy** on printer grid `*ngFor` to avoid re-rendering unchanged cards
- **Virtual scrolling** for activity feed if it grows beyond 20 items (use `@angular/cdk ScrollingModule`)
- **Lazy load** Spools, Prints, Materials, Settings tabs — only Hub is eager-loaded
- **Image optimization:** Use inline SVG for status icons, not raster images. Logo should be SVG.
### File Structure Suggestion
```
src/app/
├── pages/
│ └── hub/
│ ├── hub.component.ts
│ ├── hub.component.html
│ ├── hub.component.scss
│ └── hub.component.spec.ts
├── components/
│ ├── printer-card/
│ ├── status-dot/
│ ├── metric-chip/
│ ├── stock-card/
│ ├── activity-item/
│ ├── progress-bar/
│ ├── fleet-summary/
│ └── connection-indicator/
├── layout/
│ ├── bottom-nav/
│ ├── top-app-bar/
│ └── nav-rail/
└── shared/
└── tokens/
└── _extrudex-tokens.scss
```
---
## Appendix: Navigation Map
```
┌──────────────────────────────────────────────────────────────┐
│ EXTRUDEX │
│ │
│ ┌─ Hub (Home) ──────────────────────────────────────────┐ │
│ │ • Fleet Status Grid │ │
│ │ • Filament Snapshot │ │
│ │ • Recent Activity │ │
│ │ • [FAB: Quick Scan] │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Printers ────────┐ ┌─ Spools ─────────────────────┐ │
│ │ Printer List │ │ Spool Inventory │ │
│ │ → Printer Detail │ │ → Spool Detail │ │
│ │ → Job History │ │ → Check-in/Check-out │ │
│ │ → Controls │ │ → Link to Printer │ │
│ └────────────────────┘ └─────────────────────────────┘ │
│ │
│ ┌─ Prints ──────────┐ ┌─ Settings ───────────────────┐ │
│ │ Print Job List │ │ System Configuration │ │
│ │ → Print Detail │ │ Printer Management │ │
│ │ → COGS Report │ │ User Preferences │ │
│ └────────────────────┘ └──────────────────────────────┘ │
│ │
│ ══════════════════════════════════════════════════════════ │
│ [Hub] [Printers] [Spools] [Prints] [Settings] │
│ ══════════════════════════════════════════════════════════ │
└──────────────────────────────────────────────────────────────┘
```
---
*End of Specification — Ready for Stuart (mockup generation) and Rex (implementation handoff)*

BIN
design/kiosk_interface.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -0,0 +1,100 @@
# Design Specification: Smart Intake - Identification State
**Screen ID:** FIL-003-B
**State:** 2 of 3 (Identify)
**Context:** Occurs immediately after a successful barcode/QR scan.
**Objective:** Validate the identity of the spool before committing it to the inventory.
---
## 1. User Experience Goals
- **Frictionless Validation:** If the scan is high-confidence, the user should only need to tap "Confirm".
- **Error Correction:** Provide easy paths to correct misidentified attributes without restarting the entire flow.
- **Glanceability:** Use color-coded confidence bars so the user knows immediately if the system is "sure" or "guessing".
---
## 2. Visual Hierarchy & Layout
### 2.1 Global Header
- **Top App Bar:** `md-top-app-bar` (Small).
- **Leading:** Close icon (✕) -> Returns to Inventory with "Discard changes?" prompt.
- **Title:** "Smart Intake"
- **Step Indicator:** Custom linear stepper.
- `Scan` (Completed: Green check) $\rightarrow$ `Identify` (Active: Blue filled) $\rightarrow$ `Update` (Pending: Grey outline).
### 2.2 Main Content Area
The content is organized as a vertical stack of cards/inputs to ensure touch-first accessibility.
#### A. Confidence Card (`md-card` - filled)
- **Label:** "Scanned: [Barcode Value]" (Monospace)
- **Confidence Metric:** "Match: HIGH CONFIDENCE"
- **Visual Indicator:** A linear progress bar.
- **High (≥80%):** Success Green (`#16A34A` / `#4ADE70`)
- **Medium (40-79%):** Warning Yellow (`#CA8A04` / `#FACC15`)
- **Low (<40%):** Error Red (`#DC2626` / `#F87171`)
- **Percentage:** Displayed as text (e.g., "92%") at the end of the bar.
#### B. Attribute Selection (Input Group)
- **Material Selector:** `md-dropdown-select` (Searchable).
- *Pre-fill:* Based on scan result.
- **Brand Selector:** `md-dropdown-select` (Searchable).
- *Pre-fill:* Based on scan result.
- **Color Picker:** Custom circular swatch grid.
- **Interaction:** Tap to select. Selected swatch has a high-contrast ring indicator.
- **Custom Option:** A "Custom" button that opens a text input for unique colors.
- **Finish & Modifier:** `md-chip-set` (Single select).
- **Finish:** [Basic, Matte, Silk, Sparkle, Metallic, Translucent]
- **Modifier:** [None, Carbon Fiber, Wood Fill, Glow-in-Dark, Marble, Gradient]
### 2.3 Action Bar (Footer)
- **Rescan Button:** `md-outlined-button`. Transitions user back to State 1 (Scan).
- **Confirm Button:** `md-filled-button`. Primary CTA. Transitions user to State 3 (Update).
- *Constraint:* Disabled until Material, Brand, and Color are selected.
---
## 3. Technical Specifications
### 3.1 Dimensions & Touch Targets
- **Touch Targets:** Minimum 44x44px for all buttons, dropdowns, and swatches.
- **Padding:** 16px internal card padding; 24px external margin between major sections.
- **Typography:**
- Title: `Display Small` (text-3xl font-bold)
- Section Labels: `Label Medium` (text-sm font-medium)
- Value Text: `Body Large` (text-base font-normal)
### 3.2 Theme Mapping
| Element | Kiosk (Dark) | Mobile (Light) |
|-----------|--------------|---------------|
| Background | `#0F172A` | `#F8FAFC` |
| Surface | `#1E293B` | `#FFFFFF` |
| Primary | `#60A5FA` | `#2563EB` |
| Text Primary | `#F1F5F9` | `#0F172A` |
| Text Secondary| `#94A3B8` | `#475569` |
---
## 4. State Transitions
### Transition: Scan $\rightarrow$ Identify
- **Trigger:** Barcode detected and decoded.
- **Animation:**
1. Scanning frame flashes **Green**.
2. A "Slide-in" transition from the right (300ms ease-out).
3. Step indicator dot for "Scan" transforms into a checkmark.
4. Step indicator dot for "Identify" fills with Primary color.
### Transition: Identify $\rightarrow$ Update
- **Trigger:** "Confirm" button tapped.
- **Animation:**
1. Slide-in transition from the right (300ms ease-out).
2. Step indicator dot for "Identify" transforms into a checkmark.
3. Step indicator dot for "Update" fills with Primary color.
---
## 5. Edge Cases & Error Handling
- **No Match Found:** Confidence bar is Red (0%). All dropdowns are empty. System displays a hint: "Barcode unknown. Please enter details manually."
- **Ambiguous Match:** Confidence bar is Yellow. Material and Brand are pre-filled, but Color is highlighted with a "Review Required" pulse.
- **Invalid Input:** If a user attempts to confirm without required fields, the missing fields pulse Red.