Files
Extrudex/design/03-smart-intake-workflow.md

738 lines
41 KiB
Markdown
Raw Normal View History

2026-04-25 18:51:05 +00:00
# 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)