Admins can now edit pages directly on the live shop by clicking the
pencil icon in the header. A sidebar slides in with block management
controls (add, remove, reorder, edit settings, save, reset, done).
Key features:
- PageEditorHook on_mount with handle_params/event/info hooks
- BlockEditor pure functions extracted from admin editor
- Shared BlockEditorComponents with event_prefix namespacing
- Collapsible sidebar: X closes it, header pencil reopens it
- Backdrop overlay dismisses sidebar on tap
- Conditional admin.css loading for logged-in users
- content_body block now portable (textarea setting + rich text fallback)
13 integration tests, 26 unit tests, 1370 total passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stage 6 of the page builder: admin UI at /admin/pages for managing
page layouts. Page list shows all 14 pages grouped by category.
Editor supports reorder (up/down), add, remove, duplicate, save,
and reset to defaults. DirtyGuard JS hook warns on unsaved changes.
ARIA live regions announce block operations for screen readers.
Also: regenerate admin icons (81 rules via mix task with @layer
wrapping), add gen_smtp dep for SMTP email adapter, add :key to
page renderer block loop for correct LiveView diffing.
1309 tests, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All 14 pages now render through PageRenderer. Theme editor preview
unified from 10 preview_page clauses to one function + page-context
helpers. PageTemplates module and 10 .heex template files deleted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redirects context with redirect/broken_url schemas, chain flattening,
ETS cache for fast lookups in the request pipeline. BrokenUrlTracker
plug logs 404s. Auto-redirect on product slug change via upsert_product
hook. Admin redirects page with active/broken tabs, manual create form.
RedirectPrunerWorker cleans up old broken URLs. 1227 tests passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Variant options (colour, size) are now URL params handled via
handle_params instead of phx-click events. Swatches and size buttons
render as patch links in shop mode, so changing variants works as
plain navigation without JS. Quantity is now a number input that
submits with the form. Unavailable variants render as disabled spans.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The delivery country form now has action="/cart/country" with a
noscript submit button. Without JS, changing the country and clicking
Update POSTs to a new CartController.update_country action that saves
the country to session and redirects back to /cart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both order tracking forms now have action="/contact/lookup" so they
POST to a new OrderLookupController.lookup action when JS is off.
The controller mirrors the LiveView handler: checks for paid orders,
sends the verification email, and redirects with a flash message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The button type condition checked @mode == :live but ThemeHook sets
mode to :shop, so the button rendered as type="button" (doing nothing).
Changed to @mode == :preview to match the existing phx-click pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every key shop flow now works via plain HTML forms when JS is
unavailable. LiveView progressively enhances when JS connects.
- PDP: form wraps variant/qty/add-to-cart with action="/cart/add"
- Cart page: qty +/- and remove use form POST fallbacks
- Cart/search header icons are now links with phx-click enhancement
- Collection sort form has GET action + noscript submit button
- New /search page with form-based search for no-JS users
- CartController gains add/remove/update_item POST actions
- CartHook gains update_quantity_form and remove_item_form handlers
- Fix flaky analytics tests caused by event table pollution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upload a source image (PNG, JPEG, or SVG) and get a complete favicon
setup: PNG variants at 32, 180, 192, 512px served from DB via
FaviconController with ETag caching, SVG favicon for vector sources,
dynamic site.webmanifest, and theme-color meta tag. Theme editor gains
a site icon section with "use logo as icon" toggle, dedicated icon
upload, short name, and background colour picker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The stretched-link::after overlay (z-index: 0) was blocked by
product-card-image-wrap (z-index: 1), so only the title text was
actually clickable. Wrapping the image area in a <.link> component
directly fixes this — taps/clicks bubble up to the link naturally,
and touch-scroll on the image carousel still works on mobile.
Also corrects the mode check: ThemeHook sets mode: :shop on shop pages,
not :live, so the condition is now mode != :preview (consistent with
how the title link already worked).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Capitalise lead sentence regardless of shop_name value
- Add stripe.com/privacy URL when mentioning Stripe in payment section
- Remove mention of logout from session cookie description
- Make third-party sharing text provider-agnostic (no longer names Printify etc.)
- Add :updated_at block to privacy, delivery, and terms pages showing when
content last changed — auto-tracked via content hash, so the date advances
automatically whenever relevant settings change
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Magic link flow on contact page: customer enters email, gets a
time-limited signed link, clicks through to /orders showing all their
paid orders and full detail pages with thumbnails and product links.
- OrderLookupController generates/verifies Phoenix.Token signed links
- Contact LiveView handles lookup_orders + reset_tracking events
- Orders and OrderDetail LiveViews gated by session email
- Order detail shows thumbnails, links to products still available
- .themed-button gets base padding/font-weight so all usages are consistent
- order-summary-card sticky scoped to .cart-grid (was leaking to orders list)
- 27 new tests (1095 total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Product pages: Product schema (name, description, image, price/currency,
availability) + BreadcrumbList (Home > Category > Product). Home page:
Organization schema (name, url). Uses Jason with html_safe escaping so
the JSON is safe to embed in <script> tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Canonical: all shop pages now assign og_url (reusing the existing og:url
assign), which the layout renders as <link rel="canonical">. Collection
pages strip the sort param so ?sort=price_asc doesn't create a duplicate
canonical.
robots.txt: dynamic controller disallows /admin/, /api/, /users/,
/webhooks/, /checkout/. Removed robots.txt from static_paths so it
goes through the router instead of Plug.Static.
sitemap.xml: auto-generated from all visible products + categories +
static pages, served as application/xml. 8 tests.
Also updates PROGRESS.md: marks tasks 55, 58, 59, 61, 62 as done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Product pages get og:type=product, og:url, og:image (hero image), and
twitter:card=summary_large_image. All other shop pages get og:type=website
and twitter:card=summary. og:title and og:description mirror the existing
page title and meta description.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All shop pages now render as "Page · Store Name" using live_title suffix.
Home stays as "Home · Store Name" for consistency with live navigation.
Updated cart test to use regex for whitespace-tolerant title matching.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three-layer pipeline: Plug for all HTTP requests (no JS needed), LiveView
hook for SPA navigations, JS hook for screen width. ETS-backed buffer
batches writes to SQLite every 10s. Daily-rotating salt for visitor hashing.
Includes admin dashboard with date ranges, visitor trends, top pages,
sources, devices, and e-commerce conversion funnel. Oban cron for 12-month
data retention.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Card radio component for picking email providers (SMTP, SendGrid, Mailjet, etc.)
with instant client-side switching via JS hook. Adapter configs are pre-rendered
and toggled without a server round-trip. Secrets are preserved when re-saving
with blank password fields. Includes from address field, test email sending,
and disconnect flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Setup wizard no longer requires email delivery. Admin account is
auto-confirmed and auto-logged-in via token redirect. Adds setup
secret gate for prod (logged on boot), SMTP env var config in
runtime.exs, email_configured? helper, and admin warning banner
when email isn't set up. Includes plan files for this task and
the follow-up email settings UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 7: add LoadTheme to base :browser pipeline so auth and setup
pages get theme settings. Update root.html.heex with .themed wrapper,
font preloads, layer declaration, and generated CSS injection.
Remove old data-theme JS toggle script.
Phase 8: upgrade admin/reset.css to a proper @layer reset matching
the shop reset structure. Remove dead theme toggle CSS rules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 4: admin components and utilities now reference --t-* theme
tokens directly. Status colour tokens added to theme-semantic.css.
Bridge file (admin/themes.css) deleted.
Phase 5: removed duplicated .preview-frame CSS block (~160 lines).
Admin components and icons wrapped in @layer admin. Layer order
updated in admin_root to include admin layer.
Phase 6: added prefers-reduced-motion support (zeroes all durations
and disables animations). Migrated physical properties to logical
equivalents (text-align start/end, margin-inline, padding-inline,
inset-inline-end) across shop and admin CSS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 3: Rewrite admin/themes.css as a bridge mapping DaisyUI --color-*
variables to --t-* theme tokens. Admin components still reference
--color-base-100, --color-primary etc but these now resolve through the
bridge to the merchant's theme colours. Status colours (error, success,
warning, info) stay hardcoded.
Add .themed wrapper with data-mood to admin_root.html.heex. Remove the
old data-theme JS toggle script — dark mode now comes from the theme
system mood setting. Admin inherits theme colours, typography, and shape
from the merchant's chosen theme.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1: Replace hex_to_hsl with hex_to_oklch in CSSGenerator, output
--t-accent-l/c/h instead of --t-accent-h/s/l. All 46 HSL accent
references across theme-semantic.css, theme-layer2-attributes.css, and
shop/components.css replaced with oklch/color-mix equivalents. Dead
style*= attribute selectors for button variants replaced with proper
class-based selectors. Added color-scheme: light/dark to mood output.
Phase 2: Add LoadTheme plug to admin pipeline, extend AdminLayoutHook
with theme_settings and generated_css assigns, add font preloads and
generated CSS injection to admin_root.html.heex. No visual changes to
admin yet — .themed wrapper added in next phase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Coming soon page was unstyled after Tailwind removal — replaced
utility classes with proper component CSS. Also removed dead h-full
classes from shop_root layout, adding height: 100% to the shop reset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Providers was a hidden page with no nav link. Also adds
LiveDashboard and ErrorTracker to the sidebar footer, and
styles the provider dropdown menu items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the setup stepper out of the dashboard into its own LiveView.
Dashboard now redirects to setup when site isn't live, and shows
stats-only view once live. Also cleans up button component variant
handling, fixes alert CSS, and removes stale demo.html.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>