Replace hardcoded zinc/white Tailwind colors with DaisyUI semantic
colors (base-content, base-200, etc.) that adapt to light/dark themes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3-step vertical stepper with inline forms for Printify and Stripe,
real-time sync progress via PubSub, and celebration state on go-live.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-tenant app: every user is the admin. The previous pattern
match on conn.assigns.current_scope didn't work for first-time
logins because the scope isn't assigned to the conn yet at that
point, causing the fallback to ~p"/" instead of ~p"/admin".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check socket.assigns.current_scope (validated by mount_current_scope)
instead of raw session token. Prevents stale/invalid session cookies
from bypassing the site-live gate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fresh installs now redirect to /users/register instead of showing the
demo shop. Post-login redirect goes to /admin dashboard (with setup
checklist) instead of /admin/settings. Added 16 provider tests covering
index (list, delete, sync) and form (new, edit, test connection).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dashboard at /admin shows setup progress (when not live), stat cards
(orders, revenue, products), and recent paid orders table. Replaces
the old AdminController redirect. Add Dashboard to sidebar nav as
first item, update admin bar and theme editor links to /admin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge shop status, payments, products (Printify), account (email/password),
and advanced (dashboard/error tracker links) into /admin/settings. Simplify
Auth.Settings to a redirector for /users/settings and confirm-email tokens.
Remove Providers from sidebar nav. Update all redirects and tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New admin root + child layouts with daisyUI drawer sidebar
- AdminLayoutHook tracks current path for active nav highlighting
- Split router into :admin, :admin_theme, :user_settings live_sessions
- Theme editor stays full-screen with back link to admin
- Admin bar on shop pages for logged-in users (mount_current_scope)
- Strip Layouts.app wrapper from admin LiveViews
- Remove nav from root.html.heex (now only serves auth pages)
- 9 new layout tests covering sidebar, active state, theme editor, admin bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidates admin_live/, theme_live/, provider_live/ into admin/
(with theme/ and providers/ subdirs). Renames shop_live/ to shop/
and user_live/ to auth/. Updates all module names, router refs,
test files, CSS source paths, and dialyzer ignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Settings.site_live?/0 and set_site_live/1 for shop visibility control
- Accounts.has_admin?/0 to detect single-tenant admin existence
- Registration lockdown: /users/register redirects when admin exists
- Setup.setup_status/0 aggregates provider, product, and stripe checks
- Coming soon page at /coming-soon with themed styling
- ThemeHook :require_site_live gate on all public shop routes
- Site live → everyone through
- Authenticated → admin preview through
- No admin → fresh install demo through
- Otherwise → redirect to coming soon
- Go live / take offline toggle on /admin/settings
- 648 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All internal links used plain <a href>, causing full page reloads
between pages in the same live_session. Changed to <.link navigate>
for client-side navigation across layout, product, cart, base, and
content components.
Added explicit width/height attributes to all SVG icons so they
render at sensible sizes before CSS loads on initial page visit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- collection filter bar: horizontal scroll on mobile instead of wrapping
across 3 rows, smaller pills at mobile sizes
- add sale collection filter at /collections/sale (filters on_sale products)
- hero :page variant: add consistent top padding (var(--space-2xl))
- contact page: remove redundant top padding (hero handles it now)
- error page: fix CSS path from /assets/app.css to /assets/css/app.css
(broken in production due to asset fingerprinting)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The stretched-link ::after pseudo-element (z-index: 0) was covering
the image area, intercepting touch events. Give the image container
z-index: 1 so swipe gestures reach the scroll container.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
shop_footer now calls PreviewData.categories() itself, removing the
need to thread categories through CartHook, shop_layout, and all 8
page templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add missing cta_href to hero section and error page CTAs
- replace hardcoded footer shop links with real product categories
- restructure product cards with stretched-link pattern so category
badges link to their collection page
- unify social icons: footer and contact page share the same default
links from a single source in content.ex
- add search implementation plan (docs/plans/search.md, deferred)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The drawer now has full quantity controls, remove, subtotal, and
checkout — the link to the cart page added friction without value.
Cart page remains accessible via the basket icon in nav.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move increment/decrement handlers from Cart LiveView into CartHook so
they work from any page's drawer. Enable show_quantity_controls on the
drawer's cart_item_row. Scope cart tests to #main-content to avoid
duplicate button matches.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up +/− buttons with phx-click events and handle_event handlers,
clamp to 1–99, reset to 1 after add-to-cart. Trust badges now use a
single hero-check-circle icon and sentence case text.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrite breadcrumb as semantic ol/li with aria-current="page", CSS
chevron separators, 0.875em font size, and ellipsis truncation on
mobile for long product names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mobile: swipeable carousel with dot indicators, no lightbox trigger.
Desktop: carousel with thumbnail grid, prev/next arrows, click to
open existing lightbox. Keeps all lightbox appearance and behaviour.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Product cards now use CSS scroll-snap on touch devices (mobile) for
swiping between images, with dot indicators and a JS hook for active
state. Desktop keeps the existing hover crossfade via @media (hover:
hover). Dots use size differentiation (WCAG 2.2 AA compliant) with
outline rings for contrast on any background.
Also fixes: no-image placeholder (SVG icon instead of broken img),
unnecessary wrapper div for single-image cards, and dev static asset
caching (was immutable for all envs, now only prod).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add priority attr to product_card, thread through to responsive_image
- First 2 featured products get fetchpriority="high" and eager loading
- Remove longPollFallbackMs to avoid unnecessary fallback attempt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add @source for shop_components/ directory in app-shop.css (Tailwind
wasn't scanning sub-modules after the refactor, dropping ~73 utilities)
- Remove overly aggressive .dockerignore rules that excluded mockup
image variants needed by the responsive_image component
- Seed default theme settings on first boot via Release.seed_defaults/0
in the supervision tree (seeds.exs doesn't run in releases)
- Fix PDP gallery images for mock data by appending -1200.webp to
bare mockup base paths
- Update fly.toml format from fly launch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plug.Static with gzip: true serves .gz files when they exist
(created by phx.digest) and falls back to uncompressed otherwise.
The previous code_reloading? guard was needlessly conservative —
in dev without digested assets, there are no .gz files so nothing
changes. With the lighthouse task building prod assets, this gives
realistic transfer sizes (42KB JS vs 135KB).
Mobile lighthouse scores jump from 95-97 to 99-100.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move LiveDashboard to /admin/dashboard behind session auth (all envs)
- Add ErrorTracker at /admin/errors for auto-captured exceptions
- Add Oban job and LiveView metrics to telemetry module
- Add logger_json for structured JSON logs in production
- Enable os_mon for CPU/disk/memory in LiveDashboard OS Data tab
- Extend logger metadata with oban_worker and oban_queue fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Alpine multi-stage Dockerfile (131 MB image)
- Release overlays (bin/server, bin/migrate), env.sh, Release module
- Health check endpoint at GET /health
- Fly.io config with SQLite volume mount
- Fix hardcoded paths in optimizer.ex and variant_cache.ex to use
Application.app_dir/2 (breaks in releases where Plug.Static serves
from a different directory than CWD)
- strip_beams: true in release config
- Optimised .dockerignore and .gitignore for mockup variants
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
on_mount hook assigns theme_settings, generated_css, logo_image,
header_image, and mode for all public shop LiveViews. Removes
~70 lines of identical boilerplate and 18 unused aliases across
7 LiveViews.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace verbose explicit attr passing with {assigns} spread for
home, contact, cart, product show and checkout success LiveViews.
Collection skipped (inline rendering, no template component).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace one-off ShopLive.About with generic ShopLive.Content that
handles all static content pages via live_action. Add delivery &
returns, privacy policy, and terms of service pages with sample
content. Update footer help links and theme editor preview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plain text emails via Swoosh OrderNotifier module. Order confirmation
triggered from Stripe webhook after payment, shipping notification
from Printify shipment webhook with polling fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Submit paid orders to Printify via provider API with idempotent
guards, Stripe address mapping, and error handling. Track fulfilment
status through submitted → processing → shipped → delivered via
webhook-driven updates (primary) and Oban Cron polling fallback.
- 9 fulfilment fields on orders (status, provider IDs, tracking, timestamps)
- OrderSubmissionWorker with retry logic, auto-enqueued after Stripe payment
- FulfilmentStatusWorker polls every 30 mins for missed webhook events
- Printify order webhook handlers (sent-to-production, shipment, delivered)
- Admin UI: fulfilment column in table, fulfilment card with tracking info,
submit/retry and refresh buttons on order detail
- Mox provider mocking for test isolation (Provider.for_type configurable)
- 33 new tests (555 total), verified against real Printify API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin UI at /admin/orders to view, filter, and inspect orders.
Adds list_orders/1 and count_orders_by_status/0 to the Orders
context, status filter tabs, clickable order table with streams,
and a detail page showing items, totals, and shipping address.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store API keys and secrets encrypted in the SQLite database via the
existing Vault module (AES-256-GCM). The only external dependency is
SECRET_KEY_BASE — everything else lives in the portable DB file.
- Add encrypted_value column to settings table with new "encrypted" type
- Add put_secret/get_secret/delete_setting/secret_hint to Settings context
- Add Secrets module to load encrypted config into Application env at startup
- Add Stripe.Setup module with connect/disconnect/verify_api_key flow
- Auto-creates webhook endpoints via Stripe API in production
- Detects localhost and shows Stripe CLI instructions for dev
- Add admin credentials page at /admin/settings with guided setup:
- Not configured: single Secret key input with dashboard link
- Connected (production): status display, webhook info, disconnect
- Connected (dev): Stripe CLI instructions, manual signing secret input
- Remove Stripe env vars from dev.exs and runtime.exs
- Fix CSSCache test startup crash (handle_continue instead of init)
- Add nav link for Credentials page
507 tests, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stripe-hosted Checkout integration with full order lifecycle:
- stripity_stripe ~> 3.2 with sandbox/prod config via env vars
- Order and OrderItem schemas with price snapshots at purchase time
- CheckoutController creates pending order then redirects to Stripe
- StripeWebhookController verifies signatures and confirms payment
- Success page with real-time PubSub updates from webhook
- Shop flash messages for checkout error feedback
- Cart cleared after successful payment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace all placeholder text with demo-aware copy that signals "replace me"
- Update USPs for POD accuracy (made to order, quality materials)
- Fix broken footer links (/delivery, /returns → /contact)
- Add real platform URLs to social icons with target="_blank"
- Make cart item images and names link to product pages
- Switch about page image to responsive_image component
- Add missing cart_status to collection page cart drawer
- Unify search hint text across all page templates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cart context with pure functions for add/remove/update/hydrate
- Price formatting via ex_money (replaces all float division)
- CartHook on_mount with attach_hook for shared event handlers
(open/close drawer, remove item, PubSub sync)
- Accessible cart drawer with focus trap, scroll lock, aria-live
- Cart page with increment/decrement quantity controls
- Preview mode cart drawer support in theme editor
- Cart persistence to session via JS hook + API endpoint
- 19 tests covering all Cart pure functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Printify options parsing (Color/Size were swapped)
- Add extract_option_types/1 for frontend display with hex colors
- Filter option types to only published variants (not full catalog)
- Track selected variant in LiveView with price updates
- Color swatches for color-type options, text buttons for size
- Disable unavailable combinations
- Add startup recovery for stale sync status
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Downloads Printify CDN images via ImageDownloadWorker, processes
through Media pipeline (WebP conversion, AVIF/WebP variant generation),
and links to ProductImage via new image_id FK.
- Add image_id to product_images table
- ImageDownloadWorker downloads and processes external images
- sync_product_images preserves image_id when URL unchanged
- PreviewData uses local images for responsive <picture> elements
- VariantCache enqueues pending downloads on startup
- mix simpleshop.download_images backfill task
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PreviewData now queries the Products context when real products exist,
falling back to mock data otherwise. Shop pages automatically display
synced Printify products.
Fixes:
- Printify image position was string ("front"), now uses index
- Category extraction improved to match more Printify tags
- ProductShow finds products by slug for real data
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add /admin/providers LiveView for connecting and managing POD providers
- Implement pagination for Printify API (handles all products, not just first page)
- Add parallel processing (5 concurrent) for faster product sync
- Add slug-based fallback matching when provider_product_id changes
- Add error recovery with try/rescue to prevent stuck sync status
- Add checksum-based change detection to skip unchanged products
- Add upsert tests covering race conditions and slug matching
- Add Printify provider tests
- Document Printify integration research (product identity, order risks,
open source vs managed hosting implications)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pass allow_remote_access option directly to the Tidewave plug
instead of using application config (which was not being read).
Remove the ineffective config line from dev.exs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>