From 73e749c573cb925ea21d9d92cad5e98c98b53677 Mon Sep 17 00:00:00 2001 From: Jamey Greenwood Date: Mon, 19 Jan 2026 21:06:31 +0000 Subject: [PATCH] docs: replace ROADMAP with detailed MVP implementation plan New "Core MVP" section with 5 phases: - Phase A: Products Context + Printify Sync - Phase B: Session-Based Cart - Phase C: Stripe Checkout integration - Phase D: Orders + Printify Fulfillment - Phase E: Cost Verification at Checkout Includes concrete schemas, code examples, and file lists. Adds Multi-Provider Support to future features. Co-Authored-By: Claude Opus 4.5 --- ROADMAP.md | 356 ++++++++++++++++++++++++++++++-- ROADMAP_UPDATED.md | 503 --------------------------------------------- 2 files changed, 342 insertions(+), 517 deletions(-) delete mode 100644 ROADMAP_UPDATED.md diff --git a/ROADMAP.md b/ROADMAP.md index bb1899c..1b0ef6b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,6 +2,341 @@ This document tracks future improvements, features, and known gaps. +--- + +## Core MVP: Real Products & Checkout (Priority) + +This section covers the work needed to turn SimpleShop from a theme demo into a working e-commerce storefront. Estimated total effort: **3-4 days** (leveraging existing Printify demo code). + +### Phase A: Products Context + Printify Sync +**Status:** Not implemented +**Effort:** 1-1.5 days +**Dependencies:** None + +Replace `PreviewData` with real products synced from Printify. + +**Schemas:** +```elixir +# products table +field :printify_id, :string +field :title, :string +field :description, :text +field :images, {:array, :map} # [{src, position}] +field :print_provider_id, :integer +field :blueprint_id, :integer +field :synced_at, :utc_datetime +field :published, :boolean, default: true +timestamps() + +# product_variants table +field :printify_variant_id, :integer +field :title, :string # e.g. "Black / M" +field :sku, :string +field :price_cents, :integer # selling price +field :cost_cents, :integer # Printify cost (for profit calc) +field :options, :map # %{"Color" => "Black", "Size" => "M"} +field :is_available, :boolean +belongs_to :product + +# product_cost_history table (append-only for future analytics) +field :cost_cents, :integer +field :recorded_at, :utc_datetime +belongs_to :product_variant +``` + +**Implementation:** +1. Migrate OAuth + Client modules from `simpleshop_printify` demo + - Adapt `Simpleshop.Printify.OAuth` → `SimpleshopTheme.Printify.OAuth` + - Adapt `Simpleshop.Printify.Client` → `SimpleshopTheme.Printify.Client` + - Adapt `Simpleshop.Printify.TokenStore` → `SimpleshopTheme.Printify.TokenStore` +2. Create `SimpleshopTheme.Products` context with schemas +3. Add `mix sync_products` task to pull products from Printify +4. Add webhook endpoint for `product:publish:started` events +5. Replace `PreviewData` calls in LiveViews with `Products` context queries +6. Store cost history on each sync for future profit analytics + +**Webhook flow:** +1. Seller clicks "Publish to SimpleShop" in Printify dashboard +2. Printify fires `product:publish:started` with product data +3. SimpleShop stores product locally in SQLite +4. SimpleShop calls Printify "publish succeeded" endpoint + +**Files to create:** +- `lib/simpleshop_theme/printify/oauth.ex` +- `lib/simpleshop_theme/printify/token_store.ex` +- `lib/simpleshop_theme/products.ex` +- `lib/simpleshop_theme/products/product.ex` +- `lib/simpleshop_theme/products/variant.ex` +- `lib/simpleshop_theme/products/cost_history.ex` +- `lib/simpleshop_theme_web/controllers/printify_webhook_controller.ex` +- `lib/mix/tasks/sync_products.ex` +- `priv/repo/migrations/*_create_products.exs` + +--- + +### Phase B: Session-Based Cart +**Status:** Not implemented +**Effort:** 0.5 days +**Dependencies:** Phase A (Products) + +Real cart functionality with session persistence. + +**Approach:** Store cart in Phoenix session (no separate cart table needed for MVP). Cart is a map of `%{variant_id => quantity}` stored in the session. + +**Implementation:** +```elixir +# lib/simpleshop_theme/cart.ex +defmodule SimpleshopTheme.Cart do + alias SimpleshopTheme.Products + + def get(session), do: Map.get(session, "cart", %{}) + + def add_item(session, variant_id, quantity \\ 1) + def remove_item(session, variant_id) + def update_quantity(session, variant_id, quantity) + def clear(session) + + def to_line_items(cart) do + # Returns list of %{variant: variant, quantity: qty, subtotal: price} + end + + def total(cart) # Returns total in cents + def item_count(cart) # For header badge +end +``` + +**LiveView integration:** +- Add `phx-click="add_to_cart"` to product pages +- Update cart LiveView to use real data +- Add cart count to header (assign in `on_mount`) + +**Files to create/modify:** +- `lib/simpleshop_theme/cart.ex` +- Modify `lib/simpleshop_theme_web/live/shop_live/product_show.ex` +- Modify `lib/simpleshop_theme_web/live/shop_live/cart.ex` +- Modify `lib/simpleshop_theme_web/components/layouts.ex` (cart count) + +--- + +### Phase C: Stripe Checkout +**Status:** Not implemented +**Effort:** 0.5-1 day +**Dependencies:** Phase B (Cart) + +Stripe Checkout (hosted payment page) integration. + +**Dependencies to add:** +```elixir +# mix.exs +{:stripity_stripe, "~> 3.0"} +``` + +**Config:** +```elixir +# config/runtime.exs +config :stripity_stripe, + api_key: System.get_env("STRIPE_SECRET_KEY") + +# Also need STRIPE_WEBHOOK_SECRET for webhook verification +``` + +**Implementation:** +```elixir +# lib/simpleshop_theme/checkout.ex +defmodule SimpleshopTheme.Checkout do + def create_session(cart, success_url, cancel_url) do + line_items = Enum.map(cart, fn {variant_id, qty} -> + variant = Products.get_variant!(variant_id) + %{ + price_data: %{ + currency: "gbp", + unit_amount: variant.price_cents, + product_data: %{ + name: "#{variant.product.title} - #{variant.title}", + images: [hd(variant.product.images)["src"]] + } + }, + quantity: qty + } + end) + + Stripe.Checkout.Session.create(%{ + mode: "payment", + line_items: line_items, + success_url: success_url <> "?session_id={CHECKOUT_SESSION_ID}", + cancel_url: cancel_url, + shipping_address_collection: %{allowed_countries: ["GB"]}, + metadata: %{cart: Jason.encode!(cart)} + }) + end +end +``` + +**Webhook handler:** +```elixir +# lib/simpleshop_theme_web/controllers/stripe_webhook_controller.ex +def handle_event(%Stripe.Event{type: "checkout.session.completed"} = event) do + session = event.data.object + cart = Jason.decode!(session.metadata["cart"]) + shipping = session.shipping_details + + # Create order and push to Printify (Phase D) + Orders.create_from_checkout(session, cart, shipping) +end +``` + +**Files to create:** +- `lib/simpleshop_theme/checkout.ex` +- `lib/simpleshop_theme_web/controllers/stripe_webhook_controller.ex` +- `lib/simpleshop_theme_web/live/shop_live/checkout_success.ex` +- `lib/simpleshop_theme_web/live/shop_live/checkout_cancel.ex` + +**Routes:** +```elixir +post "/webhooks/stripe", StripeWebhookController, :handle +live "/checkout/success", ShopLive.CheckoutSuccess +live "/checkout/cancel", ShopLive.CheckoutCancel +``` + +--- + +### Phase D: Orders + Printify Fulfillment +**Status:** Not implemented +**Effort:** 0.5-1 day +**Dependencies:** Phase C (Stripe Checkout) + +Create orders locally and push to Printify for fulfillment. + +**Schema:** +```elixir +# orders table +field :stripe_session_id, :string +field :stripe_payment_intent_id, :string +field :printify_order_id, :string +field :status, Ecto.Enum, values: [:pending, :paid, :submitted, :in_production, :shipped, :delivered, :cancelled] +field :total_cents, :integer +field :shipping_address, :map +field :customer_email, :string +timestamps() + +# order_items table +field :quantity, :integer +field :unit_price_cents, :integer +field :unit_cost_cents, :integer # For profit tracking +belongs_to :order +belongs_to :product_variant +``` + +**Implementation:** +```elixir +# lib/simpleshop_theme/orders.ex +def create_from_checkout(stripe_session, cart, shipping) do + Repo.transaction(fn -> + # 1. Create local order + order = create_order(stripe_session, shipping) + + # 2. Create order items + create_order_items(order, cart) + + # 3. Push to Printify + case Printify.Client.create_order(order) do + {:ok, printify_response} -> + update_order(order, %{ + printify_order_id: printify_response["id"], + status: :submitted + }) + {:error, reason} -> + # Log error but don't fail - can retry later + Logger.error("Failed to submit to Printify: #{inspect(reason)}") + update_order(order, %{status: :paid}) # Mark as paid, needs manual submission + end + end) +end +``` + +**Printify order creation:** +```elixir +# Add to lib/simpleshop_theme/printify/client.ex +def create_order(order) do + body = %{ + external_id: order.id, + shipping_method: 1, # Standard + address_to: %{ + first_name: order.shipping_address["name"] |> String.split() |> hd(), + last_name: order.shipping_address["name"] |> String.split() |> List.last(), + email: order.customer_email, + address1: order.shipping_address["line1"], + address2: order.shipping_address["line2"], + city: order.shipping_address["city"], + zip: order.shipping_address["postal_code"], + country: order.shipping_address["country"] + }, + line_items: Enum.map(order.items, fn item -> + %{ + product_id: item.variant.product.printify_id, + variant_id: item.variant.printify_variant_id, + quantity: item.quantity + } + end) + } + + post("/shops/#{shop_id()}/orders.json", body) +end +``` + +**Files to create:** +- `lib/simpleshop_theme/orders.ex` +- `lib/simpleshop_theme/orders/order.ex` +- `lib/simpleshop_theme/orders/order_item.ex` +- `priv/repo/migrations/*_create_orders.exs` +- Update `lib/simpleshop_theme/printify/client.ex` with `create_order/1` + +--- + +### Phase E: Cost Verification at Checkout (Safety Net) +**Status:** Not implemented +**Effort:** 0.25 days +**Dependencies:** Phase D (Orders) + +Verify Printify costs haven't changed before completing checkout. + +**Implementation:** +```elixir +# In checkout flow, before creating Stripe session +def verify_costs(cart) do + Enum.reduce_while(cart, :ok, fn {variant_id, _qty}, _acc -> + variant = Products.get_variant!(variant_id) + + case Printify.Client.get_product(variant.product.printify_id) do + {:ok, printify_product} -> + current_cost = find_variant_cost(printify_product, variant.printify_variant_id) + + if current_cost != variant.cost_cents do + # Update local cost + Products.update_variant_cost(variant, current_cost) + + if cost_increase_exceeds_threshold?(variant.cost_cents, current_cost) do + {:halt, {:error, :costs_changed, variant}} + else + {:cont, :ok} + end + else + {:cont, :ok} + end + + {:error, _} -> + # Can't verify - proceed but log warning + Logger.warning("Could not verify costs for variant #{variant_id}") + {:cont, :ok} + end + end) +end +``` + +This ensures sellers never unknowingly sell at a loss due to Printify price changes. + +--- + ## Quick Wins (Low Effort) ### CSS Cache Warming on Startup @@ -79,20 +414,6 @@ Allow shop owners to build custom pages by combining pre-built sections: ## Future Features (Large Scope) -### Products Context -Real product management instead of preview data: -- Product CRUD -- Variant support (size, colour) -- Inventory tracking -- Image galleries - -### Orders & Cart Functionality -Currently cart is display-only with mock data: -- Session-based cart persistence -- Add to cart functionality -- Checkout flow -- Order creation and history - ### Multi-Admin Support Currently single-user authentication: - Multiple admin users @@ -116,6 +437,13 @@ Backup and restore theme settings: - Custom JavaScript snippets - Code-level overrides for developers +### Multi-Provider Support (Future) +Support multiple POD providers beyond Printify: +- Prodigi (better for art prints) +- Gelato (global fulfillment) +- Provider-agnostic product model +- Price comparison across providers + --- ## Technical Debt diff --git a/ROADMAP_UPDATED.md b/ROADMAP_UPDATED.md deleted file mode 100644 index 1b0ef6b..0000000 --- a/ROADMAP_UPDATED.md +++ /dev/null @@ -1,503 +0,0 @@ -# SimpleShop Roadmap - -This document tracks future improvements, features, and known gaps. - ---- - -## Core MVP: Real Products & Checkout (Priority) - -This section covers the work needed to turn SimpleShop from a theme demo into a working e-commerce storefront. Estimated total effort: **3-4 days** (leveraging existing Printify demo code). - -### Phase A: Products Context + Printify Sync -**Status:** Not implemented -**Effort:** 1-1.5 days -**Dependencies:** None - -Replace `PreviewData` with real products synced from Printify. - -**Schemas:** -```elixir -# products table -field :printify_id, :string -field :title, :string -field :description, :text -field :images, {:array, :map} # [{src, position}] -field :print_provider_id, :integer -field :blueprint_id, :integer -field :synced_at, :utc_datetime -field :published, :boolean, default: true -timestamps() - -# product_variants table -field :printify_variant_id, :integer -field :title, :string # e.g. "Black / M" -field :sku, :string -field :price_cents, :integer # selling price -field :cost_cents, :integer # Printify cost (for profit calc) -field :options, :map # %{"Color" => "Black", "Size" => "M"} -field :is_available, :boolean -belongs_to :product - -# product_cost_history table (append-only for future analytics) -field :cost_cents, :integer -field :recorded_at, :utc_datetime -belongs_to :product_variant -``` - -**Implementation:** -1. Migrate OAuth + Client modules from `simpleshop_printify` demo - - Adapt `Simpleshop.Printify.OAuth` → `SimpleshopTheme.Printify.OAuth` - - Adapt `Simpleshop.Printify.Client` → `SimpleshopTheme.Printify.Client` - - Adapt `Simpleshop.Printify.TokenStore` → `SimpleshopTheme.Printify.TokenStore` -2. Create `SimpleshopTheme.Products` context with schemas -3. Add `mix sync_products` task to pull products from Printify -4. Add webhook endpoint for `product:publish:started` events -5. Replace `PreviewData` calls in LiveViews with `Products` context queries -6. Store cost history on each sync for future profit analytics - -**Webhook flow:** -1. Seller clicks "Publish to SimpleShop" in Printify dashboard -2. Printify fires `product:publish:started` with product data -3. SimpleShop stores product locally in SQLite -4. SimpleShop calls Printify "publish succeeded" endpoint - -**Files to create:** -- `lib/simpleshop_theme/printify/oauth.ex` -- `lib/simpleshop_theme/printify/token_store.ex` -- `lib/simpleshop_theme/products.ex` -- `lib/simpleshop_theme/products/product.ex` -- `lib/simpleshop_theme/products/variant.ex` -- `lib/simpleshop_theme/products/cost_history.ex` -- `lib/simpleshop_theme_web/controllers/printify_webhook_controller.ex` -- `lib/mix/tasks/sync_products.ex` -- `priv/repo/migrations/*_create_products.exs` - ---- - -### Phase B: Session-Based Cart -**Status:** Not implemented -**Effort:** 0.5 days -**Dependencies:** Phase A (Products) - -Real cart functionality with session persistence. - -**Approach:** Store cart in Phoenix session (no separate cart table needed for MVP). Cart is a map of `%{variant_id => quantity}` stored in the session. - -**Implementation:** -```elixir -# lib/simpleshop_theme/cart.ex -defmodule SimpleshopTheme.Cart do - alias SimpleshopTheme.Products - - def get(session), do: Map.get(session, "cart", %{}) - - def add_item(session, variant_id, quantity \\ 1) - def remove_item(session, variant_id) - def update_quantity(session, variant_id, quantity) - def clear(session) - - def to_line_items(cart) do - # Returns list of %{variant: variant, quantity: qty, subtotal: price} - end - - def total(cart) # Returns total in cents - def item_count(cart) # For header badge -end -``` - -**LiveView integration:** -- Add `phx-click="add_to_cart"` to product pages -- Update cart LiveView to use real data -- Add cart count to header (assign in `on_mount`) - -**Files to create/modify:** -- `lib/simpleshop_theme/cart.ex` -- Modify `lib/simpleshop_theme_web/live/shop_live/product_show.ex` -- Modify `lib/simpleshop_theme_web/live/shop_live/cart.ex` -- Modify `lib/simpleshop_theme_web/components/layouts.ex` (cart count) - ---- - -### Phase C: Stripe Checkout -**Status:** Not implemented -**Effort:** 0.5-1 day -**Dependencies:** Phase B (Cart) - -Stripe Checkout (hosted payment page) integration. - -**Dependencies to add:** -```elixir -# mix.exs -{:stripity_stripe, "~> 3.0"} -``` - -**Config:** -```elixir -# config/runtime.exs -config :stripity_stripe, - api_key: System.get_env("STRIPE_SECRET_KEY") - -# Also need STRIPE_WEBHOOK_SECRET for webhook verification -``` - -**Implementation:** -```elixir -# lib/simpleshop_theme/checkout.ex -defmodule SimpleshopTheme.Checkout do - def create_session(cart, success_url, cancel_url) do - line_items = Enum.map(cart, fn {variant_id, qty} -> - variant = Products.get_variant!(variant_id) - %{ - price_data: %{ - currency: "gbp", - unit_amount: variant.price_cents, - product_data: %{ - name: "#{variant.product.title} - #{variant.title}", - images: [hd(variant.product.images)["src"]] - } - }, - quantity: qty - } - end) - - Stripe.Checkout.Session.create(%{ - mode: "payment", - line_items: line_items, - success_url: success_url <> "?session_id={CHECKOUT_SESSION_ID}", - cancel_url: cancel_url, - shipping_address_collection: %{allowed_countries: ["GB"]}, - metadata: %{cart: Jason.encode!(cart)} - }) - end -end -``` - -**Webhook handler:** -```elixir -# lib/simpleshop_theme_web/controllers/stripe_webhook_controller.ex -def handle_event(%Stripe.Event{type: "checkout.session.completed"} = event) do - session = event.data.object - cart = Jason.decode!(session.metadata["cart"]) - shipping = session.shipping_details - - # Create order and push to Printify (Phase D) - Orders.create_from_checkout(session, cart, shipping) -end -``` - -**Files to create:** -- `lib/simpleshop_theme/checkout.ex` -- `lib/simpleshop_theme_web/controllers/stripe_webhook_controller.ex` -- `lib/simpleshop_theme_web/live/shop_live/checkout_success.ex` -- `lib/simpleshop_theme_web/live/shop_live/checkout_cancel.ex` - -**Routes:** -```elixir -post "/webhooks/stripe", StripeWebhookController, :handle -live "/checkout/success", ShopLive.CheckoutSuccess -live "/checkout/cancel", ShopLive.CheckoutCancel -``` - ---- - -### Phase D: Orders + Printify Fulfillment -**Status:** Not implemented -**Effort:** 0.5-1 day -**Dependencies:** Phase C (Stripe Checkout) - -Create orders locally and push to Printify for fulfillment. - -**Schema:** -```elixir -# orders table -field :stripe_session_id, :string -field :stripe_payment_intent_id, :string -field :printify_order_id, :string -field :status, Ecto.Enum, values: [:pending, :paid, :submitted, :in_production, :shipped, :delivered, :cancelled] -field :total_cents, :integer -field :shipping_address, :map -field :customer_email, :string -timestamps() - -# order_items table -field :quantity, :integer -field :unit_price_cents, :integer -field :unit_cost_cents, :integer # For profit tracking -belongs_to :order -belongs_to :product_variant -``` - -**Implementation:** -```elixir -# lib/simpleshop_theme/orders.ex -def create_from_checkout(stripe_session, cart, shipping) do - Repo.transaction(fn -> - # 1. Create local order - order = create_order(stripe_session, shipping) - - # 2. Create order items - create_order_items(order, cart) - - # 3. Push to Printify - case Printify.Client.create_order(order) do - {:ok, printify_response} -> - update_order(order, %{ - printify_order_id: printify_response["id"], - status: :submitted - }) - {:error, reason} -> - # Log error but don't fail - can retry later - Logger.error("Failed to submit to Printify: #{inspect(reason)}") - update_order(order, %{status: :paid}) # Mark as paid, needs manual submission - end - end) -end -``` - -**Printify order creation:** -```elixir -# Add to lib/simpleshop_theme/printify/client.ex -def create_order(order) do - body = %{ - external_id: order.id, - shipping_method: 1, # Standard - address_to: %{ - first_name: order.shipping_address["name"] |> String.split() |> hd(), - last_name: order.shipping_address["name"] |> String.split() |> List.last(), - email: order.customer_email, - address1: order.shipping_address["line1"], - address2: order.shipping_address["line2"], - city: order.shipping_address["city"], - zip: order.shipping_address["postal_code"], - country: order.shipping_address["country"] - }, - line_items: Enum.map(order.items, fn item -> - %{ - product_id: item.variant.product.printify_id, - variant_id: item.variant.printify_variant_id, - quantity: item.quantity - } - end) - } - - post("/shops/#{shop_id()}/orders.json", body) -end -``` - -**Files to create:** -- `lib/simpleshop_theme/orders.ex` -- `lib/simpleshop_theme/orders/order.ex` -- `lib/simpleshop_theme/orders/order_item.ex` -- `priv/repo/migrations/*_create_orders.exs` -- Update `lib/simpleshop_theme/printify/client.ex` with `create_order/1` - ---- - -### Phase E: Cost Verification at Checkout (Safety Net) -**Status:** Not implemented -**Effort:** 0.25 days -**Dependencies:** Phase D (Orders) - -Verify Printify costs haven't changed before completing checkout. - -**Implementation:** -```elixir -# In checkout flow, before creating Stripe session -def verify_costs(cart) do - Enum.reduce_while(cart, :ok, fn {variant_id, _qty}, _acc -> - variant = Products.get_variant!(variant_id) - - case Printify.Client.get_product(variant.product.printify_id) do - {:ok, printify_product} -> - current_cost = find_variant_cost(printify_product, variant.printify_variant_id) - - if current_cost != variant.cost_cents do - # Update local cost - Products.update_variant_cost(variant, current_cost) - - if cost_increase_exceeds_threshold?(variant.cost_cents, current_cost) do - {:halt, {:error, :costs_changed, variant}} - else - {:cont, :ok} - end - else - {:cont, :ok} - end - - {:error, _} -> - # Can't verify - proceed but log warning - Logger.warning("Could not verify costs for variant #{variant_id}") - {:cont, :ok} - end - end) -end -``` - -This ensures sellers never unknowingly sell at a loss due to Printify price changes. - ---- - -## Quick Wins (Low Effort) - -### CSS Cache Warming on Startup -**Status:** Not implemented -**Effort:** Small - -Currently the CSS cache (ETS) is created on startup but not pre-warmed. The first request after server restart generates CSS on-demand. - -**Implementation:** -Add cache warming to `lib/simpleshop_theme/application.ex`: -```elixir -# After supervisor starts -Task.start(fn -> - settings = SimpleshopTheme.Settings.get_theme_settings() - css = SimpleshopTheme.Theme.CSSGenerator.generate(settings) - SimpleshopTheme.Theme.CSSCache.put(css) -end) -``` - -### Navigation Links Between Admin and Shop -**Status:** Not implemented -**Effort:** Small - -No links exist to navigate between the theme editor (`/admin/theme`) and the public shop (`/`). - -**Implementation:** -- Add "View Shop" button in theme editor header -- Add "Edit Theme" link in shop header (when authenticated) - -### Collection Slug Routes -**Status:** Partial -**Effort:** Small - -Currently we have `/products` but the original plan included `/collections/:slug` for filtered views by category. - -### Enhanced Contact Page -**Status:** Not implemented -**Effort:** Small - -The current contact page has subtle footer social icons that are easy to miss. Improvements: - -1. **Newsletter signup card** - Prominent card encouraging newsletter subscription as the best way to stay updated (already in footer, but deserves dedicated placement) - -2. **Social media links card** - Full-width card listing social platforms with icons and text labels: - ``` - [Instagram icon] Instagram - [Patreon icon] Patreon - [TikTok icon] TikTok - [Facebook icon] Facebook - [Pinterest icon] Pinterest - ``` - This makes social links more discoverable than the current small footer icons. - -**Implementation:** -- Add `newsletter_card/1` component to ShopComponents -- Add `social_links_card/1` component with configurable platforms -- Update contact page template to include both cards -- Consider making platforms configurable via theme settings - ---- - -## Medium Features - -### Page Builder (Database-Driven Pages) -**Status:** Planned (see `docs/plans/page-builder.md`) -**Effort:** Large - -Allow shop owners to build custom pages by combining pre-built sections: -- Hero, Featured Products, Testimonials, Newsletter, etc. -- Drag-and-drop section ordering -- Per-section configuration -- Database-backed page storage - ---- - -## Future Features (Large Scope) - -### Multi-Admin Support -Currently single-user authentication: -- Multiple admin users -- Role-based permissions -- Audit logging - -### Custom Domains -Allow shops to use their own domain: -- Domain verification -- SSL certificate provisioning -- DNS configuration guidance - -### Theme Export/Import -Backup and restore theme settings: -- JSON export of all settings -- Import with validation -- Preset sharing between shops - -### Advanced Theme Features -- Custom CSS injection -- Custom JavaScript snippets -- Code-level overrides for developers - -### Multi-Provider Support (Future) -Support multiple POD providers beyond Printify: -- Prodigi (better for art prints) -- Gelato (global fulfillment) -- Provider-agnostic product model -- Price comparison across providers - ---- - -## Technical Debt - -### Test Coverage -Phase 9 testing is basic. Areas needing better coverage: -- Shop LiveView integration tests -- CSS cache invalidation flow -- Theme application across all pages -- Responsive behaviour -- Accessibility validation - -### Error Handling -- Better error states for missing products -- Graceful degradation when theme settings are invalid -- Network error handling in LiveView - -### Rename Project to SimpleShop -**Status:** Not implemented -**Effort:** Medium - -The project is currently named `simpleshop_theme` (reflecting its origins as a theme system), but it's now a full e-commerce storefront. Rename to `simple_shop` or `simpleshop` to reflect this. - -**Files to update:** -- `mix.exs` - app name -- `lib/simpleshop_theme/` → `lib/simple_shop/` -- `lib/simpleshop_theme_web/` → `lib/simple_shop_web/` -- All module names (`SimpleshopTheme` → `SimpleShop`) -- `config/*.exs` - endpoint and repo references -- `test/` directories -- Database file name - ---- - -## Completed (For Reference) - -### Sample Content ("Wildprint Studio") ✅ -- 16 POD products across 5 categories -- Nature/botanical theme with testimonials -- UK-focused (prices in £) -- Printify API integration for mockup generation (`mix generate_mockups`) - -### Phase 1-8: Theme Editor ✅ -- Theme settings schema and persistence -- CSS three-layer architecture -- 8 theme presets -- All customisation controls -- Logo/header image uploads -- SVG recolouring -- Preview system with 7 pages - -### Phase 9: Storefront Integration ✅ -- Public shop routes (/, /products, /products/:id, /cart, /about, /contact) -- Shared PageTemplates for shop and preview -- CSS injection via shop layout -- Themed error pages (404/500) -- Dev routes for error page preview