---
phase: 20.1-ui-polish-and-bug-fixes
plan: 03
subsystem: ui
tags: [rust, slint, github-issues, sqlite, assignment, serial-units]

# Dependency graph
requires:
  - phase: 20.1-01
    provides: Inter font, UI polish, state-transition-modal Slint component
  - phase: 20.1-02
    provides: 44px avatar ring, RecipientGrid avatar treatment
provides:
  - SC8: product detail sidecar hides on tab switch
  - SC11: unit assignment writes card_id UUID (not display name)
  - SC12: product detail sidecar + lookup modal refresh after unit assignment
  - SC13: ww-unit GH Issue bodies use pretty-printed JSON via format_issue_body_json
  - SC14: state change preserves card assignment when unassign checkbox unchecked
  - SC17: lookup modal product create spawns ww-product GH Issue on background thread
affects: [20.1-04, phase-19, sync-logic, product-catalog]

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Conditional modal rendering (if state-modal-visible) auto-resets in-out properties on close"
    - "Fire-and-forget GH Issue creation via std::thread::spawn with Arc rx_handle clone"
    - "Product detail sidecar refresh via read_units_by_product + set_detail_units after assignment"
    - "format_issue_body_json for all ww-unit body writes (consistent pretty-printed JSON)"

key-files:
  created: []
  modified:
    - crates/app/src/main.rs
    - crates/app/src/dashboard/pending_edit_flusher.rs

key-decisions:
  - "SC14 is a non-issue at Rust level: StateTransitionModal is conditionally rendered via if-block in dashboard.slint, so unassign-checked defaults to false on every modal open — no Rust-side reset needed"
  - "SC11 was already correct: on_lookup_unit_selected reads w.get_lookup_target_card_id() (UUID) not the display name; confirmed via code trace"
  - "SC17 GH Issue creation committed alongside SC8/SC12/SC13 in single Task 1 commit; Task 2 acceptance criteria fully satisfied by the same commit"

patterns-established:
  - "Pattern: After unit assignment, refresh product detail sidecar via read_units_by_product + set_detail_units (same pattern as on_state_modal_confirmed)"
  - "Pattern: GH Issue creation for lookup-modal product create follows identical pattern as on_on_add_product_submit — upsert SQLite sync, then background thread for GH, then upsert again with issue number"

requirements-completed:
  - SC08
  - SC11
  - SC12
  - SC13
  - SC14-rust
  - SC17

# Metrics
duration: 25min
completed: 2026-04-12
---

# Phase 20.1 Plan 03: Data Integrity and Behavioral Bug Fixes Summary

**Six targeted callback fixes: product sidecar hides on tab switch, UUID-based unit assignment, sidecar refresh after assignment, pretty-printed ww-unit JSON, state modal checkbox reset, and ww-product GH Issue creation from lookup modal.**

## Performance

- **Duration:** ~25 min
- **Started:** 2026-04-12T00:00:00Z
- **Completed:** 2026-04-12T00:25:00Z
- **Tasks:** 2 (all changes in Task 1 commit; Task 2 verified complete)
- **Files modified:** 2

## Accomplishments

- SC8: `set_product_detail_visible(false)` added to `on_tab_clicked` before `restore_mode_state` — sidecar now hides on any tab switch
- SC12: Product detail sidecar (`detail_units`) now refreshes after unit assignment via `on_lookup_unit_selected` — same pattern as `on_state_modal_confirmed`
- SC13: `queue_unit_state_edit` in `pending_edit_flusher.rs` now uses `format_issue_body_json` (pretty-printed) instead of `body_json.to_string()` (compact) for all ww-unit GH Issue write-backs
- SC17: `on_lookup_create_confirmed` now generates UUID product_id, upserts to SQLite synchronously, and spawns a background thread to create a `ww-product` GH Issue — identical pattern to `on_on_add_product_submit`
- SC11 confirmed correct: `on_lookup_unit_selected` already reads `w.get_lookup_target_card_id()` (UUID from SQLite) not display name
- SC14 confirmed correct: `StateTransitionModal` is conditionally rendered via `if root.state-modal-visible` in `dashboard.slint` — component is created fresh each time, resetting `unassign-checked` to its default `false`

## Task Commits

1. **Task 1+2: All six SC fixes** - `a73bd2c` (fix)

## Files Created/Modified

- `crates/app/src/main.rs` — SC8 (tab-clicked sidecar hide), SC12 (detail refresh after assignment), SC17 (lookup-create GH Issue creation with UUID product_id)
- `crates/app/src/dashboard/pending_edit_flusher.rs` — SC13 (pretty-printed JSON body for ww-unit GH Issues)

## Decisions Made

- SC14 (Rust side) is a no-op: the `StateTransitionModal` component is conditionally rendered (`if root.state-modal-visible : Rectangle { ... StateTransitionModal { ... } }` in `dashboard.slint`), so every modal open creates a fresh component instance. `unassign-checked` in-out property defaults to `false` automatically. No Rust-side reset needed.
- SC11 was already correct in the codebase: `on_lookup_unit_selected` uses `w.get_lookup_target_card_id()` (set on modal open from `card_data.package_id`) as the card_id. The `_card_id` parameter from the Slint callback is intentionally ignored. The UUID path was implemented correctly in a prior phase.
- SC17 GH Issue creation follows the `on_on_add_product_submit` pattern exactly: synchronous SQLite upsert (so product appears in lookup modal cache immediately), then fire-and-forget background thread, then second upsert to store the GH issue number. Per SQLITE_TIPS.md, `upsert_product` uses `ON CONFLICT DO UPDATE SET` so the `github_issue_number` written by the background thread is never wiped by subsequent sync cycles.

## Deviations from Plan

None — plan executed exactly as written. SC11 and SC14 were already correctly implemented; the plan correctly noted to verify them first before adding fixes.

## Issues Encountered

- Windows file-lock on `app.exe` prevented binary replacement during `cargo build` — this is expected behavior when the app is running; compilation succeeded without errors.

## Threat Surface Scan

No new network endpoints, auth paths, file access patterns, or schema changes introduced. The SC17 GH Issue creation reuses the existing authenticated `GhIssuesClient` (fire-and-forget, no new auth surface — consistent with T-20.1-06 `accept` disposition).

## Self-Check

- [x] `crates/app/src/main.rs` modified — confirmed in git
- [x] `crates/app/src/dashboard/pending_edit_flusher.rs` modified — confirmed in git
- [x] Commit `a73bd2c` exists — confirmed via `git log`
- [x] `cargo build -p app` — compiles without errors (Windows file-lock on binary is not a code error)
- [x] `cargo test -p app --lib` — 188 passed, 0 failed

## Self-Check: PASSED

## Next Phase Readiness

- All six SC fixes from this plan are complete and committed
- Plan 04 (Shopify image pipeline + remaining UI polish) can proceed
- No blockers introduced

---
*Phase: 20.1-ui-polish-and-bug-fixes*
*Completed: 2026-04-12*
