berrypod/docs/plans/setup-and-launch.md
jamey edef628214 tidy docs: condense progress, trim readme, mark plan statuses
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:18 +00:00

15 KiB

Unified setup and launch readiness

Status: Complete

Context

The current first-run experience is disjointed: /users/register is a standalone page that closes after one user, the login page still links to it, and /admin/setup is a 3-step wizard hardcoded to Printify that bundles "go live" into the initial setup. Research into Shopify, WooCommerce and Squarespace shows that every successful platform separates initial setup (get the plumbing working) from launch readiness (guide the owner to a shop worth opening).

Two-phase design

Phase A: Initial setup (/setup)

Quick, focused, one-time. Gets the system functional. 5-10 minutes.

  • Create admin account (email + magic link)
  • Connect a print provider (provider-agnostic: Printify, Printful, etc.)
  • Connect payments (Stripe)

No theme customisation, no "go live", no product sync waiting. Once all three connections are made, redirect to /admin. The /setup page is never shown again.

Phase B: Launch checklist (persistent on /admin dashboard)

Ongoing guidance that lives on the admin dashboard until all items are complete or dismissed. Inspired by Shopify's setup guide and WooCommerce's task list. Items auto-complete based on real state. Uses the Zeigarnik effect — seeing a partially-complete checklist motivates finishing it.

Checklist items:

# Item Auto-completes when Links to
1 Sync your products product_count > 0 /admin/providers
2 Customise your theme Theme preset changed from default /admin/theme
3 Review your pages At least one content page edited (future: page editor) /admin/settings
4 Place a test order At least one order exists / (the shop)
5 Go live site_live? == true Button right on the checklist

"Go live" is the final step, only enabled when at least items 1 (products synced) and the provider + Stripe connections from setup are still valid. Items 2-4 are recommended but not blocking — the admin can dismiss them or skip straight to go live.

When all items are complete (or dismissed), the checklist is replaced by the normal dashboard stats (orders, revenue, products) — same as what WooCommerce does.

Coming-soon page (branded holding page)

Until "go live" is flipped, unauthenticated visitors see a themed page using the current theme settings (logo, colours, fonts, site name). Not a sad blank wall — a preview of the brand.

Content:

  • Site name / logo
  • "We're getting things ready. Check back soon."
  • Optional: email signup for launch notification (future enhancement, not MVP)

The admin bypasses this and sees the full shop (existing behaviour via ThemeHook).

Architecture

/setup page

One LiveView, one page, one URL: /setup.

Not a stepper wizard — a single scrollable page with independent sections as cards. Each section can be completed in any order. Completed sections collapse to show a checkmark and summary.

Access rules:

  • No admin exists → page is public, shows all sections including account creation
  • Admin exists + logged in + setup incomplete → page is accessible, account section hidden
  • Admin exists + not logged in → redirect to /users/log-in
  • Setup complete (all three connections made) → redirect to /admin
  • Site is live → redirect to /

"Setup complete" means: admin exists + provider connected + Stripe connected. Products don't need to be synced yet — that's a launch checklist item, not a setup gate.

Why no auth gate for provider/payment setup: Single-tenant app. During initial setup there's no data to protect. The page is only reachable before setup is complete.

Sections:

Section Shown when Content
Create admin account No admin exists Email field, magic link flow
Connect a print provider No provider connected Provider cards (Printify, Printful, coming soon), API key form
Connect payments No payment provider Stripe API key form

Each completed section collapses to a summary line with a checkmark (e.g. "Connected to Printify").

No theme/style section here. Theme customisation is a launch readiness concern, not a setup concern. Keep setup lean.

Dashboard launch checklist

Lives on the existing /admin dashboard page (lib/berrypod_web/live/admin/dashboard.ex). Shown as a card above the stats when site_live? is false or when checklist items remain incomplete.

Component: A function component <.launch_checklist> that takes @setup assigns and renders the checklist card. Each item is a row with: checkbox/checkmark, label, description, link/button.

Progress indicator: "3 of 5 complete" with a simple progress bar. Shopify research shows progress bars motivate completion.

Dismissal: The checklist can be dismissed permanently via a "Dismiss" link. Stores checklist_dismissed in Settings. Once dismissed or all items complete, shows the normal stats-only dashboard.

"Go live" button: Inline in the checklist as the final item. Enabled when products are synced + connections valid. Clicking it sets site_live and shows a brief celebration state before transitioning to the normal dashboard.

Setup status (Setup.setup_status/0)

Make provider-agnostic and add launch checklist fields:

