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>
This commit is contained in:
jamey
2026-02-28 23:25:28 +00:00
parent 8f989d892d
commit ad2e6d1e6d
32 changed files with 2497 additions and 32 deletions

View File

@@ -136,6 +136,7 @@ defmodule BerrypodWeb.Router do
pipe_through [:browser, :require_authenticated_user, :admin]
get "/analytics/export", AnalyticsExportController, :export
get "/newsletter/export", NewsletterExportController, :export
live_session :admin,
layout: {BerrypodWeb.Layouts, :admin},
@@ -160,6 +161,9 @@ defmodule BerrypodWeb.Router do
live "/pages/:slug", Admin.Pages.Editor, :edit
live "/navigation", Admin.Navigation, :index
live "/media", Admin.Media, :index
live "/newsletter", Admin.Newsletter, :index
live "/newsletter/campaigns/new", Admin.Newsletter.CampaignForm, :new
live "/newsletter/campaigns/:id", Admin.Newsletter.CampaignForm, :edit
live "/redirects", Admin.Redirects, :index
end
@@ -203,6 +207,7 @@ defmodule BerrypodWeb.Router do
get "/orders/verify/:token", OrderLookupController, :verify
get "/unsubscribe/:token", UnsubscribeController, :unsubscribe
get "/newsletter/confirm/:token", NewsletterController, :confirm
end
# Dev-only routes (mailbox preview, error previews)
@@ -247,7 +252,8 @@ defmodule BerrypodWeb.Router do
{BerrypodWeb.CartHook, :mount_cart},
{BerrypodWeb.SearchHook, :mount_search},
{BerrypodWeb.AnalyticsHook, :track},
{BerrypodWeb.PageEditorHook, :mount_page_editor}
{BerrypodWeb.PageEditorHook, :mount_page_editor},
{BerrypodWeb.NewsletterHook, :mount_newsletter}
] do
live "/", Shop.Home, :index
live "/about", Shop.Content, :about
@@ -274,6 +280,9 @@ defmodule BerrypodWeb.Router do
post "/contact/send", ContactController, :create
post "/contact/lookup", OrderLookupController, :lookup
# Newsletter signup (no-JS fallback)
post "/newsletter/subscribe", NewsletterController, :subscribe
# Cart form actions (no-JS fallbacks for LiveView cart events)
post "/cart/add", CartController, :add
post "/cart/remove", CartController, :remove