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>
auth pages (login, registration, confirmation, recover) now use
setup-page/setup-header/admin-btn-block. theme toggle indicator
gets proper CSS. cleaned up dead h-full, size-3.5, ml-2 classes.
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>
Disable checkout when Stripe isn't connected (cart drawer, cart page,
and early guard in checkout controller to prevent orphaned orders).
Show amber warning on order detail when email isn't configured.
Fix pre-existing missing vertical spacing between page blocks.
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 icon={false} option to external_link for links with their own
visual indicator. Migrate remaining manual target="_blank" links:
email settings adapter links, product show provider edit, card radio
links, social link cards/icons, page renderer tracking and video
fallback. Every external link in the codebase now goes through the
single component — one place to change rel, target, or sr-only text.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New external_link component in core_components handles target="_blank",
rel="noopener noreferrer", external-link icon, and sr-only "(opens in
new tab)" text. Migrated admin providers form, settings (Stripe),
order tracking, onboarding setup links to use it. Fixed rel="noopener"
to "noopener noreferrer" on remaining links (email settings, product
show, core_components card radio). Added sr-only text to shop social
link cards and aria-label to page renderer tracking link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add AnalyticsHook to the coming_soon live session — the shop layout
fires an analytics:screen JS event but the session had no handler,
crashing the page on connect.
Centre the logo image (display:block from CSS reset needs
margin-inline:auto). Add a subtle "Admin" link at the bottom using
flex flow rather than fixed/absolute positioning so it works in
iframes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap logo mode radios in fieldset/legend for screen reader grouping.
Hide native radio input properly (was using nonexistent .hidden class),
add aria-hidden on decorative dot spans, focus-visible ring on cards,
and IDs on each input.
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>
The listing preload (images: :image) was loading the full images table row
including the data BLOB column (~3MB per page). Now only loads :id and
:source_width. Listing preloads also limited to first 2 images (primary +
hover) since product cards don't use the rest. Added composite indexes on
(visible, status, inserted_at) and (visible, status, category) to eliminate
the TEMP B-TREE sort SQLite was doing.
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>
Source mockup WebPs are copied from the release to /data/mockups/
on startup, and variants are generated there. This eliminates the
182-job storm on every deploy that was saturating the CPU and
causing SQLite locking. After the first successful run, subsequent
deploys find all variants intact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Image variants were written to the ephemeral release directory
and wiped on every deploy, causing 500 errors with 50s timeouts
as browsers waited for images that could never be served.
- Point image_cache_dir at /data/image_cache in prod
- Add Plug.Static to serve from the persistent volume
- Exclude /image_cache/ from broken URL tracking
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>
More prominent placement just below the header, removed from footer.
Footer now only has dev tools disclosure and log out.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LiveDashboard, Errors, and Jobs are now behind a "Developer tools"
toggle, keeping View shop and Log out as the prominent footer links.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump oban to 2.19+, add oban_web for real-time job monitoring.
Mounted behind admin auth with sidebar link under "Jobs".
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>
- Delete utilities.css (701 lines / 24 KB of Tailwind utility clones)
- Add layout.css with admin-stack, admin-row, admin-cluster, admin-grid
primitives and gap variants (sm, md, lg, xl)
- Add transitions.css import and layout.css import to admin.css entry point
- Replace all Tailwind utility classes across 26 admin templates with
semantic admin-*/theme-*/page-specific CSS classes
- Replace all non-dynamic inline styles with semantic classes
- Add ~100 new semantic classes to components.css (analytics, dashboard,
order detail, settings, theme editor, generic utilities)
- Fix stray text-error → admin-text-error in media.ex
- Add missing .truncate definition to admin CSS
- Only remaining inline styles are dynamic data values (progress bars,
chart dimensions) and one JS.toggle target
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
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>
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>
Block cards now show a one-line content summary below the name.
Block picker items include SVG wireframe thumbnails. Newsletter
block marked as decorative with configurable title/description
and form submission prevented on the shop side.
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>
- rename "Providers" to "Print providers" in sidebar (#110)
- add LiveView navigation guard to EditorKeyboard hook — intercepts
link clicks in capture phase when editor has unsaved changes (#103)
- add description field to all 26 block types, shown as subtitle in
block picker; filter searches descriptions too (#104)
- add visible column headers (Label / Path) and proper sr-only labels
with for attributes on nav editor inputs (#106)
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>