# 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)