simpleshop_theme/PROGRESS.md
jamey 57c3ba0e28 wire shop LiveViews to DB queries and improve search UX
Replace PreviewData indirection in all shop LiveViews with direct
Products context queries. Home, collection, product detail and error
pages now query the database. Categories loaded once in ThemeHook.
Cart hydration no longer falls back to mock data. PreviewData kept
only for the theme editor.

Search modal gains keyboard navigation (arrow keys, Enter, Escape),
Cmd+K/Ctrl+K shortcut, full ARIA combobox pattern, LiveView navigate
links, and 150ms debounce. SearchModal JS hook manages selection
state and highlight. search.ex gets transaction safety on reindex
and a public remove_product/1. 10 new integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 08:27:26 +00:00

342 lines
22 KiB
Markdown

# SimpleShop Progress
> Single source of truth for project status and task tracking.
## Current Status
**Working:**
- Theme editor with 8 presets, instant switching, full customization
- Image optimization pipeline (AVIF/WebP/JPEG responsive variants)
- Shop pages (home, collections, products, cart, about, contact, error, delivery, privacy, terms)
- Mobile-first design with bottom navigation
- 100% PageSpeed score
- Variant selector with color swatches and size buttons
- Session-based cart with real variant data (add/remove/quantity, cross-tab sync)
- Cart drawer and cart page with hydrated product info
- Stripe Checkout with order persistence and webhook handling
- Admin credentials page with guided Stripe setup flow
- Encrypted settings for API keys and secrets
- FTS5 full-text search with live results modal
- Denormalized product fields (cheapest_price, in_stock, on_sale) for DB-level sort/filter
- Transactional emails (order confirmation, shipping notification)
- Demo content polished and ready for production
**Tier 1 MVP complete.** CI pipeline done. Hosting & deployment done (including observability). PageSpeed CI done (99-100 mobile, 97+ desktop). Usability fixes 15/18 done (remaining 3 are now tracked as features below).
## Task list
Ordered by dependency level — admin shell chain first (unblocks most downstream work).
Plans: [admin-redesign.md](docs/plans/admin-redesign.md) | [setup-wizard.md](docs/plans/setup-wizard.md) | [search.md](docs/plans/search.md) | [products-refactor.md](/home/jamey/.claude/plans/snug-roaming-zebra.md)
| # | Task | Depends on | Est | Status |
|---|------|------------|-----|--------|
| | **Done** | | | |
| ~~2~~ | ~~`site_live` setting + `Settings.site_live?/0`~~ | — | 30m | done |
| ~~3~~ | ~~`Accounts.has_admin?/0` + registration lockdown~~ | — | 1h | done |
| ~~8~~ | ~~Coming soon page~~ | 2 | 1h | done |
| ~~9~~ | ~~`Setup.setup_status/0` helper~~ | 2, 3 | 30m | done |
| ~~10~~ | ~~ThemeHook gate (redirect to coming soon)~~ | 2, 8 | 30m | done |
| ~~14~~ | ~~Go live / take offline toggle (on settings page)~~ | 2 | 30m | done |
| ~~1~~ | ~~Filesystem restructure (consolidate live/ directories)~~ | — | 2h | done |
| ~~6~~ | ~~Admin shell component (sidebar nav, header)~~ | 1 | 2-3h | done |
| ~~7~~ | ~~Admin root + child layout templates~~ | 1 | 1h | done |
| ~~11~~ | ~~Theme editor back-to-admin link~~ | 6 | 30m | done |
| ~~4~~ | ~~Admin bar on shop pages~~ | — | 1h | done |
| ~~12~~ | ~~Consolidate settings page~~ | 6, 7 | 2-3h | done |
| ~~13~~ | ~~Admin dashboard (+ setup checklist)~~ | 6, 7, 9 | 2h | done |
| ~~15~~ | ~~Setup wizard + admin tests~~ | 13 | 1.5h | done |
| ~~5~~ | ~~Search (functional search with results)~~ | — | 3-4h | done |
| ~~17~~ | ~~Wire shop LiveViews to DB queries (replace PreviewData indirection)~~ | — | 2-3h | done |
| | **Next up** | | | |
| 16 | Variant refinement with live data | — | 2-3h | |
| 18 | Shipping costs at checkout | 17 | 2-3h | |
| | **CSS migration (after admin stable)** | | | |
| 19 | Admin design tokens (`admin-tokens.css`) | 12 | 30m | |
| 20 | Admin component styles (`app-admin.css`) | 19 | 3-4h | |
| 21 | Migrate core_components.ex off DaisyUI | 20 | 2h | |
| 22 | Remove DaisyUI | 21 | 1h | |
| 23 | CSS migration tests + visual QA | 22 | 1h | |
**Total remaining: ~27-33 hours across ~12 sessions**
## Usability fixes (15/18 done)
Issues from hands-on testing of the deployed prod site (Feb 2025). 15 of 18 complete. The remaining 3 are tracked as features in the task list above (#5 search, #16 variant refinement, #18 shipping costs).
## Roadmap
### Tier 1 — MVP (can take real orders and fulfil them)
1. ~~**Order management admin**~~ — ✅ Complete (02cdc81). Admin UI at `/admin/orders` with status filter tabs, streamed order table, and detail view showing items, totals, and shipping address.
2. ~~**Orders & fulfilment**~~ — ✅ Complete. Submit paid orders to Printify, track fulfilment status (submitted → processing → shipped → delivered), webhook-driven status updates with polling fallback, admin UI with submit/refresh actions.
3. ~~**Transactional emails**~~ — ✅ Complete. Plain text order confirmation (on payment via Stripe webhook) and shipping notification (on dispatch via Printify webhook + polling fallback). OrderNotifier module, 10 tests.
4. ~~**Default content pages**~~ — ✅ Complete (5a43cfc). Generic `ShopLive.Content` LiveView handles about + 3 policy pages (delivery, privacy, terms) via `live_action`. Rich text with list blocks, footer links updated, theme editor preview. 10 tests (575 total).
### Tier 2 — Production readiness (can deploy and run reliably)
5. ~~**Hosting & deployment**~~ — ✅ Complete. Alpine Docker image (131 MB), Fly.io config, release overlays, health check endpoint, hardcoded path fixes for releases. Observability: LiveDashboard in prod behind admin auth, ErrorTracker for exception capture, JSON structured logging, Oban/LiveView telemetry metrics, os_mon for CPU/disk/memory.
6. **Litestream / SQLite replication** — Litestream for continuous SQLite backup to S3-compatible storage. Point-in-time recovery. Simple sidecar process, no code changes needed, works with vanilla SQLite. For the hosted platform (Tier 5), evaluate [Turso](https://turso.tech/) (libSQL fork of SQLite) with embedded read replicas via [ecto_libsql](https://github.com/ocean/ecto_libsql) adapter — gives multi-node reads without a separate replication daemon, but adds a dependency on the libSQL fork.
7. ~~**CI pipeline**~~ — ✅ Complete. `mix ci` alias: compile --warning-as-errors, deps.unlock --unused, format --check-formatted, credo, dialyzer, test. Credo configured with sensible defaults. Dialyzer with ignore file for false positives (Stripe types, Mix tasks, ExUnit internals). 612 tests, 0 failures.
8. ~~**PageSpeed in CI**~~ — ✅ Complete. `mix lighthouse` task runs Google Lighthouse against the shop with configurable thresholds. Builds production assets (minified + digested + gzipped), waits for image variant cache, checks all 4 categories. Mobile: 99-100, Desktop: 97-100 across all pages. Unconditional gzip on Plug.Static.
9. **End-to-end & accessibility tests** — Wallaby browser tests for critical flows (browse → add to cart → checkout → order confirmation) with A11yAudit assertions baked into each test. Covers the happy path, key error cases, and WCAG 2.1 AA compliance in one pass. Wallaby drives a headless Chrome, A11yAudit wraps axe-core for automated a11y checks within ExUnit. Focus management, ARIA labels, keyboard navigation, colour contrast — all verified as part of the e2e suite rather than a separate audit.
### Tier 3 — Compliance & quality
10. **Privacy-respecting analytics** — Self-hosted, cookie-free analytics. Plausible, Umami, or a lightweight custom solution. No Google Analytics, no third-party tracking. GDPR-friendly by design.
11. **AGPL licensing & code hosting** — Currently AGPL-3.0. Decide on GitHub vs Codeberg vs self-hosted Forgejo. Set up proper LICENSE file, contribution guidelines, and release process.
12. **Security (Paraxial.io)** — Runtime application security monitoring for Elixir. Bot detection, rate limiting, vulnerability scanning. Evaluate whether it fits the self-hosted model.
### Tier 4 — Growth & content
13. **Page editor** — Database-driven pages with drag-and-drop sections. Extend the theme system to custom pages beyond the defaults. Replaces the static content pages from Tier 1 with editable versions. **Removes PreviewData usage from `content.ex`** (about, delivery, privacy, terms content blocks are currently hardcoded in PreviewData).
14. **Newsletter & email marketing** — Email list collection (signup forms). Campaign sending for product launches, sales. Can be simple initially (collect emails, send via Swoosh) or integrate with a service.
15. **Product page improvements** — Pre-checkout variant validation (verify Printify availability). Cost change monitoring/alerts. Better image gallery (zoom, multiple angles). **Product reviews system** to replace the hardcoded `PreviewData.reviews()` on the PDP template.
### Tier 5 — Platform vision
16. **Hosted platform** — Marketing/brochure site for SimpleShop as a service. Subscribe/sign-up flow. Multi-tenancy with per-tenant databases. Stripe Connect for customer shops (each merchant connects their own Stripe account via OAuth).
17. **Migration & export** — Let shop owners export their data (products, orders, customers, theme settings). Import from other platforms (Shopify, WooCommerce). Portable data as a selling point for the self-hosted story.
18. **Internationalisation (i18n)** — Multi-language support via Gettext (already in Phoenix). Currency formatting. RTL layout support. Per-shop locale configuration. **Note:** `ex_money`/`ex_cldr` are currently used *only* for `Cart.format_price/1` (a single GBP formatting call) but add ~13 MB to the release (ex_cldr 9.5 MB, digital_token 3.7 MB, ex_cldr_numbers, ex_cldr_currencies). Consider replacing with a simple `format_price/2` function that handles GBP/EUR/USD directly — all three use 2 decimal places and are trivial to format. Re-add `ex_money` later if proper locale-aware number formatting is needed (e.g., German `12.345,67 €`).
---
## Feature Areas
### Theme System
**Status:** Complete
- 8 theme presets (Gallery, Studio, Boutique, etc.)
- Three-layer CSS architecture (primitives, attributes, semantic)
- Instant theme switching via CSS custom property injection
- Logo/header image uploads with SVG recoloring
- Self-hosted fonts (10 typefaces, GDPR compliant)
- ETS-cached CSS generation
### Image Optimization
**Status:** Complete
- Oban background job processing
- Responsive `<picture>` element (AVIF/WebP/JPEG)
- Only generates sizes <= source dimensions
- Disk cache for variants (regenerable from DB)
- `mix optimize_images` task for mockups
- On-demand JPEG fallback generation
- Product image download pipeline (downloads Printify CDN images, processes through Media pipeline)
- ImageDownloadWorker downloads and links images to ProductImage
- PreviewData uses local images for responsive `<picture>` elements
- Startup recovery re-enqueues pending downloads
- `mix simpleshop.download_images` backfill task
See: [docs/plans/image-optimization.md](docs/plans/image-optimization.md) for implementation details
### Products & Provider Integration
**Status:** Complete
- [x] Products context with schemas (c5c06d9)
- [x] Provider abstraction layer
- [x] Printify client integration
- [x] Product/variant/image schemas
- [x] Admin Provider Setup UI (`/admin/providers`) - connect, test, sync
- [x] ProductSyncWorker with pagination, parallel processing, error recovery
- [x] Slug-based fallback matching for changed provider IDs
- [x] Printify webhook endpoint with HMAC verification (a9c15ea)
- Note: Printify only supports `product:deleted` and `product:publish:*` events (no `product:updated`)
- [x] Product image download pipeline (1b49b47)
- Downloads Printify CDN images via ImageDownloadWorker
- Processes through Media pipeline (WebP conversion, AVIF/WebP variants)
- Startup recovery and `mix simpleshop.download_images` backfill
- [x] Variant selector component (880e7a2)
- Color swatches with hex colors, size buttons
- Fixed Printify options parsing (Color/Size swap bug)
- Filters to only published variants (not full catalog)
- Price updates on variant change
- Startup recovery for stale sync status
#### Future Enhancements (post-MVP)
- [ ] Pre-checkout variant validation (verify availability before order)
- [ ] Cost change monitoring/alerts (warn if Printify cost increased)
- [ ] OAuth platform integration (appear in Printify's "Publish to" UI)
#### Technical Debt
- [x] Mox provider mocking for fulfilment tests (Provider.for_type configurable via app env)
See: [docs/plans/products-context.md](docs/plans/products-context.md) for implementation details
See: [docs/plans/printify-integration-research.md](docs/plans/printify-integration-research.md) for API research & risk analysis
### Cart & Checkout
**Status:** Complete
- [x] Cart drawer component with slide-over panel (1bc08bf)
- [x] Cart page with item list and order summary (1bc08bf)
- [x] Shared CartHook for cross-page cart events (1bc08bf)
- [x] CartPersist JS hook for localStorage backup
- [x] Add-to-cart with flash status feedback
- [x] Cart item links to product pages
- [x] Session-based cart with real variants (1bc08bf)
- Cart stores {variant_id, qty} tuples in session
- Hydrates with real product data via Products context
- Cross-tab sync via PubSub, session persistence via CartController API
- [x] Stripe Checkout integration (ff1bc48, stripity_stripe ~> 3.2)
- Stripe-hosted Checkout with redirect flow
- Webhook handler for checkout.session.completed/expired
- Signature verification via CacheRawBody + construct_event
- Shipping address collection during checkout
- [x] Order/OrderItem schemas and context (ff1bc48)
- Order number format: SS-YYMMDD-XXXX
- Payment status tracking (pending → paid/failed)
- Price snapshots in OrderItem (protects against changes)
- Idempotent webhook processing
- [x] Checkout success page with real-time PubSub updates
- [x] Cart clearing after successful payment
See: [ROADMAP.md](ROADMAP.md) for design notes
### Admin Settings & Stripe Setup
**Status:** Complete
- [x] Encrypted settings infrastructure (eede9bb)
- `encrypted_value` column on settings table
- `put_secret/2`, `get_secret/2`, `has_secret?/1`, `secret_hint/1`
- AES-GCM encryption via `SECRET_KEY_BASE`
- Secrets loaded into Application env on startup via `Secrets.load_all/0`
- [x] Guided Stripe setup flow (eede9bb)
- `Stripe.Setup` module: connect, disconnect, verify, auto-create webhook
- Three-state admin UI: not configured, connected (production), connected (dev/localhost)
- Auto-creates Stripe webhook endpoint on production hosts
- Dev mode shows Stripe CLI instructions for localhost
- Manual signing secret input for dev/advanced use
- API key verification via `Stripe.Balance.retrieve/2`
- [x] Admin credentials page at `/admin/settings` (eede9bb)
- Single Secret key input with "Connect Stripe" button
- Masked key hints (e.g. `sk_test_•••789`)
- Disconnect button clears keys from DB and Application env
- CSSCache test startup crash fixed (handle_continue pattern)
### Orders & Fulfilment
**Status:** Complete
- [x] Orders context with schemas (ff1bc48)
- [x] Stripe Checkout integration with webhook handling
- [x] Order management admin UI (02cdc81, Roadmap #1)
- Order list with status filter tabs (all/paid/pending/failed/refunded) and counts
- Streamed table with row click navigation to detail
- Order detail with info card, shipping address, line items table with totals
- Nav link in admin bar, 15 tests
- [x] Printify order submission and fulfilment tracking (Roadmap #2)
- 9 fulfilment fields on orders (status, provider_order_id, tracking, timestamps)
- `submit_to_provider/1` with idempotent guard, error handling, address mapping
- `refresh_fulfilment_status/1` polls provider for status updates
- OrderSubmissionWorker (Oban, :checkout queue, max_attempts: 3)
- FulfilmentStatusWorker (Oban Cron, every 30 mins, :sync queue)
- Printify order webhook handlers (sent-to-production, shipment:created, shipment:delivered)
- Stripe webhook auto-enqueues submission after payment confirmed
- Admin UI: fulfilment badge column, fulfilment card with tracking, submit/refresh buttons
- Mox provider mocking for test isolation, 33 new tests (555 total)
- [x] Transactional emails (Roadmap #3)
- OrderNotifier module with plain text emails via Swoosh
- Order confirmation sent from Stripe webhook after payment + address/email updates
- Shipping notification sent from Printify shipment webhook + polling fallback
- Guards for missing customer_email, graceful tracking info handling
- 10 tests (565 total)
See: [docs/plans/products-context.md](docs/plans/products-context.md) for schema design
### DRY Refactor
**Status:** Complete
All 8 items from the plan done. Key wins: ThemeHook eliminated mount duplication, shop_layout saved ~195 lines, shop_components split into 5 focused modules (largest file dropped from 4,487 to ~1,600 lines), Settings repo lookups consolidated via `fetch_setting/1`, secrets loading made scalable via registry pattern.
See: [docs/plans/dry-refactor.md](docs/plans/dry-refactor.md) for full analysis and plan
### Shop Page Integration Tests
**Status:** Complete
All shop pages now have LiveView integration tests (612 total):
- **Product detail page** (15 tests) — rendering, breadcrumbs, variant selection, price updates, add-to-cart, related products, fallback for unknown IDs
- **Cart page** (10 tests) — empty state, item display with DB fixtures, order summary, increment/decrement, remove, checkout button
- **Home page** (12 tests) — hero section, category nav, featured products, image+text section, navigation links
- **Collection page** (16 tests, pre-existing) — category filtering, sorting, URL params
- **Content pages** (10 tests, pre-existing) — about, delivery, privacy, terms
### CI Pipeline
**Status:** Complete
- `mix ci` alias: compile --warning-as-errors → deps.unlock --unused → format --check-formatted → credo → dialyzer → test
- `mix precommit` alias: compile --warning-as-errors → deps.unlock --unused → format → test
- Credo with tuned config (disabled AliasUsage, ModuleDoc, PredicateFunctionNames; relaxed line length, nesting, complexity)
- Dialyzer with ignore file for known false positives (Stripe library types, Mix.Task dev-only modules, ExUnit internals)
- All credo issues resolved (map_join, filter consolidation, nesting extraction)
- 612 tests, 0 failures
### Hosting & Deployment
**Status:** Complete
- Alpine Docker image (131 MB), Fly.io config, release overlays
- Health check endpoint at `/health`
- Hardcoded path fixes for releases (`Application.app_dir/2`)
- LiveDashboard at `/admin/dashboard` behind admin auth (all envs)
- ErrorTracker at `/admin/errors` — auto-captures Phoenix/LiveView/Oban exceptions, stored in SQLite, pruner for resolved errors
- JSON structured logging in prod via `logger_json` (machine-parseable)
- Oban job duration/count and LiveView mount/event telemetry metrics
- os_mon for CPU, disk, and OS memory in LiveDashboard
### Products Refactor & Search
**Status:** Complete
- [x] Denormalized product fields (cheapest_price, compare_at_price, in_stock, on_sale) recomputed from variants after sync
- [x] Product display helpers (primary_image, hover_image, option_types) and ProductImage helpers (display_url, direct_url, source_width)
- [x] Products context storefront queries (list_visible_products, get_visible_product, list_categories) with DB-level sort/filter
- [x] Renamed .name → .title across all shop components and templates
- [x] PreviewData updated to struct-compatible format (removed product_to_map indirection)
- [x] FTS5 full-text search index with BM25 ranking (title 10x, category 5x, variants 3x, description 1x)
- [x] SearchHook on_mount for all shop pages (follows CartHook pattern)
- [x] Search modal with live results (thumbnails, titles, categories, prices, click-to-navigate)
- [x] Index auto-rebuilds after each provider sync
- [x] 18 search tests, 744 total
**Follow-ups:**
- [x] Wire shop LiveViews to direct DB queries (PreviewData removed from all shop pages, cart, error page)
- [ ] Keyboard navigation in search modal (up/down arrows, Enter to navigate)
### Page Editor
**Status:** Future (Tier 4)
Database-driven pages with drag-and-drop sections. Initially, default content pages (terms, privacy, delivery) will be static templates (Tier 1), later made editable via the page editor.
See: [docs/plans/page-builder.md](docs/plans/page-builder.md) for design
---
## Completed Work Reference
| Feature | Commit | Notes |
|---------|--------|-------|
| FTS5 search + products refactor | 037cd16 | FTS5 index, BM25 ranking, search modal, denormalized fields, Product struct usage, 744 tests |
| PageSpeed CI | 516d0d0 | `mix lighthouse` task, prod asset build, gzip, 99-100 mobile scores |
| Observability | eaa4bbb | LiveDashboard in prod, ErrorTracker, JSON logging, Oban/LV metrics, os_mon |
| Hosting & deployment | — | Alpine Docker, Fly.io, health check, release path fixes |
| PDP image gallery | 8445e9e | Scroll-snap carousel, dots, thumbnails, arrows, lightbox, 9 tests |
| CI pipeline | — | mix ci/precommit aliases, credo, dialyzer, 621 tests |
| Default content pages | 5a43cfc | Generic Content LiveView, delivery/privacy/terms pages, 10 tests |
| Transactional emails | — | Plain text order confirmation + shipping notification, 10 tests |
| Printify order submission & fulfilment | — | Submit, track, webhooks, polling, admin UI, 33 tests |
| Order management admin | 02cdc81 | List/detail views, status filters, 15 tests |
| Encrypted settings & Stripe setup | eede9bb | Guided setup flow, encrypted secrets, admin credentials page |
| Stripe checkout & orders | ff1bc48 | Stripe Checkout, webhooks, order persistence |
| Demo content & link fixes | cff2170 | Broken links, placeholder text, responsive about image |
| Cart UI infrastructure | 1bc08bf | Cart drawer, cart page, CartHook, CartPersist |
| Variant selector | 880e7a2 | Color swatches, size buttons, price updates |
| Product image download | 1b49b47 | PageSpeed 100% with local images |
| Wire shop to real data | c818d03 | PreviewData uses Products context |
| Printify webhooks | a9c15ea | Deletion + publish events |
| Products context Phase 1 | c5c06d9 | Schemas, provider abstraction |
| Admin provider setup UI | 5b736b9 | Connect, test, sync with pagination |
| Oban Lifeline plugin | c1e1988 | Rescue orphaned jobs |
| Image optimization | Multiple | Full pipeline complete |
| Self-hosted fonts | - | 10 typefaces, 728KB |
| Mobile bottom nav | - | Fixed tab bar |
| PageSpeed 100% | - | All optimizations |
| Theme presets (8) | - | Gallery, Studio, etc. |