---
phase: 17-gh-issues-client-and-product-catalog
plan: 03
subsystem: ui
tags: [slint, product-catalog, product-grid, product-detail, add-product-form, ellipsis-menu]

# Dependency graph
requires:
  - phase: 17-01
    provides: GhIssuesClient, ProductRow SQLite schema, V004 migration
provides:
  - Enhanced ProductGrid with ellipsis menus, recipient count badges, image display, and Add Product button
  - ProductDetailPanel component showing product metadata and serial units list
  - AddProductForm component with name, Shopify URL auto-suggest, image preview
  - Dashboard integration with properties and callbacks for both new components
affects: [17-04, 17-05]

# Tech tracking
tech-stack:
  added: []
  patterns: [PopupWindow ellipsis menu on product tiles, modal overlay with backdrop dismiss]

key-files:
  created:
    - crates/app/ui/product-detail.slint
    - crates/app/ui/add-product-form.slint
  modified:
    - crates/app/ui/option-grid.slint
    - crates/app/ui/dashboard.slint
    - crates/app/src/main.rs
    - crates/app/src/dashboard/discovery.rs

key-decisions:
  - "Product initials placeholder uses '?' instead of first-letter extraction — Slint lacks string substring/toUpperCase methods"
  - "ProductDetailPanel overlays right side of product grid (shrinks grid width when visible) rather than full-screen overlay"
  - "AddProductForm uses centered modal overlay with semi-transparent backdrop and click-outside-to-dismiss"
  - "Removed visible property declarations from ProductDetailPanel and AddProductForm — Slint Rectangle has built-in visible; controlled by parent if-conditionals"

patterns-established:
  - "Product tile ellipsis menu: PopupWindow anchored to tile top-right with conditional menu items (View on Shopify gated by shopify-linked)"
  - "Modal overlay pattern: Rectangle with #00000080 backdrop, TouchArea for dismiss declared before form component for correct z-order"

requirements-completed: [PROD-01]

# Metrics
duration: 8min
completed: 2026-03-25
---

# Phase 17 Plan 03: Product Catalog UI Summary

**Slint UI components for product catalog: enriched ProductGrid tiles with ellipsis menus and recipient badges, ProductDetailPanel with serial units, AddProductForm with Shopify auto-suggest**

## Performance

- **Duration:** 8 min
- **Started:** 2026-03-25T08:35:15Z
- **Completed:** 2026-03-25T08:43:38Z
- **Tasks:** 2
- **Files modified:** 6

## Accomplishments
- Enhanced ProductGrid with product-id, recipient-count, has-image, image, shopify-linked fields; ellipsis button with PopupWindow menu; recipient count badges; real image display; "No products yet" empty state; "Add Product" button
- Created ProductDetailPanel showing product image, name, Shopify URL, GH Issue ref, and scrollable serial units list with create-unit button
- Created AddProductForm with name field (required, with validation error), Shopify URL field with auto-suggest dropdown, image preview section, and Discard/Add Product buttons
- Integrated both new components into dashboard.slint with full property and callback wiring

## Task Commits

Each task was committed atomically:

1. **Task 1: Enhance ProductTileData, ProductGrid with ellipsis menus, recipient count, and Add Product button** - `525ffe9` (feat)
2. **Task 2: ProductDetailPanel and AddProductForm Slint components** - `c342b4d` (feat)

## Files Created/Modified
- `crates/app/ui/option-grid.slint` - Enhanced ProductTileData struct and ProductGrid with ellipsis menus, recipient count, image display, empty state, Add Product button
- `crates/app/ui/product-detail.slint` - New ProductDetailPanel component with product metadata and serial units list
- `crates/app/ui/add-product-form.slint` - New AddProductForm component with name, Shopify URL auto-suggest, image preview
- `crates/app/ui/dashboard.slint` - Imports and integrates both new components with properties and callbacks
- `crates/app/src/main.rs` - Updated ProductTileData initializer with new default fields
- `crates/app/src/dashboard/discovery.rs` - Fixed wit_service -> service module path (pre-existing)

## Decisions Made
- Product initials placeholder uses "?" instead of first-letter extraction since Slint lacks string manipulation methods (to-upper-case, substring)
- ProductDetailPanel overlays right side of product grid, shrinking grid width when visible, for side-panel UX
- AddProductForm uses centered modal with semi-transparent backdrop and click-outside-to-dismiss
- Slint built-in `visible` property used instead of redeclaring it (Slint does not allow overriding built-in properties)

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Removed Slint string method calls that don't exist**
- **Found during:** Task 1
- **Issue:** Plan specified `tile-data.name.to-upper-case().substring(0, 1)` but Slint has no to-upper-case() or substring() methods on strings
- **Fix:** Used static "?" placeholder for initials instead
- **Files modified:** crates/app/ui/option-grid.slint
- **Verification:** cargo build -p app exits 0
- **Committed in:** 525ffe9

**2. [Rule 1 - Bug] Removed visible property redeclaration from ProductDetailPanel and AddProductForm**
- **Found during:** Task 2
- **Issue:** Slint Rectangle has a built-in `visible` property; redeclaring it as `in property <bool> visible` causes "Cannot override property 'visible'" compile error
- **Fix:** Removed redeclarations, control visibility via parent if-conditionals in dashboard.slint
- **Files modified:** crates/app/ui/product-detail.slint, crates/app/ui/add-product-form.slint, crates/app/ui/dashboard.slint
- **Verification:** cargo build -p app exits 0
- **Committed in:** c342b4d

**3. [Rule 3 - Blocking] Fixed wit_service module path in discovery.rs**
- **Found during:** Task 2
- **Issue:** Pre-existing error from Plan 17-01 used `wit_service::db::sqlite::ProductRow` instead of `service::db::sqlite::ProductRow`
- **Fix:** Replaced all occurrences of wit_service with service
- **Files modified:** crates/app/src/dashboard/discovery.rs
- **Verification:** cargo build -p app exits 0
- **Committed in:** c342b4d

---

**Total deviations:** 3 auto-fixed (2 bugs, 1 blocking)
**Impact on plan:** All auto-fixes necessary for compilation. No scope creep.

## Issues Encountered
None beyond the auto-fixed deviations above.

## Known Stubs
- ProductGrid ellipsis callbacks (ellipsis-set-image, ellipsis-view-shopify, ellipsis-detail, ellipsis-archive, add-product-clicked) have empty handler bodies in dashboard.slint -- wiring to Rust happens in Plan 04
- ProductDetailPanel placeholder initial "?" does not extract first letter of product name -- Slint limitation, Rust side can populate initial in Plan 04

## User Setup Required
None - no external service configuration required.

## Next Phase Readiness
- All UI components compile and are integrated into dashboard
- Plan 04 can wire Rust callbacks to the new Slint properties and callbacks
- ProductTileData new fields (product_id, recipient_count, has_image, image, shopify_linked) need population from Rust side

---
*Phase: 17-gh-issues-client-and-product-catalog*
*Completed: 2026-03-25*

## Self-Check: PASSED

All created files verified present. Both task commits (525ffe9, c342b4d) verified in git log.