%{
  # Setup phase (connections)
  admin_created: boolean,
  provider_connected: boolean,
  provider_type: string | nil,
  stripe_connected: boolean,
  setup_complete: boolean,  # admin + provider + stripe all connected

  # Launch checklist phase
  products_synced: boolean,
  product_count: integer,
  theme_customised: boolean,
  has_orders: boolean,
  site_live: boolean,

  # Derived
  can_go_live: boolean,  # provider_connected and products_synced and stripe_connected
  checklist_dismissed: boolean
}

Files to create

lib/berrypod/providers/registry.ex

Provider metadata. Pure data, no DB.

@providers [
  %{
    type: "printify",
    name: "Printify",
    tagline: "Largest catalog — 800+ products from 90+ print providers",
    features: ["Biggest product selection", "Multi-provider routing", "Competitive pricing"],
    setup_hint: "Get your API token from Printify → Account → Connections",
    setup_url: "https://printify.com/app/account/connections",
    status: :available
  },
  %{
    type: "printful",
    name: "Printful",
    tagline: "Premium quality with in-house fulfilment",
    features: ["In-house production", "Warehousing & branding", "Mockup generator"],
    setup_hint: "Get your API token from Printful → Settings → API",
    setup_url: "https://www.printful.com/dashboard/developer/api",
    status: :available
  },
  %{type: "gelato", name: "Gelato", tagline: "Local production in 30+ countries", status: :coming_soon},
  %{type: "prodigi", name: "Prodigi", tagline: "Fine art and photo products", status: :coming_soon}
]

Functions: all/0, available/0, get/1

lib/berrypod/payments/registry.ex

Same pattern for payment providers (just Stripe for now).

lib/berrypod_web/live/setup/onboarding.ex

Single LiveView at /setup. Three sections: account, provider, payments.

Mount:

def mount(_params, _session, socket) do
  setup = Setup.setup_status()

  cond do
    setup.site_live ->
      {:ok, push_navigate(socket, to: ~p"/")}

    setup.setup_complete ->
      {:ok, push_navigate(socket, to: ~p"/admin")}

    setup.admin_created and is_nil(get_user(socket)) ->
      {:ok, push_navigate(socket, to: ~p"/users/log-in")}

    true ->
      {:ok, mount_setup(socket, setup)}
  end
end

Account section events:

  • handle_event("register", %{"email" => email}, socket) — calls Accounts.register_user/1 + deliver_login_instructions/2. Shows "Check your email" note.

Provider section events:

  • handle_event("select_provider", %{"type" => type}, socket) — expands that provider's API key form.
  • handle_event("test_connection", %{"provider" => params}, socket) — tests API key validity.
  • handle_event("connect_provider", %{"provider" => params}, socket) — creates connection, enqueues sync in background. Does NOT wait for sync to finish — moves on.

Payments section events:

  • handle_event("connect_stripe", %{"stripe" => params}, socket) — reuses existing StripeSetup.connect/1.

Completion: When all three sections are done (setup_complete), show a brief "You're all set" message with a button to go to the dashboard. Or auto-redirect after a short delay.

Files to modify

lib/berrypod/setup.ex

  • Replace Products.get_provider_connection_by_type("printify") with Products.get_first_provider_connection/0
  • Rename printify_connectedprovider_connected
  • Add provider_type field (e.g. "printify", "printful")
  • Add setup_complete field: admin_created and provider_connected and stripe_connected
  • Add theme_customised field: true if theme preset != default
  • Add has_orders field: Orders.count_paid_orders() > 0
  • Add checklist_dismissed field from Settings
  • Update can_go_live: provider_connected and products_synced and stripe_connected

lib/berrypod/products.ex

Add get_first_provider_connection/0 — first connection with non-nil api_key_encrypted, ordered by inserted_at.

lib/berrypod_web/live/admin/dashboard.ex

  • Load Setup.setup_status() on mount
  • When !setup.site_live and !setup.checklist_dismissed, render <.launch_checklist> above the stats
  • When setup.site_live or setup.checklist_dismissed, render the normal stats dashboard
  • Handle "go_live" event: Settings.set_site_live(true), show celebration, then reload as normal dashboard
  • Handle "dismiss_checklist" event: Settings.put_setting("shop", "checklist_dismissed", "true")

lib/berrypod_web/router.ex

Add /setup to a minimal live_session (no ThemeHook, no CartHook):

live "/setup", Setup.Onboarding, :index

Remove /admin/setup from admin live_session (or redirect to /setup if not yet complete, /admin if complete).

lib/berrypod_web/user_auth.ex

