Mark as completed: - Navigation links between admin and shop - Collection routes with filtering and sorting - Header navigation accessibility improvements Update Phase 9 routes to reflect current implementation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14 KiB
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:
# 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:
- Migrate OAuth + Client modules from
simpleshop_printifydemo- Adapt
Simpleshop.Printify.OAuth→SimpleshopTheme.Printify.OAuth - Adapt
Simpleshop.Printify.Client→SimpleshopTheme.Printify.Client - Adapt
Simpleshop.Printify.TokenStore→SimpleshopTheme.Printify.TokenStore
- Adapt
- Create
SimpleshopTheme.Productscontext with schemas - Add
mix sync_productstask to pull products from Printify - Add webhook endpoint for
product:publish:startedevents - Replace
PreviewDatacalls in LiveViews withProductscontext queries - Store cost history on each sync for future profit analytics
Webhook flow:
- Seller clicks "Publish to SimpleShop" in Printify dashboard
- Printify fires
product:publish:startedwith product data - SimpleShop stores product locally in SQLite
- SimpleShop calls Printify "publish succeeded" endpoint
Files to create:
lib/simpleshop_theme/printify/oauth.exlib/simpleshop_theme/printify/token_store.exlib/simpleshop_theme/products.exlib/simpleshop_theme/products/product.exlib/simpleshop_theme/products/variant.exlib/simpleshop_theme/products/cost_history.exlib/simpleshop_theme_web/controllers/printify_webhook_controller.exlib/mix/tasks/sync_products.expriv/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:
# 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:
# mix.exs
{:stripity_stripe, "~> 3.0"}
Config:
# config/runtime.exs
config :stripity_stripe,
api_key: System.get_env("STRIPE_SECRET_KEY")
# Also need STRIPE_WEBHOOK_SECRET for webhook verification
Implementation:
# 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:
# 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.exlib/simpleshop_theme_web/controllers/stripe_webhook_controller.exlib/simpleshop_theme_web/live/shop_live/checkout_success.exlib/simpleshop_theme_web/live/shop_live/checkout_cancel.ex
Routes:
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:
# 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:
# 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:
# 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.exlib/simpleshop_theme/orders/order.exlib/simpleshop_theme/orders/order_item.expriv/repo/migrations/*_create_orders.exs- Update
lib/simpleshop_theme/printify/client.exwithcreate_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:
# 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)
Enhanced Contact Page
Status: Not implemented Effort: Small
The current contact page has subtle footer social icons that are easy to miss. Improvements:
-
Newsletter signup card - Prominent card encouraging newsletter subscription as the best way to stay updated (already in footer, but deserves dedicated placement)
-
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] PinterestThis makes social links more discoverable than the current small footer icons.
Implementation:
- Add
newsletter_card/1component to ShopComponents - Add
social_links_card/1component 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 namelib/simpleshop_theme/→lib/simple_shop/lib/simpleshop_theme_web/→lib/simple_shop_web/- All module names (
SimpleshopTheme→SimpleShop) config/*.exs- endpoint and repo referencestest/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 (/, /collections/:slug, /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
CSS Cache Warming on Startup ✅
- ETS cache pre-warmed in
CSSCache.init/1 - First request doesn't need to generate CSS
Navigation Links Between Admin and Shop ✅
- "View Shop" button in theme editor header
- Collapsible navigation sidebar in theme editor
Collection Routes with Filtering & Sorting ✅
/collections/:slugroutes for category filtering/collections/allfor all products- Product sorting (featured, newest, price, name)
- Sort parameter preserved in URL across navigation
- Category filter pills with sort persistence
Header Navigation Accessibility ✅
- Current page is not a link (avoids self-links)
- Logo links to home except when on home page
aria-current="page"with visual underline indicator