The shop_root.html.heex had duplicate .themed element with data-mood etc
attributes that were static (rendered once at page load). This prevented
live theme changes from visually updating since CSS matched the outer
stale element.
Fix: Remove data attributes from shop_root.html.heex, keeping only the
live-updated .shop-container.themed element inside the LiveView.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Phase 5 was already implemented (URL mode activation via ?edit param)
- Phase 6: Add RedirectController to redirect /admin/theme → /?edit=theme
- Update admin sidebar and dashboard links to point directly to /?edit=theme
- Delete old Admin.Theme.Index LiveView and template (no longer needed)
- Update tests for new redirect behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace individual shop LiveViews with a single Shop.Page that dispatches
to page modules based on live_action. This enables patch navigation between
pages, preserving socket state (including editor state) across transitions.
Changes:
- Add Shop.Page unified LiveView with handle_params dispatch
- Extract page logic into Shop.Pages.* modules (Home, Product, Collection, etc.)
- Update router to use Shop.Page with live_action for all shop routes
- Change navigate= to patch= in shop component links
- Add maybe_sync_editing_blocks to reload editor state when page changes
- Track editor_page_slug to detect cross-page navigation while editing
- Fix picture element height when hover image disabled
- Extract ThemeEditor components for shared use
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add theme editing to the existing PageEditorHook, enabling on-site
theme customisation alongside page editing. The editor panel now has
three tabs (Page, Theme, Settings) and can be collapsed while
keeping editing state intact.
- Add theme editing state and event handlers to PageEditorHook
- Add 3-tab UI with tab switching logic
- Add transparent overlay for click-outside dismiss
- Add mobile drag-to-resize with height persistence
- Fix animation replay on drag release (has-dragged class)
- Preserve panel height across LiveView re-renders
- Default to Page tab on editable pages, Theme otherwise
- Show unsaved changes indicator on FAB when panel collapsed
- Fix handle_event grouping warning in admin theme
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create dedicated /admin/account page for user account management
- Move email, password, and 2FA settings from /admin/settings
- Add Account link to top of admin sidebar navigation
- Add TOTP-based two-factor authentication with NimbleTOTP
- Add TOTP verification LiveView for login flow
- Add AccountController for TOTP session management
- Remove Advanced section from settings (duplicated in dev tools)
- Remove user email from sidebar footer (replaced by Account link)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace put_flash calls with inline feedback for form saves:
- Email settings: "Now send a test email" after saving
- Settings: from address and signing secret saves
- Page editor: save button shows "Saved" checkmark
Inline feedback appears next to save buttons and auto-clears after
3 seconds. Banners (put_flash) remain for page-level outcomes like
deletions, state changes, and async operations.
Task 3 of notification overhaul. Theme editor skipped as it auto-saves.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Split the editor sheet into two distinct elements:
- .editor-fab: floating action button, always a pill in the corner
- .editor-panel: sliding panel that animates in/out independently
This enables proper CSS keyframe animations (slide-up/down on mobile,
slide-in/out on desktop) with a closing class for exit transitions.
Simplified the JS hook to only handle close behaviour.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- semantic HTML: step numbers inside h2, strong provider names, details
for adapter configs, strong error messages, fieldset drawer toggle hidden
- inline field errors via flash for no-JS controller fallback
- single form POST button for test email (works with and without JS)
- admin sidebar: remove brand/view-shop, move user email to footer nav
- replace inline style with .admin-setup-step-spaced class
- clean up unused CSS (.admin-brand, .admin-sidebar-header, etc.)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Non-selected adapter configs now get HTML hidden attribute so only the
active config shows without CSS. Provider card labels use div instead
of span for natural block stacking in text-only rendering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Render all adapter field sections in the form with CSS :has(:checked)
controlling visibility. Selecting a provider instantly shows its config
fields — no JS, no page reload, no server round-trip needed.
- Render all 6 adapter configs with data-adapter attribute
- CSS :has(:checked) show/hide rules per adapter in admin stylesheet
- Namespace field names per adapter (email[brevo][api_key] etc)
- Drop 4 transactional-only providers (Resend, Postmark, Mailgun, MailPace)
- Remove noscript "Switch provider" button and controller redirect workaround
- Remove configured_adapter hidden input tracking
- Hide JS-only test email button for no-JS users via noscript style
- LiveView progressively enhances with async save and test email
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
show all 10 providers in three groups (popular, transactional,
advanced) with category headings. fix phx-change clobbering text
fields, async test email sending state, integer parse crash on
bad port. add keyboard focus on card radios, fieldset legend,
WCAG-compliant badge contrast, responsive grid. extract shared
save_config into Mailer, add no-JS controller fallback with
configured_adapter hidden field for adapter change detection.
remove CardRadioScroll JS hook (no longer needed).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
grouped providers by category, added per-provider key validation
with cross-provider detection, friendly delivery error messages,
retryable vs config error distinction, from-address in general
settings, and "Save settings" button to match admin conventions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add KeyValidation module for format-checking API keys before
attempting connections. Auto-strips whitespace, detects common
mistakes (e.g. pasting a Stripe publishable key), and returns
helpful error messages.
Inline field errors across all three entry points:
- Setup wizard: provider + Stripe keys
- Admin provider form: simplified to single Connect button
- Email settings: per-field errors instead of flash toasts
Also: plain text inputs for all API keys (not password fields),
accessible error states (aria-invalid, role=alert, thick border,
bold text), inner_block slot declaration on error component.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add password field and required shop name to setup wizard
- extract SetupHook for DRY redirect to /setup when no admin exists
- add ?from=checklist param to checklist hrefs with contextual banner on
email settings and theme pages for easy return to dashboard
- remove email warning banner from admin layout (checklist covers it)
- make email a required checklist item (no longer optional)
- add DevReset module for wiping dev data without restart
- rename "Theme Studio" to "Theme", drop subtitle
- lower theme editor side-by-side breakpoint from 64em to 48em
- clean up login/registration pages (remove dead registration_open code)
- fix settings.put_secret to invalidate cache after write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract Products.connect_provider/2 that tests the connection, fetches
shop_id, creates the record, and enqueues sync. Both the setup wizard
and the providers form now use this shared function instead of
duplicating the flow. Also makes the products empty state context-aware
(distinguishes "no provider" from "provider connected but no products").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
site_name and site_description are shop identity, not theme concerns.
They now live in the Settings table as first-class settings with their
own assigns (@site_name, @site_description) piped through hooks and
plugs. The setup wizard writes site_name on account creation, and the
theme editor reads/writes via Settings.put_setting. Removed the
"configure your shop" checklist item since currency/country aren't
built yet. Also adds shop name field to setup wizard step 1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix resolve_content_image returning base path (not full URL) so
responsive_image doesn't double-append width/extension
- Remove legacy image fields (image_src, image_alt, image_url) from
block settings schemas
- Remove demo/mockup fallbacks from renderer and defaults — blank
fields stay blank instead of showing preview content
- Replace demo text in defaults with instructional placeholders that
guide new shop owners
- Remove redundant X button from editor sidebar, add unsaved-changes
confirmation to Done button
- Fix block card name overflow on mobile (display: block, flex-wrap)
- Add onboarding UX improvement plan (10 tasks)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every shop page load was triggering ~18 DB queries for data that rarely
changes (theme settings, nav items, categories, shipping countries, logo,
header image). On a shared-cpu-1x Fly machine with SQLite this was the
primary performance bottleneck.
- Add SettingsCache GenServer+ETS for all non-encrypted settings
- Cache list_categories() with single-query N+1 fix (correlated subquery)
- Cache list_available_countries_with_names() in shipping
- Cache Media.get_logo() and Media.get_header()
- Remove duplicate LoadTheme plug from :shop and :admin pipelines
- Invalidate caches on writes (put_setting, product sync, media upload)
- Clear caches between tests via DataCase/ConnCase setup
Per-page queries reduced from ~18 to ~2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a toggle to show/hide internal system events (syncs, job failures,
abandoned cart creation) on the activity feed. Hidden by default so shop
owners only see actionable items. Toggle is ephemeral — resets on page load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace BEM-style &--modifier nesting (unsupported in native CSS) with
&.class-modifier in shop/components.css and page_renderer.ex. Fix
Elixir 1.19 type warnings comparing lists with != [].
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the 3 theme layer imports (primitives, layer2-attributes, semantic)
out of admin.css and shop.css into a new theme.css bundle loaded by all
root layouts. Eliminates 28 KB of duplication on admin pages where both
admin.css and shop.css were each embedding the same theme CSS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single activity_log table powering two views: chronological timeline
on each order detail page (replacing the old fulfilment card) and a
global feed at /admin/activity with tabs, category filters, search,
and pagination. Real-time via PubSub — new entries appear instantly,
nav badge updates across all admin pages.
Instrumented across all event points: Stripe webhooks, order notifier,
submission worker, fulfilment status worker, product sync worker, and
Oban exhausted-job telemetry. Contextual action buttons (retry
submission, retry sync, dismiss) with Oban unique constraints to
prevent double-enqueue. 90-day pruning via cron.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scans page blocks and nav items for broken URLs (internal via DB
lookup, external via HTTP HEAD). Daily Oban cron at 03:30, plus
on-demand checks when pages are saved. Admin UI tab on redirects
page with re-check, ignore, and clickable source links.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
URL-based offset pagination with ?page=N for bookmarkable pages.
Admin views use push_patch, shop collection uses navigate links.
Responsive on mobile with horizontal-scroll tables and stacking
pagination controls. Includes dev seed script for testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sends the campaign to the admin's own email address as a preview,
with [Test] prefix in subject line. Uses the same HTML template
and formatting as real sends. Does not affect campaign status or
sent counts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multipart emails (HTML + plain text fallback) with a branded wrapper:
shop name header, content area with auto-linked URLs and paragraph
formatting, and unsubscribe footer. Applied to both confirmation and
campaign emails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Subscribers with double opt-in confirmation, campaign composer with
draft/scheduled/sent lifecycle, admin dashboard with overview stats,
CSV export, and shop signup form wired into page builder blocks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sidebar nav grouped under Shop/Content/Settings section headers with
subtle uppercase labels (#105)
- custom page settings now show inline in a collapsible panel within the
editor instead of navigating away to a separate page (#107)
- admin editor preview loads real products and categories from the DB,
falling back to PreviewData only on fresh installs (#108)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up the contact form with action/method/name attrs so it works
without JavaScript. Add ContactNotifier, ContactController, and a
noscript info banner in the shop root layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Legal pages (privacy, delivery, terms) now auto-populate content from
shop settings on mount, show auto-generated vs customised badges, and
have a regenerate button. Theme editor gains alt text fields for logo,
header, and icon images. Image picker in page builder now has an upload
button and alt text warning badges. Clearing unused image references
shows an orphan info flash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New block types: spacer, divider, button/CTA, video embed (YouTube,
Vimeo with privacy-enhanced embeds, fallback for unknown URLs).
Page templates (blank, content, landing) shown when creating custom
pages. Duplicate page action on admin index with slug deduplication.
Fix block picker on shop edit sidebar being cut off on mobile by
accounting for bottom nav and making the grid scrollable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
History stacks (@history/@future) on both admin editor and live sidebar,
capped at 50 entries. All mutations routed through apply_mutation for
consistent history tracking. EditorKeyboard JS hook combines DirtyGuard
with Ctrl+Z/Ctrl+Shift+Z. Settings panel fade-in animation. 10 new tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded header, footer and mobile nav with settings-driven
loops. Nav items stored as JSON via Settings, loaded in ThemeHook with
sensible defaults. New admin navigation editor at /admin/navigation
for add/remove/reorder/save/reset. Mobile bottom nav also driven from
header nav items with icon mapping by slug.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New settings form for creating and editing custom page metadata
(title, slug, meta description, published, nav settings). Pages
index shows custom pages section with draft badges and delete.
Editor shows settings button for custom pages, hides reset to
defaults. 20 new tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shop.CustomPage handles /:slug catch-all for CMS pages. Restructured
router so the catch-all is last — all admin, auth, setup, and SEO
routes defined before the shop scope to prevent interception.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stage 1 of custom CMS pages. Adds type/published/meta/nav fields to
pages schema, splits changeset into system vs custom (with slug format
validation and reserved path exclusion), adds create/update/delete
functions with auto-redirect on slug change, and warms custom pages
in ETS cache. 62 pages tests, 1426 total.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Introduces typed settings schema with SettingsField struct, replaces
the read-only JSON textarea with a full repeater UI for info_card items.
Supports add, remove, reorder and inline editing of repeater items.
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>
Home, Content (about/delivery/privacy/terms), Contact, and ErrorHTML
now render through the generic PageRenderer instead of hardcoded
templates. Block wrapper divs enable CSS grid targeting. Featured
products block supports layout/card_variant/columns settings for
different page contexts. Contact page uses CSS grid on data-block-type
attributes for two-column layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stage 1 of the page builder: Pages schema with 14 valid slugs,
BlockTypes registry (26 block types with settings schemas and data
loaders), Defaults module matching existing templates, ETS-backed
PageCache GenServer, and Pages context (get_page/save_page/reset_page
with cache -> DB -> defaults lookup). 34 tests, zero visual change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BrokenUrlTracker now queries real analytics pageview counts instead of
hardcoding 0, so broken URLs with prior traffic are distinguished from
bot noise. For /products/ 404s with a single FTS5 search match, auto-
creates a redirect and marks the broken URL resolved. 1232 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>