Change signed_in_path/1:

  • If setup not complete → ~p"/setup"
  • If setup complete but not live → ~p"/admin"
  • If live → ~p"/admin"

lib/berrypod_web/live/auth/registration.ex

Redirect to /setup (no admin) or /users/log-in (admin exists).

lib/berrypod_web/live/auth/login.ex

  • Add @registration_open assign (!Accounts.has_admin?())
  • Hide "Sign up" link when admin exists

lib/berrypod_web/theme_hook.ex

Change fresh-install redirect from /users/register to /setup.

lib/berrypod_web/components/layouts/admin.html.heex

  • Remove "Setup" sidebar link (setup is now /setup, not an admin page)
  • The dashboard handles launch readiness now

lib/berrypod_web/live/shop/coming_soon.ex

Keep as-is but potentially enhance later with email signup. Current implementation already uses theme settings for site name.

assets/css/admin/components.css

Add under /* ── Setup ── */:

/* /setup page */
.admin-setup-done {
  /* collapsed summary: checkmark + text */
}

/* Dashboard launch checklist */
.admin-checklist { }
.admin-checklist-item { }
.admin-checklist-item[data-complete] { }
.admin-checklist-progress { }

~30-40 lines total. Everything else uses existing admin component classes.

Auth flow

Fresh install (no admin):
  Visit anything → ThemeHook → redirect /setup
  /setup shows ALL sections (account, provider, payments)
  User creates account via magic link
  User clicks magic link → confirmed → signed_in_path = /setup
  /setup: account section done, provider + payments still needed
  Complete connections → redirect /admin
  Dashboard shows launch checklist

Returning admin (setup complete, not yet live):
  Visit /admin → dashboard with launch checklist
  Checklist: sync products, customise theme, test order, go live
  Items auto-complete as admin works through them
  Admin clicks "Go live" → site opens to public

Visitor (not yet live):
  Visit anything → ThemeHook → redirect /coming-soon
  Sees branded holding page with site name

Already live:
  /setup → redirect /admin
  Dashboard shows stats (no checklist)
  Visitors see the real shop

CSS approach

Reuse existing admin components. Small additions needed:

Need Reuse New?
Section cards .admin-card + .admin-card-body + .admin-card-title No
Badges .admin-badge No
Buttons .admin-btn-* No
Forms .admin-input, .admin-label No
Alerts/notes .admin-alert-info No
Spinner .admin-spinner No
Section complete state Yes — collapsed state with checkmark
Checklist items Yes — row with checkbox, label, link
Progress bar Yes — simple bar for "3 of 5 complete"

All new CSS in assets/css/admin/components.css, using .admin-setup-* and .admin-checklist-* prefixes.

Task breakdown

# Task Files Est
1 Provider + payment registries providers/registry.ex, payments/registry.ex 30m
2 Make Setup provider-agnostic + add checklist fields setup.ex, products.ex 45m
3 Setup LiveView (/setup) — account, provider, payments setup/onboarding.ex 2.5h
4 Dashboard launch checklist component + go-live dashboard.ex 2h
5 Router, auth flow, redirects router.ex, user_auth.ex, registration.ex, login.ex, theme_hook.ex, admin.html.heex 30m
6 CSS additions (~40 lines) admin/components.css 20m
7 Tests setup, dashboard checklist, auth flow 2h
8 Remove old /admin/setup delete or redirect 15m

Total: ~9 hours across 4-5 sessions

Verification

  1. Fresh install: Delete local DB, run migrations, visit localhost:4000 → redirects to /setup. Complete account + provider + payments → lands on /admin with launch checklist.
  2. Launch checklist: Sync products → item auto-completes. Change theme → item auto-completes. Progress bar updates.
  3. Go live: Click "Go live" on checklist → celebration → visitors see real shop.
  4. Coming soon: Before go-live, unauthenticated visitors see branded holding page.
  5. Dismiss: Admin can dismiss the checklist and use the normal dashboard without going live.
  6. Provider agnostic: Connect Printful instead of Printify — works the same.
  7. Dead ends: /users/register redirects to /setup. Login page hides "Sign up" when admin exists.
  8. mix precommit passes.

Future enhancements (not in scope)

  • Email signup on coming-soon page — collect emails for launch notification
  • Test order detection — auto-complete "Place a test order" when an order with a test Stripe key exists
  • Content page editing — "Review your pages" becomes more meaningful with a page editor (Tier 4)
  • Preset picker on setup page — considered and cut. Theme customisation belongs in the launch checklist phase, not initial setup. The theme editor is the right tool for this.
  • Onboarding tooltips — contextual hints on first visit to admin pages (e.g. "This is where you manage your products")