# Phase 9: Item & Note CRUD Wiring - Context

**Gathered:** 2026-03-12
**Status:** Ready for planning

<domain>
## Phase Boundary

Wire item add/edit/remove and note save handlers to real persistence so edits survive app restart. The UI stubs (card-save-note, card-add-item, card-remove-item) already exist from Phase 4; this phase replaces println! handlers with real EditDispatcher → DashboardDataClient → service pipeline calls, adds a pending-edit queue for offline resilience, and builds the full item-add search flow.

</domain>

<decisions>
## Implementation Decisions

### Post-save card refresh
- Optimistic update: card UI updates immediately from local data after edit
- Failed edits are queued locally in a persistent file (survives app restart)
- Retry strategy: piggyback on existing 5-minute auto-poll cycle + flush queue on every app start
- No immediate notification on failure — edits silently queue

### Sync status indicator
- Global sync indicator shows pending edit count (e.g., "2 edits pending")
- Indicator decrements silently as edits sync successfully
- When all edits sync, indicator disappears
- No escalation threshold — indicator always shows the same way regardless of queue age

### Error feedback
- Silent queue model: no toasts or notifications when edits fail to persist
- Client-side validation blocks obviously invalid edits before they enter the queue (empty item name, etc.)
- Validation errors show inline on the field (e.g., red border, short message)
- Validation errors do NOT enter the pending-edit queue

### Item add flow — Shipment Product Lookup
- Full catalog search UI wired in this phase (not deferred)
- Centered floating modal titled "Shipment Product Lookup"
- Rest of app GUI dimmed behind modal (backdrop is semi-transparent, app context visible)
- During search, the target card shows a featureless white square in the slot where the new item will appear, with a slow breathing opacity animation
- Search results display as list with thumbnails (image_hint fallback to generic icon)
- "Create New" option always appears as the last entry in the results list
- New item creation form: display name + optional Shopify product page URL
- If Shopify URL is supplied, app automatically fetches product image via Shopify Admin API

### Item removal
- Carried from Phase 4: unassign item from package, preserve catalog record (soft-delete via deactivate_item)
- Removal confirmation required before dispatch

### Note editing
- Carried from Phase 4: explicit Save/Cancel, not auto-save on blur
- Multi-line plain text supported
- Note edits use the same optimistic update + queue pattern as item edits

### Claude's Discretion
- Pending-edit file format and location (JSON file in app data directory)
- Exact breathing animation timing/easing for the placeholder square
- Shipment Product Lookup modal dimensions and search debounce timing
- Inline validation message wording and styling
- Global sync indicator placement and visual treatment

</decisions>

<specifics>
## Specific Ideas

- "Shipment Product Lookup" — user's exact name for the add-item modal
- Breathing animation: slow opacity pulse on a white square placeholder, signaling "something is being placed here"
- Lookup results with thumbnails help visually identify the right product before committing
- Shopify URL auto-fetch: when creating a new item with a Shopify product page URL, the app should parse the product ID and call Shopify Admin API to pull the product image automatically

</specifics>

<code_context>
## Existing Code Insights

### Reusable Assets
- `EditCommand` enum (AddItem, RemoveItem, RenameItem, SaveNote) in `crates/app/src/dashboard/actions.rs` — fully defined, ready to dispatch
- `EditDispatcher` in same file — routes commands to DashboardDataClient trait methods
- `DashboardDataClient` trait in `crates/app/src/service_client.rs` — has `add_item`, `remove_item`, `rename_item`, `save_note`, `search_item_catalog` methods with default no-op implementations
- `ItemCatalogEntry` struct — has item_id, display_name, image_hint, shopify_product_id
- Toast system from Phase 7 — reusable for any future notification needs (not used for sync status in this phase)

### Established Patterns
- `EditDispatcher` mirrors `RefreshDispatcher` for consistent dispatch pattern
- All mutation trait methods have default no-op implementations for backward compatibility
- Snapshot projection pipeline maps service snapshots into card view models
- Soft-delete pattern: `deactivate_item` sets `is_active=false`, record survives for catalog history

### Integration Points
- `main.rs` lines 1007-1014: stub handlers (`println!`) for `on_card_save_note`, `on_card_add_item`, `on_card_remove_item` — these get replaced with real dispatch calls
- `crates/service/src/api/items.rs`: service layer item operations
- `crates/service/src/db/repository.rs`: `upsert_item`, `deactivate_item`, `upsert_package` persistence methods
- Auto-poll cycle (5-minute interval): pending-edit flush hooks into this existing timer

</code_context>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 09-item-note-crud-wiring*
*Context gathered: 2026-03-12*
