Commit Graph

28 Commits

Author SHA1 Message Date
jamey
dd20ea824f fix email settings: missing providers, a11y, no-JS support
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>
2026-03-04 21:26:59 +00:00
jamey
dd659e4c61 replace orphaned tailwind classes with project CSS
All checks were successful
deploy / deploy (push) Successful in 1m27s
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>
2026-03-04 17:12:21 +00:00
jamey
67a26eb6b4 add contextual prompts for skipped setup steps
All checks were successful
deploy / deploy (push) Successful in 1m26s
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>
2026-03-04 14:02:49 +00:00
jamey
5b41f3fedf extract site_name and site_description from theme settings into standalone settings
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>
2026-03-03 14:52:31 +00:00
jamey
580a7203c9 add activity log with order timeline and global feed
All checks were successful
deploy / deploy (push) Successful in 4m22s
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>
2026-03-01 15:09:08 +00:00
jamey
ad2e6d1e6d add newsletter and email campaigns
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>
2026-02-28 23:25:28 +00:00
jamey
ca01f43d70 add no-JS contact form and noscript banner
All checks were successful
deploy / deploy (push) Successful in 1m21s
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>
2026-02-28 18:57:51 +00:00
jamey
22d7b0e92b add published custom pages to sitemap
All checks were successful
deploy / deploy (push) Successful in 1m20s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 11:37:16 +00:00
jamey
3a243151af add data-driven navigation with admin editor
All checks were successful
deploy / deploy (push) Successful in 1m34s
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>
2026-02-28 11:18:37 +00:00
jamey
c69e51051f wire simple pages to PageRenderer (stage 3)
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>
2026-02-26 18:29:20 +00:00
jamey
d0ea9d59f5 add no-JS fallback for cart country selector
All checks were successful
deploy / deploy (push) Successful in 59s
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>
2026-02-24 23:14:48 +00:00
jamey
6289c678f7 add no-JS fallback for contact page order tracking form
Some checks failed
deploy / deploy (push) Has been cancelled
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>
2026-02-24 23:10:15 +00:00
jamey
0b0adba0fe add no-JS progressive enhancement for all shop flows
All checks were successful
deploy / deploy (push) Successful in 1m23s
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>
2026-02-24 22:56:19 +00:00
jamey
f788108665 add favicon and site icon generation from uploaded images
All checks were successful
deploy / deploy (push) Successful in 1m26s
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>
2026-02-24 17:22:15 +00:00
jamey
2f4cd81f98 add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).

Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
  signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML

Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design

Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
  has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
jamey
758e66db5c add analytics CSV export
All checks were successful
deploy / deploy (push) Successful in 1m25s
Downloads a ZIP with one CSV per report (overview, trend, pages, entry/exit
pages, sources, referrers, countries, devices, funnel). Export button lives
next to the period selector and picks up the current period and any active
filters using a JS hook + JS.set_attribute, so the downloaded data always
matches what's on screen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 09:37:45 +00:00
jamey
01ff8decd5 add order status lookup for customers
All checks were successful
deploy / deploy (push) Successful in 1m17s
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>
2026-02-24 08:40:08 +00:00
jamey
0f1135256d add canonical URLs, robots.txt, and sitemap.xml
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>
2026-02-23 21:47:35 +00:00
jamey
f91b47f0c3 include browser/os/screen_size in e-commerce analytics events
All checks were successful
deploy / deploy (push) Successful in 1m37s
Event call sites (product_view, add_to_cart, checkout_start, purchase)
were only passing visitor_hash and pathname, leaving browser, OS, screen
size and country nil. Add AnalyticsHook.attrs/1 helper to extract common
analytics fields from socket assigns, and use it in all LiveView event
call sites. Checkout controller reads the same fields from the session.

Also fix plug analytics test to clear stale events before assertions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:13:47 +00:00
jamey
2bd2e613c7 add privacy-first analytics with progressive event collection
All checks were successful
deploy / deploy (push) Successful in 3m20s
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>
2026-02-22 12:50:55 +00:00
jamey
a0985bd07e fix Stripe webhook crash on struct access
All checks were successful
deploy / deploy (push) Successful in 1m0s
Stripe.Checkout.Session is a struct that doesn't implement Access,
so get_in with atom keys fails. Use direct struct field access instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:36:21 +00:00
jamey
0ddafbd84f fix recovery login crash for users with password set
All checks were successful
deploy / deploy (push) Successful in 1m8s
login_user_by_magic_link raises for unconfirmed users with a password,
which is exactly what recovery creates. Use get_user_by_magic_link_token
directly and log in without the magic link guard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:52:46 +00:00
jamey
b0607621f3 add admin account recovery via setup secret
All checks were successful
deploy / deploy (push) Successful in 1m33s
When email isn't configured, the login page now hides the magic link
form and shows a recovery link. The /recover page logs the setup secret
to server logs and lets the admin reset their password with it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 21:40:53 +00:00
jamey
9d9bd09059 auto-confirm admin during setup, skip email verification
Some checks failed
deploy / deploy (push) Has been cancelled
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>
2026-02-21 10:24:26 +00:00
jamey
b06029079d improve error pages: minimal version when site not live
All checks were successful
deploy / deploy (push) Successful in 1m19s
Show a lightweight error page using admin.css when the shop isn't
live yet, avoiding broken theme dependencies. Also tidied up copy
to sentence case and shorter descriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:19:21 +00:00
jamey
9efc0644ba fix 404/500 error pages loading wrong stylesheet
All checks were successful
deploy / deploy (push) Successful in 1m10s
error pages render shop-themed layout but were loading admin.css,
which has no shop component styles. switch to shop.css.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:02:07 +00:00
jamey
a78a1db847 fix admin font loading and theme CSS cache miss bug
add system font stack to admin reset CSS so setup/admin pages render
sans-serif on all devices instead of falling through to browser default.
pass path_resolver to CSSGenerator.generate on cache miss paths so
font URLs resolve to digested paths in production.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:39:41 +00:00
jamey
9528700862 rename project from SimpleshopTheme to Berrypod
All modules, configs, paths, and references updated.
836 tests pass, zero warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:23:15 +00:00