docs: reorganize documentation and add ROADMAP
- Rewrite README.md with proper project overview, setup, and structure - Create ROADMAP.md capturing future improvements and known gaps - Move reference docs to docs/ directory: - docs/spec.md (theme system specification) - docs/plans/sample-content.md (POD content refresh plan) - docs/plans/page-builder.md (existing) - docs/research/typography.md - docs/research/ux-patterns.md - Delete obsolete planning files: - PLAN.md (work complete) - PHASE_9_PLAN.md (work complete) - PHASE_9_PROGRESS.md (stale tracker) - RECENT_CHANGES.md (stale) - Keep AGENTS.md (Phoenix codegen guidelines) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a2d655d302
commit
6b6c7cc148
923
PHASE_9_PLAN.md
923
PHASE_9_PLAN.md
@ -1,923 +0,0 @@
|
|||||||
# SimpleShop Theme - Phase 9 Implementation Plan
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This plan covers **Phase 9: Storefront Integration** - applying the theme system to the actual public-facing shop. Based on my analysis, Phases 1-8 are complete, with a fully functional theme editor at `/admin/theme`. Phase 9 will create the public storefront that uses the saved theme settings.
|
|
||||||
|
|
||||||
## Current State Summary
|
|
||||||
|
|
||||||
✅ **Completed (Phases 1-8):**
|
|
||||||
- Settings and Media contexts with SQLite storage
|
|
||||||
- CSS three-layer architecture (primitives, attributes, semantic)
|
|
||||||
- Theme editor LiveView with 8 presets
|
|
||||||
- All 7 preview pages (home, collection, pdp, cart, about, contact, error)
|
|
||||||
- Full customization controls (mood, typography, shape, density, colors, layout, toggles)
|
|
||||||
- Logo and header image uploads with BLOB storage
|
|
||||||
- SVG recoloring functionality
|
|
||||||
- CSS generation system
|
|
||||||
- Preview data system (mock products, testimonials, categories)
|
|
||||||
|
|
||||||
❌ **Not Started (Phase 9):**
|
|
||||||
- Public storefront routes
|
|
||||||
- Storefront LiveViews
|
|
||||||
- Theme CSS injection for public pages
|
|
||||||
- Plug to load theme settings
|
|
||||||
- CSS cache warming
|
|
||||||
- Real product integration
|
|
||||||
|
|
||||||
## Architecture Decisions
|
|
||||||
|
|
||||||
### 1. Routing Strategy
|
|
||||||
- **Public routes**: Use root-level paths (`/`, `/products/:slug`, `/cart`, etc.)
|
|
||||||
- **No shop slug**: Since this app IS the shop (not multi-tenant), no slug needed
|
|
||||||
- **Future-proof**: Structure allows adding multi-shop support later if needed
|
|
||||||
|
|
||||||
### 2. LiveView Structure
|
|
||||||
- **Reuse preview templates**: The preview pages are already production-ready
|
|
||||||
- **Extract to shared components**: Move reusable pieces from preview_pages/ to components/
|
|
||||||
- **Storefront-specific LiveViews**: Create dedicated LiveViews that handle real data
|
|
||||||
|
|
||||||
### 3. CSS Application
|
|
||||||
- **Same three-layer system**: Use identical CSS as theme editor
|
|
||||||
- **Root element**: Use `.shop-root` class with data attributes
|
|
||||||
- **Inline CSS injection**: Inject generated CSS into layout via assign
|
|
||||||
- **ETS caching**: Use CSSCache for production performance
|
|
||||||
- **Cache invalidation**: Invalidate on theme settings save
|
|
||||||
|
|
||||||
### 4. Data Handling
|
|
||||||
- **Smart degradation**: Show empty states when no products exist
|
|
||||||
- **Graceful fallback**: Use preview data patterns for missing content
|
|
||||||
- **Real products**: When Products context exists, use real data
|
|
||||||
|
|
||||||
## Implementation Steps
|
|
||||||
|
|
||||||
### Step 1: Extract Shared Components
|
|
||||||
|
|
||||||
**Goal**: Move reusable preview components to shared location for use in both admin and storefront
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/announcement_bar.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/header.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/footer.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/search_modal.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/product_card.ex`
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/preview_pages.ex` - Import and use new components
|
|
||||||
- All preview templates - Update to use new component modules
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create `lib/simpleshop_theme_web/components/shop/` directory
|
|
||||||
2. Extract `announcement_bar/1` from preview_pages.ex to `shop/announcement_bar.ex`
|
|
||||||
3. Extract `shop_header/1` to `shop/header.ex`
|
|
||||||
4. Extract `shop_footer/1` to `shop/footer.ex`
|
|
||||||
5. Extract `search_modal/1` to `shop/search_modal.ex`
|
|
||||||
6. Create new `product_card/1` component (used in multiple places)
|
|
||||||
7. Update preview_pages.ex to import and use new components
|
|
||||||
8. Update all 7 preview templates to use new component paths
|
|
||||||
9. Test theme editor still works correctly
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
- Visit `/admin/theme` and verify all 7 preview pages still render correctly
|
|
||||||
- No visual regressions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 2: Create LoadTheme Plug
|
|
||||||
|
|
||||||
**Goal**: Create a plug that loads theme settings and generated CSS for every public request
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/plugs/load_theme.ex`
|
|
||||||
- `test/simpleshop_theme_web/plugs/load_theme_test.exs`
|
|
||||||
|
|
||||||
**Plug functionality:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.Plugs.LoadTheme do
|
|
||||||
import Plug.Conn
|
|
||||||
alias SimpleshopTheme.Settings
|
|
||||||
alias SimpleshopTheme.Theme.{CSSGenerator, CSSCache}
|
|
||||||
|
|
||||||
def init(opts), do: opts
|
|
||||||
|
|
||||||
def call(conn, _opts) do
|
|
||||||
# Try cache first
|
|
||||||
{theme_settings, generated_css} =
|
|
||||||
case CSSCache.get() do
|
|
||||||
{:ok, css} ->
|
|
||||||
{Settings.get_theme_settings(), css}
|
|
||||||
:miss ->
|
|
||||||
settings = Settings.get_theme_settings()
|
|
||||||
css = CSSGenerator.generate(settings)
|
|
||||||
CSSCache.put(css)
|
|
||||||
{settings, css}
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> assign(:theme_settings, theme_settings)
|
|
||||||
|> assign(:generated_css, generated_css)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add plug to public pipeline
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create plug file with init/1 and call/2 functions
|
|
||||||
2. Implement cache-first CSS loading strategy
|
|
||||||
3. Assign both `:theme_settings` and `:generated_css` to conn
|
|
||||||
4. Add plug to `:browser` pipeline in router (before routes)
|
|
||||||
5. Write tests for plug behavior
|
|
||||||
6. Test cache hit and cache miss scenarios
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run `mix test test/simpleshop_theme_web/plugs/load_theme_test.exs`
|
|
||||||
- Verify assigns are available in subsequent requests
|
|
||||||
- Verify CSS is cached and reused
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3: Create Storefront Layout
|
|
||||||
|
|
||||||
**Goal**: Create a dedicated layout for the public storefront with theme CSS injection
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts/shop.html.heex`
|
|
||||||
|
|
||||||
**Layout structure:**
|
|
||||||
```heex
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="h-full">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="csrf-token" content={get_csrf_token()} />
|
|
||||||
<.live_title><%= assigns[:page_title] || @theme_settings.site_name %></.live_title>
|
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}></script>
|
|
||||||
|
|
||||||
<!-- Inject theme CSS -->
|
|
||||||
<style id="theme-css">
|
|
||||||
<%= Phoenix.HTML.raw(@generated_css) %>
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="h-full">
|
|
||||||
<div class="shop-root h-full"
|
|
||||||
data-mood={@theme_settings.mood}
|
|
||||||
data-typography={@theme_settings.typography}
|
|
||||||
data-shape={@theme_settings.shape}
|
|
||||||
data-density={@theme_settings.density}
|
|
||||||
data-grid={@theme_settings.grid_columns}
|
|
||||||
data-header={@theme_settings.header_layout}
|
|
||||||
data-sticky={to_string(@theme_settings.sticky_header)}
|
|
||||||
data-layout={@theme_settings.layout_width}
|
|
||||||
data-shadow={@theme_settings.card_shadow}>
|
|
||||||
<%= @inner_content %>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts.ex` - Add `shop/1` layout function
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create new shop.html.heex layout file
|
|
||||||
2. Add `.shop-root` wrapper with all theme data attributes
|
|
||||||
3. Inject `@generated_css` into `<style>` tag
|
|
||||||
4. Use `@theme_settings.site_name` for page title default
|
|
||||||
5. Add shop layout function to layouts.ex module
|
|
||||||
6. Ensure Google Fonts are loaded (already in root.html.heex)
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Inspect HTML output to verify data attributes present
|
|
||||||
- Verify CSS injection works
|
|
||||||
- Check that layout compiles without errors
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 4: Create Home Page LiveView
|
|
||||||
|
|
||||||
**Goal**: Create the public homepage LiveView using real/mock data
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/home.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/home.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/home_test.exs`
|
|
||||||
|
|
||||||
**LiveView structure:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.Home do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
alias SimpleshopTheme.Theme.PreviewData
|
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
# Use real products when available, else preview data
|
|
||||||
products = PreviewData.products()
|
|
||||||
categories = PreviewData.categories()
|
|
||||||
testimonials = PreviewData.testimonials()
|
|
||||||
|
|
||||||
{:ok, assign(socket,
|
|
||||||
page_title: "Home",
|
|
||||||
products: products,
|
|
||||||
categories: categories,
|
|
||||||
testimonials: testimonials
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Template approach:**
|
|
||||||
- Copy structure from `preview_pages/home.html.heex`
|
|
||||||
- Use new shared components (header, footer, announcement_bar)
|
|
||||||
- Remove preview-specific wrapper divs
|
|
||||||
- Ensure data-driven rendering from assigns
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add home route
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create home LiveView module with mount function
|
|
||||||
2. Load preview data (real products when available)
|
|
||||||
3. Create home template based on preview version
|
|
||||||
4. Use shared components for header, footer, announcement bar
|
|
||||||
5. Add route: `live "/", ShopLive.Home, :index` in public scope
|
|
||||||
6. Set layout to `:shop` for route
|
|
||||||
7. Write tests for home page rendering
|
|
||||||
8. Test with and without products
|
|
||||||
|
|
||||||
**Router changes:**
|
|
||||||
```elixir
|
|
||||||
# Public storefront
|
|
||||||
scope "/", SimpleshopThemeWeb do
|
|
||||||
pipe_through :browser
|
|
||||||
|
|
||||||
live_session :public_shop, layout: {SimpleshopThemeWeb.Layouts, :shop} do
|
|
||||||
live "/", ShopLive.Home, :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Visit `/` and verify homepage renders
|
|
||||||
- Check theme styles are applied
|
|
||||||
- Test announcement bar toggle works
|
|
||||||
- Verify header layout variations work
|
|
||||||
- Test sticky header toggle
|
|
||||||
- Confirm products display (mock data)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 5: Create Collection Page LiveView
|
|
||||||
|
|
||||||
**Goal**: Create product collection/grid page
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/collection.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/collection.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/collection_test.exs`
|
|
||||||
|
|
||||||
**LiveView structure:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.Collection do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
alias SimpleshopTheme.Theme.PreviewData
|
|
||||||
|
|
||||||
def mount(%{"slug" => slug}, _session, socket) do
|
|
||||||
# Future: Load real collection by slug
|
|
||||||
# For now: Use preview products filtered by category
|
|
||||||
products = PreviewData.products()
|
|
||||||
|
|
||||||
{:ok, assign(socket,
|
|
||||||
page_title: "Shop All Products",
|
|
||||||
collection_name: "All Products",
|
|
||||||
products: products,
|
|
||||||
filter: nil,
|
|
||||||
sort: "featured"
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("filter", %{"category" => category}, socket) do
|
|
||||||
# Filter products by category
|
|
||||||
{:noreply, assign(socket, filter: category)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("sort", %{"by" => sort_by}, socket) do
|
|
||||||
# Sort products
|
|
||||||
{:noreply, assign(socket, sort: sort_by)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Template approach:**
|
|
||||||
- Copy from `preview_pages/collection.html.heex`
|
|
||||||
- Implement filter sidebar (responds to filter events)
|
|
||||||
- Implement sort dropdown (price, name, featured)
|
|
||||||
- Use product_card component
|
|
||||||
- Respect grid_columns setting (2/3/4)
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add collection route
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create collection LiveView module
|
|
||||||
2. Implement filtering and sorting logic
|
|
||||||
3. Create collection template based on preview
|
|
||||||
4. Add route: `live "/collections/:slug", ShopLive.Collection, :show`
|
|
||||||
5. Handle empty state (no products)
|
|
||||||
6. Write tests for filtering and sorting
|
|
||||||
7. Test grid columns respect theme setting
|
|
||||||
|
|
||||||
**Router changes:**
|
|
||||||
```elixir
|
|
||||||
live "/collections/:slug", ShopLive.Collection, :show
|
|
||||||
live "/collections", ShopLive.Collection, :index # "All Products"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Visit `/collections` and `/collections/tshirts`
|
|
||||||
- Test filter sidebar works
|
|
||||||
- Test sort dropdown changes order
|
|
||||||
- Verify grid columns (2, 3, 4) work correctly
|
|
||||||
- Check card shadow setting is respected
|
|
||||||
- Test hover image toggle works
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 6: Create Product Detail Page (PDP) LiveView
|
|
||||||
|
|
||||||
**Goal**: Create individual product page with gallery, details, add to cart
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/product.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/product.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/product_test.exs`
|
|
||||||
|
|
||||||
**LiveView structure:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.Product do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
alias SimpleshopTheme.Theme.PreviewData
|
|
||||||
|
|
||||||
def mount(%{"slug" => slug}, _session, socket) do
|
|
||||||
# Future: Load real product by slug
|
|
||||||
# For now: Use first preview product
|
|
||||||
[product | _] = PreviewData.products()
|
|
||||||
|
|
||||||
{:ok, assign(socket,
|
|
||||||
page_title: product.name,
|
|
||||||
product: product,
|
|
||||||
selected_variant: nil,
|
|
||||||
quantity: 1,
|
|
||||||
selected_image: 0
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("select_variant", %{"variant_id" => id}, socket) do
|
|
||||||
{:noreply, assign(socket, selected_variant: id)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("select_image", %{"index" => index}, socket) do
|
|
||||||
{:noreply, assign(socket, selected_image: String.to_integer(index))}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("add_to_cart", _params, socket) do
|
|
||||||
# Future: Add to cart functionality
|
|
||||||
{:noreply, put_flash(socket, :info, "Added to cart")}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Template approach:**
|
|
||||||
- Copy from `preview_pages/pdp.html.heex`
|
|
||||||
- Image gallery with thumbnails
|
|
||||||
- Variant selector (size, color)
|
|
||||||
- Quantity selector
|
|
||||||
- Add to cart button
|
|
||||||
- Respect toggle settings: `pdp_trust_badges`, `pdp_reviews`, `pdp_related_products`
|
|
||||||
- Gallery position: left/right based on `gallery_position` setting
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add product route
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create product LiveView module
|
|
||||||
2. Implement variant selection logic
|
|
||||||
3. Implement image gallery navigation
|
|
||||||
4. Create product template based on preview
|
|
||||||
5. Add route: `live "/products/:slug", ShopLive.Product, :show`
|
|
||||||
6. Handle toggle features (reviews, badges, related)
|
|
||||||
7. Write tests for variant selection and cart add
|
|
||||||
8. Test gallery position setting
|
|
||||||
|
|
||||||
**Router changes:**
|
|
||||||
```elixir
|
|
||||||
live "/products/:slug", ShopLive.Product, :show
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Visit `/products/example-tshirt`
|
|
||||||
- Test variant selection updates UI
|
|
||||||
- Test image gallery thumbnail clicks
|
|
||||||
- Verify trust badges show/hide based on setting
|
|
||||||
- Verify reviews show/hide based on setting
|
|
||||||
- Verify related products show/hide based on setting
|
|
||||||
- Test "Add to cart" button (shows flash for now)
|
|
||||||
- Test gallery position (left/right) setting
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 7: Create Cart Page LiveView
|
|
||||||
|
|
||||||
**Goal**: Create shopping cart page with line items and checkout
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/cart.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/cart.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/cart_test.exs`
|
|
||||||
|
|
||||||
**LiveView structure:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.Cart do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
alias SimpleshopTheme.Theme.PreviewData
|
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
# Future: Load real cart from session
|
|
||||||
# For now: Use preview cart items
|
|
||||||
cart_items = PreviewData.cart_items()
|
|
||||||
|
|
||||||
{:ok, assign(socket,
|
|
||||||
page_title: "Shopping Cart",
|
|
||||||
cart_items: cart_items,
|
|
||||||
subtotal: calculate_subtotal(cart_items),
|
|
||||||
shipping: 0,
|
|
||||||
tax: 0
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("update_quantity", %{"id" => id, "quantity" => qty}, socket) do
|
|
||||||
# Update item quantity
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("remove_item", %{"id" => id}, socket) do
|
|
||||||
# Remove item from cart
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("checkout", _params, socket) do
|
|
||||||
# Future: Proceed to checkout
|
|
||||||
{:noreply, put_flash(socket, :info, "Checkout coming soon")}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp calculate_subtotal(items) do
|
|
||||||
Enum.reduce(items, 0, fn item, acc ->
|
|
||||||
acc + (item.price * item.quantity)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Template approach:**
|
|
||||||
- Copy from `preview_pages/cart.html.heex`
|
|
||||||
- Line items with product image, name, variant, price, quantity
|
|
||||||
- Quantity update controls
|
|
||||||
- Remove item button
|
|
||||||
- Cart summary (subtotal, shipping, tax, total)
|
|
||||||
- Checkout button
|
|
||||||
- Empty cart state
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add cart route
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create cart LiveView module
|
|
||||||
2. Implement quantity update logic
|
|
||||||
3. Implement remove item logic
|
|
||||||
4. Create cart template based on preview
|
|
||||||
5. Add route: `live "/cart", ShopLive.Cart, :show`
|
|
||||||
6. Handle empty cart state
|
|
||||||
7. Write tests for cart operations
|
|
||||||
8. Test price calculations
|
|
||||||
|
|
||||||
**Router changes:**
|
|
||||||
```elixir
|
|
||||||
live "/cart", ShopLive.Cart, :show
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Visit `/cart`
|
|
||||||
- Test quantity update buttons
|
|
||||||
- Test remove item button
|
|
||||||
- Verify price calculations correct
|
|
||||||
- Test empty cart state
|
|
||||||
- Verify checkout button shows flash
|
|
||||||
- Check button style setting is respected
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 8: Create Static Pages (About, Contact, Error)
|
|
||||||
|
|
||||||
**Goal**: Create remaining static pages
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/about.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/about.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/contact.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/contact.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/controllers/error_html/404.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/about_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/contact_test.exs`
|
|
||||||
|
|
||||||
**About Page:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.About do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
{:ok, assign(socket, page_title: "About Us")}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Contact Page:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ShopLive.Contact do
|
|
||||||
use SimpleshopThemeWeb, :live_view
|
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
{:ok, assign(socket,
|
|
||||||
page_title: "Contact Us",
|
|
||||||
form: to_form(%{})
|
|
||||||
)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("submit_contact", %{"contact" => params}, socket) do
|
|
||||||
# Future: Send contact email
|
|
||||||
{:noreply, put_flash(socket, :info, "Message sent successfully")}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add routes
|
|
||||||
- `lib/simpleshop_theme_web/controllers/error_html.ex` - Add 404 template
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Create about LiveView and template (copy from preview)
|
|
||||||
2. Create contact LiveView and template (copy from preview)
|
|
||||||
3. Add contact form submission handler
|
|
||||||
4. Create 404 error template (copy from preview_pages/error.html.heex)
|
|
||||||
5. Add routes: `live "/about"` and `live "/contact"`
|
|
||||||
6. Write tests for static pages
|
|
||||||
7. Test contact form submission
|
|
||||||
|
|
||||||
**Router changes:**
|
|
||||||
```elixir
|
|
||||||
live "/about", ShopLive.About, :show
|
|
||||||
live "/contact", ShopLive.Contact, :show
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Visit `/about` and verify page renders
|
|
||||||
- Visit `/contact` and test form submission
|
|
||||||
- Visit invalid URL and verify 404 page shows with theme
|
|
||||||
- Check all pages respect theme settings
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 9: Wire Up CSS Cache Invalidation
|
|
||||||
|
|
||||||
**Goal**: Ensure CSS cache is invalidated when theme settings are saved
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme/settings.ex` - Add cache invalidation to update_theme_settings/1
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Ensure save_theme calls invalidation
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Update `Settings.update_theme_settings/1` to call `CSSCache.invalidate()` after save
|
|
||||||
2. Update `ThemeLive.Index.handle_event("save_theme")` to trigger cache refresh
|
|
||||||
3. Optionally warm cache after invalidation for first request performance
|
|
||||||
4. Test cache invalidation works correctly
|
|
||||||
|
|
||||||
**Code changes in Settings context:**
|
|
||||||
```elixir
|
|
||||||
def update_theme_settings(attrs) do
|
|
||||||
current = get_theme_settings()
|
|
||||||
changeset = ThemeSettings.changeset(current, attrs)
|
|
||||||
|
|
||||||
case changeset.valid? do
|
|
||||||
true ->
|
|
||||||
updated = Ecto.Changeset.apply_changes(changeset)
|
|
||||||
put_setting("theme_settings", updated, :json)
|
|
||||||
|
|
||||||
# Invalidate CSS cache
|
|
||||||
SimpleshopTheme.Theme.CSSCache.invalidate()
|
|
||||||
|
|
||||||
# Optionally warm cache immediately
|
|
||||||
css = SimpleshopTheme.Theme.CSSGenerator.generate(updated)
|
|
||||||
SimpleshopTheme.Theme.CSSCache.put(css)
|
|
||||||
|
|
||||||
{:ok, updated}
|
|
||||||
false ->
|
|
||||||
{:error, changeset}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Change theme settings in admin
|
|
||||||
- Save theme
|
|
||||||
- Visit public storefront
|
|
||||||
- Verify changes are reflected immediately
|
|
||||||
- Check cache is being used (add logging if needed)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 10: Add Cache Warming on Application Start
|
|
||||||
|
|
||||||
**Goal**: Pre-generate and cache theme CSS when application starts
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme/application.ex` - Add cache warming after supervisor start
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. Add cache warming function to Application module
|
|
||||||
2. Call after supervisor starts successfully
|
|
||||||
3. Handle case where no theme settings exist yet
|
|
||||||
4. Add logging for cache warming
|
|
||||||
|
|
||||||
**Code changes:**
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopTheme.Application do
|
|
||||||
def start(_type, _args) do
|
|
||||||
children = [
|
|
||||||
# ... existing children ...
|
|
||||||
SimpleshopTheme.Theme.CSSCache,
|
|
||||||
# ... more children ...
|
|
||||||
]
|
|
||||||
|
|
||||||
opts = [strategy: :one_for_one, name: SimpleshopTheme.Supervisor]
|
|
||||||
result = Supervisor.start_link(children, opts)
|
|
||||||
|
|
||||||
# Warm CSS cache on startup
|
|
||||||
Task.start(fn -> warm_theme_cache() end)
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
defp warm_theme_cache do
|
|
||||||
try do
|
|
||||||
settings = SimpleshopTheme.Settings.get_theme_settings()
|
|
||||||
css = SimpleshopTheme.Theme.CSSGenerator.generate(settings)
|
|
||||||
SimpleshopTheme.Theme.CSSCache.put(css)
|
|
||||||
Logger.info("Theme CSS cache warmed successfully")
|
|
||||||
rescue
|
|
||||||
e -> Logger.warning("Failed to warm theme cache: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Restart application
|
|
||||||
- Check logs for "Theme CSS cache warmed successfully"
|
|
||||||
- Visit storefront immediately (should be fast)
|
|
||||||
- Verify no database queries for CSS generation on first request
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 11: Update Navigation Links
|
|
||||||
|
|
||||||
**Goal**: Connect theme editor to storefront and vice versa
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/header.ex` - Add admin link when authenticated
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add "View Shop" link
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
1. In shop header component, add conditional admin link for authenticated users
|
|
||||||
2. In theme editor, add "View Shop" button in controls area
|
|
||||||
3. Ensure links use proper route helpers
|
|
||||||
4. Test navigation between admin and storefront
|
|
||||||
|
|
||||||
**Shop header changes:**
|
|
||||||
```heex
|
|
||||||
<%= if @current_user do %>
|
|
||||||
<a href={~p"/admin/theme"} class="admin-link">
|
|
||||||
Edit Theme
|
|
||||||
</a>
|
|
||||||
<% end %>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Theme editor changes (add near "Save Theme" button):**
|
|
||||||
```heex
|
|
||||||
<a href={~p"/"} target="_blank" class="btn btn-outline">
|
|
||||||
View Shop
|
|
||||||
</a>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- When logged in on storefront, verify "Edit Theme" link appears
|
|
||||||
- Click link and verify navigates to theme editor
|
|
||||||
- In theme editor, click "View Shop" link
|
|
||||||
- Verify opens storefront in new tab
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 12: Testing and Polish
|
|
||||||
|
|
||||||
**Goal**: Comprehensive testing and UX improvements
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- All test files - Add comprehensive coverage
|
|
||||||
- Various LiveViews - Add loading states, error handling
|
|
||||||
|
|
||||||
**Actions:**
|
|
||||||
|
|
||||||
1. **Write comprehensive tests:**
|
|
||||||
- Test all storefront LiveViews
|
|
||||||
- Test plug behavior
|
|
||||||
- Test cache invalidation flow
|
|
||||||
- Test theme application across all pages
|
|
||||||
- Test responsive behavior
|
|
||||||
|
|
||||||
2. **Add loading states:**
|
|
||||||
- Product loading skeletons
|
|
||||||
- Cart update feedback
|
|
||||||
- Form submission states
|
|
||||||
|
|
||||||
3. **Error handling:**
|
|
||||||
- Handle missing products gracefully
|
|
||||||
- Handle invalid routes
|
|
||||||
- Handle network errors
|
|
||||||
|
|
||||||
4. **Performance optimization:**
|
|
||||||
- Verify CSS cache is working
|
|
||||||
- Check database query counts
|
|
||||||
- Test with many products (when available)
|
|
||||||
|
|
||||||
5. **Accessibility:**
|
|
||||||
- Check color contrast ratios
|
|
||||||
- Verify keyboard navigation
|
|
||||||
- Add ARIA labels where needed
|
|
||||||
|
|
||||||
6. **Mobile testing:**
|
|
||||||
- Test all pages on mobile viewport
|
|
||||||
- Verify touch interactions work
|
|
||||||
- Check responsive grid layouts
|
|
||||||
|
|
||||||
7. **Cross-browser testing:**
|
|
||||||
- Test in Chrome, Firefox, Safari
|
|
||||||
- Verify CSS variables work correctly
|
|
||||||
- Check font loading
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run full test suite: `mix test`
|
|
||||||
- All tests pass
|
|
||||||
- No console errors
|
|
||||||
- Good Lighthouse scores
|
|
||||||
- Responsive on all devices
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary of Files
|
|
||||||
|
|
||||||
### Files to Create (23 files)
|
|
||||||
|
|
||||||
**Shared Components (5 files):**
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/announcement_bar.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/header.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/footer.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/search_modal.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/product_card.ex`
|
|
||||||
|
|
||||||
**Infrastructure (2 files):**
|
|
||||||
- `lib/simpleshop_theme_web/plugs/load_theme.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts/shop.html.heex`
|
|
||||||
|
|
||||||
**Storefront LiveViews (10 files):**
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/home.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/home.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/collection.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/collection.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/product.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/product.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/cart.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/cart.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/about.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/about.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/contact.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/shop_live/contact.html.heex`
|
|
||||||
|
|
||||||
**Error Pages (1 file):**
|
|
||||||
- `lib/simpleshop_theme_web/controllers/error_html/404.html.heex`
|
|
||||||
|
|
||||||
**Tests (6 files):**
|
|
||||||
- `test/simpleshop_theme_web/plugs/load_theme_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/home_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/collection_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/product_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/cart_test.exs`
|
|
||||||
- `test/simpleshop_theme_web/live/shop_live/about_test.exs`
|
|
||||||
|
|
||||||
### Files to Modify (9 files)
|
|
||||||
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add plug, public routes, shop layout
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts.ex` - Add shop layout function
|
|
||||||
- `lib/simpleshop_theme/settings.ex` - Add cache invalidation
|
|
||||||
- `lib/simpleshop_theme/application.ex` - Add cache warming
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Add "View Shop" link
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Update to use shared components
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/preview_pages.ex` - Remove extracted components
|
|
||||||
- All 7 preview page templates - Update component paths
|
|
||||||
- `lib/simpleshop_theme_web/components/shop/header.ex` - Add admin link
|
|
||||||
|
|
||||||
## Key Patterns to Follow
|
|
||||||
|
|
||||||
1. **CSS Application**: Always use `.shop-root` class with data attributes on root element
|
|
||||||
2. **Component Reuse**: Use shared components from `components/shop/` directory
|
|
||||||
3. **Preview Data**: Use `PreviewData` module for mock content until real Products context exists
|
|
||||||
4. **Cache Strategy**: Always check `CSSCache` before generating CSS
|
|
||||||
5. **Theme Settings**: Access via `@theme_settings` assign (provided by LoadTheme plug)
|
|
||||||
6. **Layout**: Use `:shop` layout for all public pages
|
|
||||||
7. **Testing**: Write tests alongside each LiveView
|
|
||||||
8. **Validation**: Test each step before moving to next
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
✅ Public storefront accessible at `/`
|
|
||||||
✅ All pages (home, collection, product, cart, about, contact) render correctly
|
|
||||||
✅ Theme settings from admin are applied to storefront
|
|
||||||
✅ CSS is cached for performance
|
|
||||||
✅ Cache invalidates when theme is saved
|
|
||||||
✅ Data attributes drive theme styling
|
|
||||||
✅ All toggle features work (announcement bar, sticky header, etc.)
|
|
||||||
✅ Grid columns, layout width, and other layout settings work
|
|
||||||
✅ Logo and header image display correctly
|
|
||||||
✅ Navigation between admin and storefront works
|
|
||||||
✅ Empty states handle gracefully (no products)
|
|
||||||
✅ All tests pass
|
|
||||||
✅ Mobile responsive
|
|
||||||
✅ No console errors
|
|
||||||
|
|
||||||
## Future Enhancements (After Phase 9)
|
|
||||||
|
|
||||||
1. **Products Context** - Add real product management
|
|
||||||
2. **Orders & Checkout** - Implement cart persistence and checkout flow
|
|
||||||
3. **Custom Domain Support** - Allow users to use their own domain
|
|
||||||
4. **SEO Optimization** - Meta tags, structured data, sitemaps
|
|
||||||
5. **Analytics Integration** - Track visitor behavior
|
|
||||||
6. **Email Templates** - Order confirmations, shipping notifications
|
|
||||||
7. **Multi-currency Support** - International selling
|
|
||||||
8. **Advanced Shipping** - Shipping rates, zones, carriers
|
|
||||||
9. **Discount Codes** - Promotional codes and sales
|
|
||||||
10. **Customer Accounts** - Registration, order history, wishlists
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- This implementation maintains perfect parity with the theme editor preview
|
|
||||||
- No JavaScript required for theming - purely CSS-based
|
|
||||||
- Self-contained in SQLite database (no external dependencies)
|
|
||||||
- Performance optimized with ETS caching
|
|
||||||
- Ready for multi-shop support in future (architecture supports it)
|
|
||||||
- Follows Phoenix/LiveView best practices throughout
|
|
||||||
- Mobile-first, accessible, performant
|
|
||||||
|
|
||||||
## Estimated Complexity
|
|
||||||
|
|
||||||
- **Step 1** (Extract Components): Medium - Refactoring existing code
|
|
||||||
- **Step 2** (LoadTheme Plug): Easy - Simple plug implementation
|
|
||||||
- **Step 3** (Storefront Layout): Easy - Template creation
|
|
||||||
- **Step 4** (Home Page): Medium - First full LiveView
|
|
||||||
- **Step 5** (Collection Page): Medium - Filtering/sorting logic
|
|
||||||
- **Step 6** (Product Page): Medium - Variant selection, gallery
|
|
||||||
- **Step 7** (Cart Page): Medium - Cart operations
|
|
||||||
- **Step 8** (Static Pages): Easy - Simple templates
|
|
||||||
- **Step 9** (Cache Invalidation): Easy - Add function calls
|
|
||||||
- **Step 10** (Cache Warming): Easy - Application startup hook
|
|
||||||
- **Step 11** (Navigation Links): Easy - Add links
|
|
||||||
- **Step 12** (Testing & Polish): Large - Comprehensive testing
|
|
||||||
|
|
||||||
**Total Effort**: ~2-3 days for experienced Phoenix developer
|
|
||||||
|
|
||||||
## Risk Assessment
|
|
||||||
|
|
||||||
**Low Risk:**
|
|
||||||
- CSS system is proven in preview pages
|
|
||||||
- No database schema changes needed
|
|
||||||
- No breaking changes to existing code
|
|
||||||
- Can test each step independently
|
|
||||||
|
|
||||||
**Medium Risk:**
|
|
||||||
- Component extraction might miss edge cases
|
|
||||||
- Cache invalidation timing could cause brief inconsistencies
|
|
||||||
|
|
||||||
**Mitigation:**
|
|
||||||
- Comprehensive testing after each step
|
|
||||||
- Keep preview pages working during refactor
|
|
||||||
- Add logging for cache operations
|
|
||||||
- Test cache invalidation thoroughly
|
|
||||||
@ -1,419 +0,0 @@
|
|||||||
# Phase 9: Storefront Integration - Progress Tracker
|
|
||||||
|
|
||||||
**Last Updated:** 2026-01-01
|
|
||||||
**Status:** Not Started
|
|
||||||
**Current Step:** Step 0 (Preparation)
|
|
||||||
|
|
||||||
## Quick Reference
|
|
||||||
|
|
||||||
- **Detailed Plan:** `.claude/plans/snuggly-forging-cat.md`
|
|
||||||
- **Main Plan:** `PLAN.md` (lines 643-690)
|
|
||||||
- **Spec:** `SPEC.md`
|
|
||||||
|
|
||||||
## Progress Overview
|
|
||||||
|
|
||||||
- [ ] Step 1: Extract Shared Components
|
|
||||||
- [ ] Step 2: Create LoadTheme Plug
|
|
||||||
- [ ] Step 3: Create Storefront Layout
|
|
||||||
- [ ] Step 4: Create Home Page LiveView
|
|
||||||
- [ ] Step 5: Create Collection Page LiveView
|
|
||||||
- [ ] Step 6: Create Product Detail Page (PDP) LiveView
|
|
||||||
- [ ] Step 7: Create Cart Page LiveView
|
|
||||||
- [ ] Step 8: Create Static Pages (About, Contact, Error)
|
|
||||||
- [ ] Step 9: Wire Up CSS Cache Invalidation
|
|
||||||
- [ ] Step 10: Add Cache Warming on Application Start
|
|
||||||
- [ ] Step 11: Update Navigation Links
|
|
||||||
- [ ] Step 12: Testing and Polish
|
|
||||||
|
|
||||||
**Completion:** 0/12 steps (0%)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step-by-Step Progress
|
|
||||||
|
|
||||||
### Step 1: Extract Shared Components
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `refactor: extract shared shop components from preview pages`
|
|
||||||
|
|
||||||
**Goal:** Move reusable preview components to `components/shop/` for use in both admin and storefront
|
|
||||||
|
|
||||||
**Files to Create (5):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/announcement_bar.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/header.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/footer.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/search_modal.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/product_card.ex`
|
|
||||||
|
|
||||||
**Files to Modify (8):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages.ex` - Import new components
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/home.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/collection.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/pdp.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/cart.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/about.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/contact.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/preview_pages/error.html.heex`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Run `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
- [ ] Visit `/admin/theme` - verify all 7 preview pages still render
|
|
||||||
- [ ] No visual regressions
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 2: Create LoadTheme Plug
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add LoadTheme plug for public storefront`
|
|
||||||
|
|
||||||
**Goal:** Create plug that loads theme settings and CSS for every public request
|
|
||||||
|
|
||||||
**Files to Create (2):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/plugs/load_theme.ex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/plugs/load_theme_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add plug to `:browser` pipeline
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Run `mix test test/simpleshop_theme_web/plugs/load_theme_test.exs`
|
|
||||||
- [ ] Verify assigns available in requests
|
|
||||||
- [ ] Verify CSS cached and reused
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3: Create Storefront Layout
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add shop layout with theme CSS injection`
|
|
||||||
|
|
||||||
**Goal:** Create dedicated layout for public storefront with theme CSS injection
|
|
||||||
|
|
||||||
**Files to Create (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/layouts/shop.html.heex`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/layouts.ex` - Add `shop/1` function
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Inspect HTML - verify data attributes present
|
|
||||||
- [ ] Verify CSS injection works
|
|
||||||
- [ ] Layout compiles without errors
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 4: Create Home Page LiveView
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add public home page with theme support`
|
|
||||||
|
|
||||||
**Goal:** Create public homepage LiveView using real/mock data
|
|
||||||
|
|
||||||
**Files to Create (3):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/home.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/home.html.heex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/home_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add route: `live "/", ShopLive.Home, :index`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Visit `/` - verify homepage renders
|
|
||||||
- [ ] Theme styles applied correctly
|
|
||||||
- [ ] Announcement bar toggle works
|
|
||||||
- [ ] Header layout variations work
|
|
||||||
- [ ] Sticky header toggle works
|
|
||||||
- [ ] Products display (mock data)
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 5: Create Collection Page LiveView
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add collection/product grid page with filtering`
|
|
||||||
|
|
||||||
**Goal:** Create product collection/grid page with filtering and sorting
|
|
||||||
|
|
||||||
**Files to Create (3):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/collection.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/collection.html.heex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/collection_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add routes for collections
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Visit `/collections` and `/collections/:slug`
|
|
||||||
- [ ] Filter sidebar works
|
|
||||||
- [ ] Sort dropdown works
|
|
||||||
- [ ] Grid columns (2, 3, 4) work
|
|
||||||
- [ ] Card shadow setting respected
|
|
||||||
- [ ] Hover image toggle works
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 6: Create Product Detail Page (PDP) LiveView
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add product detail page with gallery and variants`
|
|
||||||
|
|
||||||
**Goal:** Create individual product page with gallery, details, add to cart
|
|
||||||
|
|
||||||
**Files to Create (3):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/product.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/product.html.heex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/product_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add route: `live "/products/:slug"`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Visit `/products/:slug`
|
|
||||||
- [ ] Variant selection works
|
|
||||||
- [ ] Image gallery works
|
|
||||||
- [ ] Trust badges show/hide based on setting
|
|
||||||
- [ ] Reviews show/hide based on setting
|
|
||||||
- [ ] Related products show/hide based on setting
|
|
||||||
- [ ] Add to cart shows flash
|
|
||||||
- [ ] Gallery position (left/right) works
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 7: Create Cart Page LiveView
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add shopping cart page with line items`
|
|
||||||
|
|
||||||
**Goal:** Create shopping cart page with line items and checkout
|
|
||||||
|
|
||||||
**Files to Create (3):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/cart.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/cart.html.heex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/cart_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add route: `live "/cart"`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Visit `/cart`
|
|
||||||
- [ ] Quantity update works
|
|
||||||
- [ ] Remove item works
|
|
||||||
- [ ] Price calculations correct
|
|
||||||
- [ ] Empty cart state works
|
|
||||||
- [ ] Checkout button shows flash
|
|
||||||
- [ ] Button style setting respected
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 8: Create Static Pages (About, Contact, Error)
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add static pages (about, contact, 404) with theme support`
|
|
||||||
|
|
||||||
**Goal:** Create remaining static pages
|
|
||||||
|
|
||||||
**Files to Create (6):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/about.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/about.html.heex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/contact.ex`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/shop_live/contact.html.heex`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/about_test.exs`
|
|
||||||
- [ ] `test/simpleshop_theme_web/live/shop_live/contact_test.exs`
|
|
||||||
|
|
||||||
**Files to Modify (2):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/router.ex` - Add routes
|
|
||||||
- [ ] `lib/simpleshop_theme_web/controllers/error_html.ex` - Add 404 template (or create if missing)
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Visit `/about` - page renders
|
|
||||||
- [ ] Visit `/contact` - form works
|
|
||||||
- [ ] Visit invalid URL - 404 shows with theme
|
|
||||||
- [ ] All pages respect theme settings
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 9: Wire Up CSS Cache Invalidation
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add CSS cache invalidation on theme save`
|
|
||||||
|
|
||||||
**Goal:** Ensure CSS cache invalidates when theme settings saved
|
|
||||||
|
|
||||||
**Files to Modify (2):**
|
|
||||||
- [ ] `lib/simpleshop_theme/settings.ex` - Add cache invalidation to `update_theme_settings/1`
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/index.ex` - Ensure save triggers invalidation
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Change theme in admin
|
|
||||||
- [ ] Save theme
|
|
||||||
- [ ] Visit storefront
|
|
||||||
- [ ] Verify changes reflected immediately
|
|
||||||
- [ ] Check cache being used
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 10: Add Cache Warming on Application Start
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add CSS cache warming on application startup`
|
|
||||||
|
|
||||||
**Goal:** Pre-generate and cache theme CSS when app starts
|
|
||||||
|
|
||||||
**Files to Modify (1):**
|
|
||||||
- [ ] `lib/simpleshop_theme/application.ex` - Add cache warming after supervisor start
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Restart application
|
|
||||||
- [ ] Check logs for "Theme CSS cache warmed successfully"
|
|
||||||
- [ ] Visit storefront immediately (should be fast)
|
|
||||||
- [ ] No DB queries for CSS on first request
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 11: Update Navigation Links
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `feat: add navigation between admin and storefront`
|
|
||||||
|
|
||||||
**Goal:** Connect theme editor to storefront and vice versa
|
|
||||||
|
|
||||||
**Files to Modify (2):**
|
|
||||||
- [ ] `lib/simpleshop_theme_web/components/shop/header.ex` - Add admin link when authenticated
|
|
||||||
- [ ] `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add "View Shop" link
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] When logged in on storefront, "Edit Theme" link appears
|
|
||||||
- [ ] Click link - navigates to theme editor
|
|
||||||
- [ ] In theme editor, click "View Shop" link
|
|
||||||
- [ ] Opens storefront in new tab
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 12: Testing and Polish
|
|
||||||
|
|
||||||
**Status:** ❌ Not Started
|
|
||||||
**Git Commit:** `test: add comprehensive tests and polish for Phase 9`
|
|
||||||
|
|
||||||
**Goal:** Comprehensive testing and UX improvements
|
|
||||||
|
|
||||||
**Testing Areas:**
|
|
||||||
- [ ] All storefront LiveViews tested
|
|
||||||
- [ ] Plug behavior tested
|
|
||||||
- [ ] Cache invalidation flow tested
|
|
||||||
- [ ] Theme application across all pages tested
|
|
||||||
- [ ] Responsive behavior tested
|
|
||||||
|
|
||||||
**Polish Areas:**
|
|
||||||
- [ ] Loading states added
|
|
||||||
- [ ] Error handling complete
|
|
||||||
- [ ] Performance optimized
|
|
||||||
- [ ] Accessibility checked
|
|
||||||
- [ ] Mobile tested
|
|
||||||
- [ ] Cross-browser tested
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- [ ] Run `mix test` - all tests pass
|
|
||||||
- [ ] No console errors
|
|
||||||
- [ ] Good Lighthouse scores
|
|
||||||
- [ ] Responsive on all devices
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Git Commit Log
|
|
||||||
|
|
||||||
Track all commits made during Phase 9 implementation:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Commits will be added here as we progress
|
|
||||||
# Format: [YYYY-MM-DD HH:MM] <commit-hash> <commit-message>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Issues / Blockers
|
|
||||||
|
|
||||||
Track any issues encountered:
|
|
||||||
|
|
||||||
- None yet
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Future Enhancements (Post-Phase 9)
|
|
||||||
|
|
||||||
1. Products Context - Add real product management
|
|
||||||
2. Orders & Checkout - Cart persistence and checkout flow
|
|
||||||
3. Custom Domain Support
|
|
||||||
4. SEO Optimization
|
|
||||||
5. Analytics Integration
|
|
||||||
6. Email Templates
|
|
||||||
7. Multi-currency Support
|
|
||||||
8. Advanced Shipping
|
|
||||||
9. Discount Codes
|
|
||||||
10. Customer Accounts
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Context Preservation
|
|
||||||
|
|
||||||
**Key Architectural Decisions:**
|
|
||||||
- CSS applied via `.shop-root` class with data attributes
|
|
||||||
- Same three-layer CSS system as theme editor
|
|
||||||
- LoadTheme plug provides `@theme_settings` and `@generated_css` assigns
|
|
||||||
- CSS cached in ETS via `CSSCache` GenServer
|
|
||||||
- Preview data used until real Products context exists
|
|
||||||
- Shared components in `components/shop/` directory
|
|
||||||
|
|
||||||
**Critical Files:**
|
|
||||||
- Theme editor: `lib/simpleshop_theme_web/live/theme_live/index.ex`
|
|
||||||
- CSS generator: `lib/simpleshop_theme/theme/css_generator.ex`
|
|
||||||
- Settings context: `lib/simpleshop_theme/settings.ex`
|
|
||||||
- Preview data: `lib/simpleshop_theme/theme/preview_data.ex`
|
|
||||||
|
|
||||||
**Current State (as of last update):**
|
|
||||||
- Theme editor fully functional at `/admin/theme`
|
|
||||||
- 8 presets working with real-time preview
|
|
||||||
- All customization options implemented
|
|
||||||
- Logo/header uploads with BLOB storage working
|
|
||||||
- SVG recoloring functional
|
|
||||||
- No public storefront routes yet (Phase 9 not started)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to Use This File
|
|
||||||
|
|
||||||
1. **Starting a new step:** Update the step status to "🚧 In Progress"
|
|
||||||
2. **Completing files:** Check off files as created/modified
|
|
||||||
3. **Finishing a step:**
|
|
||||||
- Mark step as "✅ Complete"
|
|
||||||
- Update completion percentage
|
|
||||||
- Make git commit with specified message
|
|
||||||
- Add commit to Git Commit Log section
|
|
||||||
4. **Encountering issues:** Add to Issues/Blockers section
|
|
||||||
5. **Taking notes:** Add to Notes field for each step
|
|
||||||
|
|
||||||
**For new conversations:** Read this file first to understand current progress and context.
|
|
||||||
964
PLAN.md
964
PLAN.md
@ -1,964 +0,0 @@
|
|||||||
# SimpleShop Theme - Implementation Plan
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Integrate the complete theme feature from `theme-demo-v28.html` into the Phoenix LiveView application. The system will provide 9 curated presets and extensive customization options with real-time preview. All data (including uploaded images) will be stored in a single SQLite database file.
|
|
||||||
|
|
||||||
**Key Concept:** This codebase IS the shop (like WordPress is one site). Multiple users can manage it, but there's only one shop/site per deployment.
|
|
||||||
|
|
||||||
## Core Architectural Decisions
|
|
||||||
|
|
||||||
### 1. Database Storage Strategy
|
|
||||||
- **Images as BLOBs in SQLite** - Complete self-containment in single database file
|
|
||||||
- **Application-level settings** - NO Shop model, just Settings tables for the site
|
|
||||||
- **Multiple admin users** - Any authenticated user can manage theme (no ownership model)
|
|
||||||
- **SVG stored as both BLOB and TEXT** - Enable recoloring feature
|
|
||||||
|
|
||||||
### 2. CSS Architecture
|
|
||||||
- **Three-layer CSS system** using Custom Properties:
|
|
||||||
- Layer 1: Primitives (static) - spacing, fonts, radii
|
|
||||||
- Layer 2: Theme tokens (dynamic) - generated from settings
|
|
||||||
- Layer 3: Semantic aliases (static) - friendly variable names
|
|
||||||
- **Inline `<style>` generation** in LiveView for instant preview
|
|
||||||
- **ETS cache** for storefront CSS (invalidate on save)
|
|
||||||
|
|
||||||
### 3. LiveView Structure
|
|
||||||
- **Single LiveView** (`ThemeLive.Index`) with nested function components
|
|
||||||
- **No iframe** - Direct preview in same LiveView using data attributes
|
|
||||||
- **Smart preview data** - Mock data initially, switch to real products when available
|
|
||||||
- **Optimistic UI** - Update preview immediately, persist on "Save" button
|
|
||||||
- **Idiomatic Phoenix/LiveView** - Use Tailwind utilities where possible, CSS variables for dynamic theming
|
|
||||||
|
|
||||||
### 4. Development Workflow
|
|
||||||
- **Semantic git commits** - Commit each logical step for easy rollback
|
|
||||||
- **Test-driven** - Write Phoenix tests alongside implementation (ExUnit, ConnCase, LiveViewTest)
|
|
||||||
- **Manual verification** - Test in browser after each phase
|
|
||||||
- **Progressive enhancement** - Each commit should leave app in working state
|
|
||||||
|
|
||||||
## Database Schema
|
|
||||||
|
|
||||||
**Philosophy:** No Shop model - this app IS the shop. Settings are site-wide, managed by any admin user.
|
|
||||||
|
|
||||||
### Migration 1: Create Settings Table
|
|
||||||
|
|
||||||
**File:** `priv/repo/migrations/YYYYMMDDHHMMSS_create_settings.exs`
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopTheme.Repo.Migrations.CreateSettings do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:settings, primary_key: false) do
|
|
||||||
add :id, :binary_id, primary_key: true
|
|
||||||
add :key, :string, null: false # e.g., "site_name", "theme_settings"
|
|
||||||
add :value, :text, null: false # JSON or plain text
|
|
||||||
add :value_type, :string, null: false, default: "string" # string, json, integer, boolean
|
|
||||||
|
|
||||||
timestamps(type: :utc_datetime)
|
|
||||||
end
|
|
||||||
|
|
||||||
create unique_index(:settings, [:key])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Migration 2: Create Images Table
|
|
||||||
|
|
||||||
**File:** `priv/repo/migrations/YYYYMMDDHHMMSS_create_images.exs`
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopTheme.Repo.Migrations.CreateImages do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:images, primary_key: false) do
|
|
||||||
add :id, :binary_id, primary_key: true
|
|
||||||
|
|
||||||
add :image_type, :string, null: false # "logo" | "header" | "product"
|
|
||||||
add :filename, :string, null: false
|
|
||||||
add :content_type, :string, null: false
|
|
||||||
add :file_size, :integer, null: false
|
|
||||||
|
|
||||||
# BLOB storage
|
|
||||||
add :data, :binary, null: false
|
|
||||||
|
|
||||||
# SVG support
|
|
||||||
add :is_svg, :boolean, default: false
|
|
||||||
add :svg_content, :text # For recoloring
|
|
||||||
|
|
||||||
# Optional: thumbnail for admin UI performance
|
|
||||||
add :thumbnail_data, :binary
|
|
||||||
|
|
||||||
timestamps(type: :utc_datetime)
|
|
||||||
end
|
|
||||||
|
|
||||||
create index(:images, [:image_type])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
### New Contexts and Schemas
|
|
||||||
|
|
||||||
```
|
|
||||||
lib/simpleshop_theme/
|
|
||||||
├── settings/
|
|
||||||
│ ├── setting.ex # Setting schema (key-value store)
|
|
||||||
│ └── theme_settings.ex # Embedded schema for theme settings
|
|
||||||
├── settings.ex # Settings context (site-wide config API)
|
|
||||||
├── media/
|
|
||||||
│ ├── image.ex # Image schema (replaces ShopImage)
|
|
||||||
│ └── svg_recolorer.ex # Recolor SVG logos
|
|
||||||
├── media.ex # Media context (image uploads/serving)
|
|
||||||
└── theme/
|
|
||||||
├── presets.ex # Preset definitions (9 presets)
|
|
||||||
├── css_generator.ex # Generate CSS from settings
|
|
||||||
├── css_cache.ex # ETS cache GenServer
|
|
||||||
└── preview_data.ex # Smart preview (mock → real products)
|
|
||||||
```
|
|
||||||
|
|
||||||
### LiveView Components
|
|
||||||
|
|
||||||
```
|
|
||||||
lib/simpleshop_theme_web/live/theme_live/
|
|
||||||
├── index.ex # Main LiveView
|
|
||||||
├── index.html.heex # Template
|
|
||||||
├── preset_selector.ex # 9 preset buttons (function component)
|
|
||||||
├── branding_section.ex # Logo/header uploads
|
|
||||||
├── customization_panel.ex # Accordion with all options
|
|
||||||
├── preview_frame.ex # Preview area with page switcher
|
|
||||||
└── preview_pages/
|
|
||||||
├── home.html.heex # Preview homepage
|
|
||||||
├── collection.html.heex # Preview product grid
|
|
||||||
├── pdp.html.heex # Preview product detail page
|
|
||||||
├── cart.html.heex # Preview cart
|
|
||||||
├── about.html.heex # Preview about page
|
|
||||||
├── contact.html.heex # Preview contact page
|
|
||||||
└── error.html.heex # Preview 404 page
|
|
||||||
```
|
|
||||||
|
|
||||||
### Controllers
|
|
||||||
|
|
||||||
```
|
|
||||||
lib/simpleshop_theme_web/controllers/
|
|
||||||
└── image_controller.ex # Serve images from BLOBs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Static Assets & Tests
|
|
||||||
|
|
||||||
```
|
|
||||||
priv/static/css/
|
|
||||||
├── theme-primitives.css # Layer 1: Fixed CSS variables
|
|
||||||
└── theme-semantic.css # Layer 3: Semantic aliases
|
|
||||||
|
|
||||||
assets/css/
|
|
||||||
└── app.css # Import theme CSS here
|
|
||||||
|
|
||||||
test/simpleshop_theme/
|
|
||||||
├── settings_test.exs # Settings context tests
|
|
||||||
├── media_test.exs # Media context tests
|
|
||||||
└── theme/
|
|
||||||
├── css_generator_test.exs # CSS generation tests
|
|
||||||
├── presets_test.exs # Preset validation tests
|
|
||||||
└── preview_data_test.exs # Preview data tests
|
|
||||||
|
|
||||||
test/simpleshop_theme_web/
|
|
||||||
├── controllers/
|
|
||||||
│ └── image_controller_test.exs
|
|
||||||
└── live/
|
|
||||||
└── theme_live_test.exs # LiveView integration tests
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Status
|
|
||||||
|
|
||||||
### Completed Phases (1-8): ✅
|
|
||||||
- ✅ Phase 1: Database Foundation
|
|
||||||
- ✅ Phase 2: CSS Architecture
|
|
||||||
- ✅ Phase 3: Smart Preview Data
|
|
||||||
- ✅ Phase 4: Theme LiveView (Basic)
|
|
||||||
- ✅ Phase 5: Preview Pages
|
|
||||||
- ✅ Phase 6: Customization Options
|
|
||||||
- ✅ Phase 6.5: Fix Theme Visual Application
|
|
||||||
- ✅ Phase 7: File Uploads (Logo & Header)
|
|
||||||
- ✅ Phase 8: Persistence & Polish
|
|
||||||
|
|
||||||
### Current Phase: Phase 9 - Storefront Integration 🚧
|
|
||||||
|
|
||||||
**See detailed Phase 9 plan at:** `.claude/plans/snuggly-forging-cat.md`
|
|
||||||
|
|
||||||
**Phase 9 Goal:** Apply the theme system to the actual public-facing storefront
|
|
||||||
|
|
||||||
**Key deliverables:**
|
|
||||||
- Public storefront routes (`/`, `/products/:slug`, `/collections`, `/cart`, etc.)
|
|
||||||
- Storefront LiveViews using real/mock data
|
|
||||||
- LoadTheme plug to inject theme settings and CSS
|
|
||||||
- CSS cache integration for performance
|
|
||||||
- Navigation between admin theme editor and public shop
|
|
||||||
- All 7 pages working on storefront (home, collection, product, cart, about, contact, error)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Phases (Details)
|
|
||||||
|
|
||||||
### Phase 1: Database Foundation ✅
|
|
||||||
**Goal:** Set up Settings and Media models for site-wide configuration
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create migrations for `settings` and `images` tables
|
|
||||||
2. Run migrations
|
|
||||||
3. Create `lib/simpleshop_theme/settings/setting.ex` schema
|
|
||||||
4. Create `lib/simpleshop_theme/settings/theme_settings.ex` embedded schema
|
|
||||||
5. Create `lib/simpleshop_theme/settings.ex` context with:
|
|
||||||
- `get_setting/2` - Get setting by key with default
|
|
||||||
- `put_setting/3` - Set a setting value
|
|
||||||
- `get_theme_settings/0` - Get theme settings as struct
|
|
||||||
- `update_theme_settings/1` - Update theme settings
|
|
||||||
6. Create `lib/simpleshop_theme/media/image.ex` schema
|
|
||||||
7. Create `lib/simpleshop_theme/media.ex` context with:
|
|
||||||
- `upload_image/2` - Upload image with type
|
|
||||||
- `get_image/1` - Get image by ID
|
|
||||||
- `delete_image/1` - Delete image
|
|
||||||
8. Create `lib/simpleshop_theme/theme/presets.ex` with all 9 preset definitions
|
|
||||||
9. Create seed data in `priv/repo/seeds.exs` for default theme settings
|
|
||||||
10. Write tests:
|
|
||||||
- `test/simpleshop_theme/settings_test.exs`
|
|
||||||
- `test/simpleshop_theme/media_test.exs`
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `priv/repo/migrations/*_create_settings.exs`
|
|
||||||
- `priv/repo/migrations/*_create_images.exs`
|
|
||||||
- `lib/simpleshop_theme/settings/setting.ex`
|
|
||||||
- `lib/simpleshop_theme/settings/theme_settings.ex`
|
|
||||||
- `lib/simpleshop_theme/settings.ex`
|
|
||||||
- `lib/simpleshop_theme/media/image.ex`
|
|
||||||
- `lib/simpleshop_theme/media.ex`
|
|
||||||
- `lib/simpleshop_theme/theme/presets.ex`
|
|
||||||
- `test/simpleshop_theme/settings_test.exs`
|
|
||||||
- `test/simpleshop_theme/media_test.exs`
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `priv/repo/seeds.exs` - Add default theme settings
|
|
||||||
|
|
||||||
**Git commit:** `feat: add Settings and Media contexts with theme settings schema`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test`
|
|
||||||
- Run `iex -S mix` and test Settings API
|
|
||||||
- Run seeds: `mix run priv/repo/seeds.exs`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 2: CSS Architecture ✓
|
|
||||||
**Goal:** Set up CSS generation system using Phoenix/Tailwind best practices
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create `priv/static/css/theme-primitives.css` with primitive CSS variables from demo
|
|
||||||
2. Create `priv/static/css/theme-semantic.css` with semantic aliases
|
|
||||||
3. Update `assets/css/app.css` to import theme CSS files
|
|
||||||
4. Create `lib/simpleshop_theme/theme/css_generator.ex` module:
|
|
||||||
- `generate/1` - Takes ThemeSettings, returns CSS custom properties string
|
|
||||||
- Helper functions for each theme token (mood, typography, shape, density)
|
|
||||||
- `hex_to_hsl/1` for accent color conversion
|
|
||||||
- Use Tailwind-friendly approach (CSS variables that Tailwind can reference)
|
|
||||||
5. Create `lib/simpleshop_theme/theme/css_cache.ex` GenServer with ETS table
|
|
||||||
6. Add `CSSCache` to supervision tree in `lib/simpleshop_theme/application.ex`
|
|
||||||
7. Write tests:
|
|
||||||
- `test/simpleshop_theme/theme/css_generator_test.exs`
|
|
||||||
- `test/simpleshop_theme/theme/presets_test.exs`
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `priv/static/css/theme-primitives.css`
|
|
||||||
- `priv/static/css/theme-semantic.css`
|
|
||||||
- `lib/simpleshop_theme/theme/css_generator.ex`
|
|
||||||
- `lib/simpleshop_theme/theme/css_cache.ex`
|
|
||||||
- `test/simpleshop_theme/theme/css_generator_test.exs`
|
|
||||||
- `test/simpleshop_theme/theme/presets_test.exs`
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `assets/css/app.css`
|
|
||||||
- `lib/simpleshop_theme/application.ex`
|
|
||||||
|
|
||||||
**Git commit:** `feat: add CSS generation system with custom properties and ETS cache`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test test/simpleshop_theme/theme/`
|
|
||||||
- Generate CSS for each preset in IEx, verify output
|
|
||||||
- Check CSS variables work with Tailwind
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 3: Smart Preview Data ✓
|
|
||||||
**Goal:** Create preview data system that uses real data when available, falls back to mock
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create `lib/simpleshop_theme/theme/preview_data.ex` with:
|
|
||||||
- `products/0` - Real products if exist, else mock products
|
|
||||||
- `cart_items/0` - Mock cart contents
|
|
||||||
- `testimonials/0` - Mock testimonials for homepage
|
|
||||||
- `categories/0` - Mock product categories
|
|
||||||
- `has_real_products?/0` - Check if shop has products
|
|
||||||
- Mock data uses placeholder images from `https://placehold.co/`
|
|
||||||
2. Write tests:
|
|
||||||
- `test/simpleshop_theme/theme/preview_data_test.exs`
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme/theme/preview_data.ex`
|
|
||||||
- `test/simpleshop_theme/theme/preview_data_test.exs`
|
|
||||||
|
|
||||||
**Git commit:** `feat: add smart preview data system with mock fallback`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test test/simpleshop_theme/theme/preview_data_test.exs`
|
|
||||||
- Call functions in IEx, verify data structure
|
|
||||||
- Verify falls back to mock when no products exist
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 4: Theme LiveView (Basic) ✓
|
|
||||||
**Goal:** Get basic theme editor working with preset switching
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create `lib/simpleshop_theme_web/live/theme_live/index.ex`:
|
|
||||||
- Mount: Load theme settings from Settings context
|
|
||||||
- Assign: `:theme_settings`, `:preview_page`, `:generated_css`
|
|
||||||
- Event handlers: `apply_preset`, `change_preview_page`, `save_theme`
|
|
||||||
2. Create `lib/simpleshop_theme_web/live/theme_live/index.html.heex`:
|
|
||||||
- Two-column layout (controls sidebar + preview area)
|
|
||||||
- Render preset buttons using Tailwind/daisyUI components
|
|
||||||
- Render preview frame with generated CSS in `<style>` tag
|
|
||||||
- Use Phoenix.Component for reusable pieces
|
|
||||||
3. Add route to `lib/simpleshop_theme_web/router.ex`:
|
|
||||||
- `live "/admin/theme", ThemeLive.Index` (authenticated)
|
|
||||||
4. Add "Theme" link to user menu/navigation
|
|
||||||
5. Write tests:
|
|
||||||
- `test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex`
|
|
||||||
- `test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/router.ex`
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts.ex` (add nav link)
|
|
||||||
|
|
||||||
**Git commit:** `feat: add Theme LiveView with preset switching`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
- Visit `/admin/theme`, click presets, verify CSS changes
|
|
||||||
- Test authentication requirement
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 5: Preview Pages ✓
|
|
||||||
**Goal:** Create all 7 preview page templates
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create preview page templates by porting HTML from `theme-demo-v28.html`:
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/home.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/collection.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/pdp.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/cart.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/about.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/contact.html.heex`
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/preview_pages/error.html.heex`
|
|
||||||
2. Port CSS from demo to theme CSS files (primitives, semantic)
|
|
||||||
3. Wire up mock data in templates
|
|
||||||
4. Implement page switcher buttons in preview frame
|
|
||||||
5. Add data attributes system (`data-mood`, `data-typography`, etc.) to preview container
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- 7 preview page `.html.heex` files in `preview_pages/` directory
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_studio_live/index.html.heex` - Add page switcher
|
|
||||||
- CSS files - Port all styles from demo
|
|
||||||
|
|
||||||
**Git commit:** `feat: add preview page templates with theme styling`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
- Switch between all 7 pages, verify they render correctly
|
|
||||||
- Verify preview data shows (mock or real)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6: Customization Options ✓
|
|
||||||
**Goal:** Add all customization controls beyond presets
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create option group components in `index.html.heex`:
|
|
||||||
- Mood selector (4 options: neutral, warm, cool, dark)
|
|
||||||
- Typography selector (7 options: clean, editorial, modern, classic, friendly, minimal, impulse)
|
|
||||||
- Shape selector (4 options: sharp, soft, round, pill)
|
|
||||||
- Density selector (3 options: spacious, balanced, compact)
|
|
||||||
- Grid columns selector (3 options: 2, 3, 4)
|
|
||||||
- Header layout selector (3 options: standard, centered, minimal)
|
|
||||||
2. Add color pickers:
|
|
||||||
- Accent color (with live preview)
|
|
||||||
- Secondary accent color
|
|
||||||
- Sale color
|
|
||||||
3. Add advanced options accordion:
|
|
||||||
- Font size (small, medium, large)
|
|
||||||
- Heading weight (light, regular, bold)
|
|
||||||
- Layout width (contained, wide, full)
|
|
||||||
- Button style (filled, outline, soft)
|
|
||||||
- Card shadow (none, subtle, pronounced)
|
|
||||||
- Product text align (left, center)
|
|
||||||
- Image aspect ratio (square, portrait, landscape)
|
|
||||||
4. Add feature toggles:
|
|
||||||
- Announcement bar
|
|
||||||
- Sticky header
|
|
||||||
- Hover image
|
|
||||||
- Quick add button
|
|
||||||
- Show prices
|
|
||||||
- PDP trust badges
|
|
||||||
- PDP reviews
|
|
||||||
- PDP related products
|
|
||||||
5. Implement `update_setting` event handler in LiveView
|
|
||||||
6. Wire all controls to update theme settings and regenerate CSS
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Add `update_setting` handler
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add all controls
|
|
||||||
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Add tests for customization
|
|
||||||
|
|
||||||
**Git commit:** `feat: add full customization controls with real-time preview`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test test/simpleshop_theme_web/live/theme_live_test.exs`
|
|
||||||
- Change each option, verify preview updates instantly
|
|
||||||
- Test all toggles work correctly
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6.5: Fix Theme Visual Application 🔧
|
|
||||||
**Goal:** Fix the theme controls so they actually visually affect the preview pages
|
|
||||||
|
|
||||||
**Problem:** Theme controls update the database but don't visually change the preview. The Phoenix implementation is missing the data-attribute-based CSS system that the demo uses.
|
|
||||||
|
|
||||||
**Root Cause Analysis:**
|
|
||||||
The demo HTML uses data attributes (`data-mood="warm"`, `data-typography="editorial"`, etc.) on the `.preview-frame` element, and the CSS uses attribute selectors like `.preview-frame[data-mood="warm"] { ... }` to apply styles. The Phoenix LiveView implementation is missing these data attributes entirely.
|
|
||||||
|
|
||||||
**Discovered Issues:**
|
|
||||||
|
|
||||||
| Issue | Status | Root Cause |
|
|
||||||
|-------|--------|------------|
|
|
||||||
| Fonts don't change | ❌ Broken | 1. Google Fonts not loaded<br>2. No data attributes on preview-frame |
|
|
||||||
| Mood/colors don't work (except dark) | ❌ Broken | No data attributes on preview-frame |
|
|
||||||
| Shape doesn't work | ❌ Broken | No data attributes on preview-frame |
|
|
||||||
| Density doesn't work | ❌ Broken | No data attributes on preview-frame |
|
|
||||||
| Grid columns 4 shows as 2 | ❌ Broken | Tailwind can't process dynamic class interpolation |
|
|
||||||
| Accent color doesn't update | ⚠️ Partial | CSS generated but may be overridden |
|
|
||||||
| Header layout has no component | ❌ Missing | No header component in preview pages |
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
|
|
||||||
1. **Add Google Fonts to root layout** (`lib/simpleshop_theme_web/components/layouts/root.html.heex`):
|
|
||||||
```heex
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=Libre+Baskerville:wght@400;700&family=Nunito:wght@400;600;700&family=Nunito+Sans:opsz,wght@6..12,300;6..12,400;6..12,500;6..12,600&family=Outfit:wght@300;400;500;600&family=Source+Sans+3:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add data attributes to preview frame** (`lib/simpleshop_theme_web/live/theme_live/index.html.heex:205`):
|
|
||||||
```heex
|
|
||||||
<div class="preview-frame bg-white overflow-auto"
|
|
||||||
data-mood={@theme_settings.mood}
|
|
||||||
data-typography={@theme_settings.typography}
|
|
||||||
data-shape={@theme_settings.shape}
|
|
||||||
data-density={@theme_settings.density}
|
|
||||||
data-grid={@theme_settings.grid_columns}
|
|
||||||
data-header={@theme_settings.header_layout}
|
|
||||||
style="min-height: 600px; max-height: calc(100vh - 200px);">
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Copy CSS from demo file** (`theme-demo-v28.html` lines 81-217) to create `priv/static/css/theme-layer2-attributes.css`:
|
|
||||||
- All the `.preview-frame[data-mood="..."]` rules
|
|
||||||
- All the `.preview-frame[data-typography="..."]` rules
|
|
||||||
- All the `.preview-frame[data-shape="..."]` rules
|
|
||||||
- All the `.preview-frame[data-density="..."]` rules
|
|
||||||
- All the `.preview-frame[data-grid="..."]` rules
|
|
||||||
- All the `.preview-frame[data-header="..."]` rules
|
|
||||||
|
|
||||||
4. **Import the new CSS file** in `assets/css/app.css`:
|
|
||||||
```css
|
|
||||||
@import "../priv/static/css/theme-primitives.css";
|
|
||||||
@import "../priv/static/css/theme-layer2-attributes.css";
|
|
||||||
@import "../priv/static/css/theme-semantic.css";
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Fix grid columns dynamic classes** - Replace string interpolation with conditional logic in all preview pages:
|
|
||||||
```heex
|
|
||||||
<!-- BEFORE (broken): -->
|
|
||||||
<div class={"grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-#{@theme_settings.grid_columns}"}>
|
|
||||||
|
|
||||||
<!-- AFTER (working): -->
|
|
||||||
<div class={["grid gap-6 grid-cols-1 sm:grid-cols-2",
|
|
||||||
case @theme_settings.grid_columns do
|
|
||||||
"2" -> "lg:grid-cols-2"
|
|
||||||
"3" -> "lg:grid-cols-3"
|
|
||||||
"4" -> "lg:grid-cols-4"
|
|
||||||
_ -> "lg:grid-cols-3"
|
|
||||||
end]}>
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Add header component** to preview pages - Create a header section that responds to `data-header` attribute:
|
|
||||||
```heex
|
|
||||||
<header class="theme-header" style="background-color: var(--t-surface-raised); border-bottom: 1px solid var(--t-border-subtle);">
|
|
||||||
<!-- Header content that adapts based on data-header="standard|centered|minimal" -->
|
|
||||||
</header>
|
|
||||||
```
|
|
||||||
|
|
||||||
7. **Update CSSGenerator** to work alongside attribute-based CSS:
|
|
||||||
- Keep generating accent color HSL variables (these are used by all themes)
|
|
||||||
- Remove mood/typography/shape/density generation (now handled by attribute CSS)
|
|
||||||
- Focus on dynamic values like accent_color, secondary_accent_color, sale_color
|
|
||||||
|
|
||||||
8. **Test all controls** in browser to verify visual changes work
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `priv/static/css/theme-layer2-attributes.css` - Attribute-based theme CSS from demo
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts/root.html.heex` - Add Google Fonts
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Add data attributes to preview-frame
|
|
||||||
- `assets/css/app.css` - Import new CSS file
|
|
||||||
- `lib/simpleshop_theme/theme/css_generator.ex` - Simplify to focus on color variables
|
|
||||||
- All preview page files - Fix grid columns dynamic classes
|
|
||||||
- All preview page files - Add header component
|
|
||||||
|
|
||||||
**Git commit:** `fix: add data attributes and attribute-based CSS to make theme controls work visually`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Change mood - verify background colors change
|
|
||||||
- Change typography - verify fonts change
|
|
||||||
- Change shape - verify button/card border radius changes
|
|
||||||
- Change density - verify spacing changes
|
|
||||||
- Change grid columns - verify all 3 options (2, 3, 4) work correctly
|
|
||||||
- Change accent color - verify primary buttons change color
|
|
||||||
- Change header layout - verify header structure changes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 7: File Uploads (Logo & Header) ✓
|
|
||||||
**Goal:** Enable logo and header image uploads with database storage
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Add dependency to `mix.exs`: `{:image, "~> 0.54"}` for thumbnail generation
|
|
||||||
2. Implement SVG detection and text storage in `image.ex` changeset
|
|
||||||
3. Add Media context functions for logo/header:
|
|
||||||
- Update `upload_image/2` to handle thumbnails
|
|
||||||
- Add `get_logo/0`, `get_header/0` convenience functions
|
|
||||||
4. Create `lib/simpleshop_theme_web/controllers/image_controller.ex`:
|
|
||||||
- `show/2` - Serve image BLOB with proper content-type and caching
|
|
||||||
- `recolored_svg/2` - Serve recolored SVG
|
|
||||||
5. Create `lib/simpleshop_theme/media/svg_recolorer.ex` for SVG color manipulation
|
|
||||||
6. Add routes for image serving in router
|
|
||||||
7. Configure LiveView uploads in `index.ex`:
|
|
||||||
- `:logo_upload` - Accept PNG, JPG, WebP, SVG (max 2MB)
|
|
||||||
- `:header_upload` - Accept PNG, JPG, WebP (max 5MB)
|
|
||||||
8. Add upload controls to branding section in template using daisyUI components:
|
|
||||||
- Logo mode selector (text-only, logo+text, logo-only, header-image, logo+header)
|
|
||||||
- Logo upload with preview
|
|
||||||
- Logo size slider (24-120px)
|
|
||||||
- SVG recolor toggle + color picker
|
|
||||||
- Header image upload with preview
|
|
||||||
- Header zoom slider (100-200%)
|
|
||||||
- Header position sliders (X and Y: 0-100%)
|
|
||||||
9. Implement upload event handlers
|
|
||||||
10. Display uploaded images in preview
|
|
||||||
11. Write tests:
|
|
||||||
- `test/simpleshop_theme_web/controllers/image_controller_test.exs`
|
|
||||||
- `test/simpleshop_theme/media/svg_recolorer_test.exs`
|
|
||||||
|
|
||||||
**Files to create:**
|
|
||||||
- `lib/simpleshop_theme_web/controllers/image_controller.ex`
|
|
||||||
- `lib/simpleshop_theme/media/svg_recolorer.ex`
|
|
||||||
- `test/simpleshop_theme_web/controllers/image_controller_test.exs`
|
|
||||||
- `test/simpleshop_theme/media/svg_recolorer_test.exs`
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme/media.ex` - Add logo/header helpers
|
|
||||||
- `lib/simpleshop_theme/media/image.ex` - SVG handling
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Upload config and handlers
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - Branding section UI
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Image serving routes
|
|
||||||
- `mix.exs` - Add `:image` dependency
|
|
||||||
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Add upload tests
|
|
||||||
|
|
||||||
**Git commit:** `feat: add image uploads with BLOB storage and SVG recoloring`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test`
|
|
||||||
- Upload logo and header images
|
|
||||||
- Verify BLOB storage in database
|
|
||||||
- Test SVG recoloring works
|
|
||||||
- Verify images display in preview
|
|
||||||
- Test image serving with proper cache headers
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 8: Persistence & Polish ✓
|
|
||||||
**Goal:** Complete save functionality and UX polish
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Implement "Save Theme" button and handler:
|
|
||||||
- Show loading state during save
|
|
||||||
- Flash success/error message
|
|
||||||
- Invalidate CSS cache
|
|
||||||
2. Add "Reset to Preset" functionality
|
|
||||||
3. Implement unsaved changes warning
|
|
||||||
4. Add keyboard shortcuts (Cmd+S to save)
|
|
||||||
5. Debounce color picker inputs (300ms) to avoid excessive renders
|
|
||||||
6. Add loading skeletons for preview
|
|
||||||
7. Handle edge cases:
|
|
||||||
- File too large error
|
|
||||||
- Invalid file type error
|
|
||||||
- Network errors
|
|
||||||
8. Add client-side validation
|
|
||||||
9. Improve mobile responsiveness of editor
|
|
||||||
10. Add tooltips/help text for complex options
|
|
||||||
|
|
||||||
**Files to modify:**
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Save logic, debouncing
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.html.heex` - UI polish
|
|
||||||
- JavaScript hooks for keyboard shortcuts (if needed)
|
|
||||||
- `test/simpleshop_theme_web/live/theme_live_test.exs` - Test persistence
|
|
||||||
|
|
||||||
**Git commit:** `feat: add theme persistence with UX polish and validation`
|
|
||||||
|
|
||||||
**Validation:**
|
|
||||||
- Run tests: `mix test`
|
|
||||||
- Save theme, reload page, verify settings persist
|
|
||||||
- Test unsaved changes warning
|
|
||||||
- Test keyboard shortcuts
|
|
||||||
- Verify error handling for edge cases
|
|
||||||
- Test mobile responsiveness
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 9: Storefront Integration 🚧
|
|
||||||
**Goal:** Apply theme to actual public-facing shop
|
|
||||||
|
|
||||||
**Status:** Ready to implement - See detailed plan in `.claude/plans/snuggly-forging-cat.md`
|
|
||||||
|
|
||||||
**High-level overview:**
|
|
||||||
1. Extract shared components from preview pages (header, footer, product card, etc.)
|
|
||||||
2. Create LoadTheme plug to inject theme settings and CSS into all public requests
|
|
||||||
3. Create dedicated shop layout with `.shop-root` class and data attributes
|
|
||||||
4. Build storefront LiveViews for all pages:
|
|
||||||
- Home (`/`)
|
|
||||||
- Collection (`/collections/:slug`)
|
|
||||||
- Product Detail (`/products/:slug`)
|
|
||||||
- Cart (`/cart`)
|
|
||||||
- About (`/about`)
|
|
||||||
- Contact (`/contact`)
|
|
||||||
- Error (404 page)
|
|
||||||
5. Wire up CSS cache invalidation when theme is saved
|
|
||||||
6. Add cache warming on application startup
|
|
||||||
7. Add navigation links between admin and storefront
|
|
||||||
8. Comprehensive testing and polish
|
|
||||||
|
|
||||||
**Key files to create (23 files):**
|
|
||||||
- 5 shared component modules in `components/shop/`
|
|
||||||
- 1 LoadTheme plug
|
|
||||||
- 1 shop layout template
|
|
||||||
- 6 storefront LiveView modules + templates
|
|
||||||
- 1 error page template
|
|
||||||
- 6 test files
|
|
||||||
|
|
||||||
**Key files to modify (9 files):**
|
|
||||||
- Router (add public routes and plug)
|
|
||||||
- Settings context (add cache invalidation)
|
|
||||||
- Application (add cache warming)
|
|
||||||
- Theme editor (add "View Shop" link)
|
|
||||||
- Preview pages (use shared components)
|
|
||||||
|
|
||||||
**Detailed implementation plan:** See `.claude/plans/snuggly-forging-cat.md` for 12-step implementation guide with code examples, validation steps, and architectural decisions.
|
|
||||||
|
|
||||||
**Git commit strategy:** Commit after each major step (components extraction, plug creation, each LiveView, etc.)
|
|
||||||
|
|
||||||
**Validation approach:**
|
|
||||||
- Test theme editor still works after component extraction
|
|
||||||
- Test each storefront page individually as built
|
|
||||||
- Verify CSS cache invalidation flow
|
|
||||||
- Test navigation between admin and storefront
|
|
||||||
- Comprehensive testing in Step 12
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Implementation Details
|
|
||||||
|
|
||||||
### CSS Generation Example
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# lib/simpleshop_theme/theme/css_generator.ex
|
|
||||||
defmodule SimpleshopTheme.Theme.CSSGenerator do
|
|
||||||
alias SimpleshopTheme.Settings.ThemeSettings
|
|
||||||
|
|
||||||
def generate(%ThemeSettings{} = settings) do
|
|
||||||
"""
|
|
||||||
.preview-frame, .shop-root {
|
|
||||||
#{generate_accent(settings.accent_color)}
|
|
||||||
#{generate_mood(settings.mood)}
|
|
||||||
#{generate_typography(settings.typography)}
|
|
||||||
#{generate_shape(settings.shape)}
|
|
||||||
#{generate_density(settings.density)}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp generate_accent(hex) do
|
|
||||||
{h, s, l} = hex_to_hsl(hex)
|
|
||||||
"""
|
|
||||||
--t-accent-h: #{h};
|
|
||||||
--t-accent-s: #{s}%;
|
|
||||||
--t-accent-l: #{l}%;
|
|
||||||
--t-accent: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp hex_to_hsl("#" <> hex), do: hex_to_hsl(hex)
|
|
||||||
defp hex_to_hsl(hex) when byte_size(hex) == 6 do
|
|
||||||
{r, ""} = Integer.parse(String.slice(hex, 0..1), 16)
|
|
||||||
{g, ""} = Integer.parse(String.slice(hex, 2..3), 16)
|
|
||||||
{b, ""} = Integer.parse(String.slice(hex, 4..5), 16)
|
|
||||||
|
|
||||||
r = r / 255
|
|
||||||
g = g / 255
|
|
||||||
b = b / 255
|
|
||||||
|
|
||||||
max = Enum.max([r, g, b])
|
|
||||||
min = Enum.min([r, g, b])
|
|
||||||
|
|
||||||
l = (max + min) / 2
|
|
||||||
|
|
||||||
if max == min do
|
|
||||||
{0, 0, round(l * 100)}
|
|
||||||
else
|
|
||||||
d = max - min
|
|
||||||
s = if l > 0.5, do: d / (2 - max - min), else: d / (max + min)
|
|
||||||
|
|
||||||
h = cond do
|
|
||||||
max == r -> (g - b) / d + (if g < b, do: 6, else: 0)
|
|
||||||
max == g -> (b - r) / d + 2
|
|
||||||
max == b -> (r - g) / d + 4
|
|
||||||
end
|
|
||||||
|
|
||||||
{round(h * 60), round(s * 100), round(l * 100)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp generate_mood("neutral"), do: """
|
|
||||||
--t-surface-base: #ffffff;
|
|
||||||
--t-surface-raised: #ffffff;
|
|
||||||
--t-text-primary: #171717;
|
|
||||||
--t-border-default: #e5e5e5;
|
|
||||||
"""
|
|
||||||
|
|
||||||
defp generate_mood("warm"), do: """
|
|
||||||
--t-surface-base: #fdf8f3;
|
|
||||||
--t-surface-raised: #fffcf8;
|
|
||||||
--t-text-primary: #1c1917;
|
|
||||||
--t-border-default: #e7e0d8;
|
|
||||||
"""
|
|
||||||
|
|
||||||
defp generate_mood("cool"), do: """
|
|
||||||
--t-surface-base: #f4f7fb;
|
|
||||||
--t-surface-raised: #f8fafc;
|
|
||||||
--t-text-primary: #0f172a;
|
|
||||||
--t-border-default: #d4dce8;
|
|
||||||
"""
|
|
||||||
|
|
||||||
defp generate_mood("dark"), do: """
|
|
||||||
--t-surface-base: #0a0a0a;
|
|
||||||
--t-surface-raised: #171717;
|
|
||||||
--t-text-primary: #fafafa;
|
|
||||||
--t-border-default: #262626;
|
|
||||||
--p-shadow-strength: 0.25;
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Similar for typography, shape, density...
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### LiveView Upload Configuration
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# In mount/3
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> allow_upload(:logo_upload,
|
|
||||||
accept: ~w(.png .jpg .jpeg .webp .svg),
|
|
||||||
max_entries: 1,
|
|
||||||
max_file_size: 2_000_000,
|
|
||||||
auto_upload: true
|
|
||||||
)
|
|
||||||
|> allow_upload(:header_upload,
|
|
||||||
accept: ~w(.png .jpg .jpeg .webp),
|
|
||||||
max_entries: 1,
|
|
||||||
max_file_size: 5_000_000,
|
|
||||||
auto_upload: true
|
|
||||||
)
|
|
||||||
|
|
||||||
# Handle upload completion
|
|
||||||
def handle_event("logo_uploaded", _params, socket) do
|
|
||||||
uploaded_files =
|
|
||||||
consume_uploaded_entries(socket, :logo_upload, fn %{path: path}, entry ->
|
|
||||||
file_binary = File.read!(path)
|
|
||||||
|
|
||||||
{:ok, image} = Media.upload_image(%{
|
|
||||||
image_type: "logo",
|
|
||||||
filename: entry.client_name,
|
|
||||||
content_type: entry.client_type,
|
|
||||||
file_size: entry.client_size,
|
|
||||||
data: file_binary
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, image}
|
|
||||||
end)
|
|
||||||
|
|
||||||
case uploaded_files do
|
|
||||||
[image | _] ->
|
|
||||||
# Update theme settings to reference this image
|
|
||||||
settings = socket.assigns.theme_settings
|
|
||||||
{:ok, _} = Settings.update_theme_settings(%{settings | logo_image_id: image.id})
|
|
||||||
{:noreply, assign(socket, :logo_image, image)}
|
|
||||||
[] ->
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Image Controller Serving
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
defmodule SimpleshopThemeWeb.ImageController do
|
|
||||||
use SimpleshopThemeWeb, :controller
|
|
||||||
alias SimpleshopTheme.Media
|
|
||||||
|
|
||||||
def show(conn, %{"id" => id}) do
|
|
||||||
case Media.get_image(id) do
|
|
||||||
nil ->
|
|
||||||
send_resp(conn, 404, "Image not found")
|
|
||||||
|
|
||||||
image ->
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type(image.content_type)
|
|
||||||
|> put_resp_header("cache-control", "public, max-age=31536000, immutable")
|
|
||||||
|> put_resp_header("etag", "\"#{image.id}\"")
|
|
||||||
|> send_resp(200, image.data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recolored_svg(conn, %{"id" => id, "color" => color}) do
|
|
||||||
with %{is_svg: true, svg_content: svg} <- Media.get_image(id),
|
|
||||||
clean_color <- String.trim_leading(color, "#"),
|
|
||||||
recolored <- SimpleshopTheme.Media.SVGRecolorer.recolor(svg, "##{clean_color}") do
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type("image/svg+xml")
|
|
||||||
|> put_resp_header("cache-control", "public, max-age=3600")
|
|
||||||
|> send_resp(200, recolored)
|
|
||||||
else
|
|
||||||
_ -> send_resp(conn, 400, "Invalid request")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Dependencies to Add
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# mix.exs
|
|
||||||
{:image, "~> 0.54"} # Thumbnail generation (uses libvips)
|
|
||||||
{:fast_sanitize, "~> 0.2"} # SVG sanitization (security)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Router Updates
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# lib/simpleshop_theme_web/router.ex
|
|
||||||
|
|
||||||
# Theme editor (authenticated)
|
|
||||||
scope "/admin", SimpleshopThemeWeb do
|
|
||||||
pipe_through [:browser, :require_authenticated_user]
|
|
||||||
|
|
||||||
live_session :theme_editor,
|
|
||||||
on_mount: [{SimpleshopThemeWeb.UserAuth, :require_authenticated}] do
|
|
||||||
live "/theme", ThemeLive.Index, :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Image serving (public)
|
|
||||||
scope "/", SimpleshopThemeWeb do
|
|
||||||
pipe_through :browser
|
|
||||||
|
|
||||||
get "/images/:id", ImageController, :show
|
|
||||||
get "/images/:id/recolored/:color", ImageController, :recolored_svg
|
|
||||||
end
|
|
||||||
|
|
||||||
# Public storefront (future - Phase 9)
|
|
||||||
# scope "/", SimpleshopThemeWeb do
|
|
||||||
# pipe_through :browser
|
|
||||||
# live "/", ShopLive.Home, :index
|
|
||||||
# live "/products/:slug", ShopLive.Product, :show
|
|
||||||
# end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Critical Files Reference
|
|
||||||
|
|
||||||
### To Create
|
|
||||||
- `lib/simpleshop_theme/settings.ex` - Settings context
|
|
||||||
- `lib/simpleshop_theme/settings/setting.ex` - Setting schema (key-value)
|
|
||||||
- `lib/simpleshop_theme/settings/theme_settings.ex` - Embedded theme settings
|
|
||||||
- `lib/simpleshop_theme/media.ex` - Media context
|
|
||||||
- `lib/simpleshop_theme/media/image.ex` - Image schema
|
|
||||||
- `lib/simpleshop_theme/media/svg_recolorer.ex` - SVG manipulation
|
|
||||||
- `lib/simpleshop_theme/theme/presets.ex` - 9 preset definitions
|
|
||||||
- `lib/simpleshop_theme/theme/css_generator.ex` - CSS generation
|
|
||||||
- `lib/simpleshop_theme/theme/css_cache.ex` - ETS caching
|
|
||||||
- `lib/simpleshop_theme/theme/preview_data.ex` - Smart preview data
|
|
||||||
- `lib/simpleshop_theme_web/live/theme_live/index.ex` - Main LiveView
|
|
||||||
- `lib/simpleshop_theme_web/controllers/image_controller.ex` - Image serving
|
|
||||||
- `priv/static/css/theme-primitives.css` - CSS Layer 1
|
|
||||||
- `priv/static/css/theme-semantic.css` - CSS Layer 3
|
|
||||||
- Comprehensive test files for all contexts and LiveViews
|
|
||||||
|
|
||||||
### To Modify
|
|
||||||
- `lib/simpleshop_theme/application.ex` - Add CSS cache to supervision tree
|
|
||||||
- `lib/simpleshop_theme_web/router.ex` - Add theme and image routes
|
|
||||||
- `lib/simpleshop_theme_web/components/layouts.ex` - Add theme nav link
|
|
||||||
- `assets/css/app.css` - Import theme CSS
|
|
||||||
- `priv/repo/seeds.exs` - Add default theme settings
|
|
||||||
- `mix.exs` - Add dependencies
|
|
||||||
|
|
||||||
### Reference Files
|
|
||||||
- `SIMPLESHOP_THEME_STUDIO_SPEC.md` - Complete specification
|
|
||||||
- `theme-demo-v28.html` - Working prototype (CSS, HTML structure, JavaScript logic)
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
✅ User can access theme studio at `/admin/theme`
|
|
||||||
✅ All 9 presets work with real-time preview
|
|
||||||
✅ All customization options update preview instantly
|
|
||||||
✅ Logo and header images upload and store as BLOBs
|
|
||||||
✅ SVG recoloring works correctly
|
|
||||||
✅ Preview shows all 7 mock pages
|
|
||||||
✅ Settings persist to database
|
|
||||||
✅ "Save" button works with success feedback
|
|
||||||
✅ Images served efficiently from database
|
|
||||||
✅ CSS cached in ETS for performance
|
|
||||||
✅ Everything self-contained in SQLite file
|
|
||||||
|
|
||||||
## Next Steps After Implementation
|
|
||||||
|
|
||||||
1. **Add Products Context** - So users can add real products
|
|
||||||
2. **Build Storefront** - Public-facing shop pages using saved theme
|
|
||||||
3. **Add Orders/Cart** - E-commerce functionality
|
|
||||||
4. **Multi-admin Support** - Invite additional users to manage shop
|
|
||||||
5. **Custom Domains** - Allow users to point their own domain
|
|
||||||
6. **Theme Export/Import** - Share themes between shops
|
|
||||||
7. **Advanced Features** - Custom CSS, code injection, etc.
|
|
||||||
136
README.md
136
README.md
@ -1,18 +1,132 @@
|
|||||||
# SimpleshopTheme
|
# SimpleshopTheme
|
||||||
|
|
||||||
To start your Phoenix server:
|
A customisable e-commerce theme system built with Phoenix LiveView. Designed for print-on-demand sellers who want beautiful, professional shops without design expertise.
|
||||||
|
|
||||||
* Run `mix setup` to install and setup dependencies
|
## Features
|
||||||
* Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`
|
|
||||||
|
|
||||||
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
|
### Theme Editor (`/admin/theme`)
|
||||||
|
- **8 curated presets** - Gallery, Studio, Boutique, Bold, Playful, Minimal, Night, Classic
|
||||||
|
- **Real-time preview** - See changes instantly across 7 page types
|
||||||
|
- **Comprehensive customisation**:
|
||||||
|
- Mood (Neutral, Warm, Cool, Dark)
|
||||||
|
- Typography (Clean, Editorial, Modern, Classic, Friendly, Minimal)
|
||||||
|
- Shape (Sharp, Soft, Round, Pill)
|
||||||
|
- Density (Compact, Balanced, Spacious)
|
||||||
|
- Accent colours with automatic contrast
|
||||||
|
- Layout options (grid columns, header style, width)
|
||||||
|
- Feature toggles (announcement bar, sticky header, etc.)
|
||||||
|
- **Logo upload** with optional SVG recolouring
|
||||||
|
- **Header image** with zoom and position controls
|
||||||
|
|
||||||
Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
|
### Public Storefront
|
||||||
|
- Home page with hero, categories, featured products, testimonials
|
||||||
|
- Products listing with grid layout
|
||||||
|
- Product detail pages with image gallery
|
||||||
|
- Shopping cart page
|
||||||
|
- About and Contact pages
|
||||||
|
- Themed 404/500 error pages
|
||||||
|
|
||||||
## Learn more
|
### Technical Highlights
|
||||||
|
- CSS custom properties for instant theme switching
|
||||||
|
- Three-layer CSS architecture (primitives → attributes → semantic)
|
||||||
|
- ETS-based CSS caching for performance
|
||||||
|
- SQLite database with BLOB storage for images
|
||||||
|
- Shared PageTemplates between preview and live shop
|
||||||
|
|
||||||
* Official website: https://www.phoenixframework.org/
|
## Getting Started
|
||||||
* Guides: https://hexdocs.pm/phoenix/overview.html
|
|
||||||
* Docs: https://hexdocs.pm/phoenix
|
### Prerequisites
|
||||||
* Forum: https://elixirforum.com/c/phoenix-forum
|
- Elixir 1.17+
|
||||||
* Source: https://github.com/phoenixframework/phoenix
|
- Erlang 27+
|
||||||
|
- Node.js 20+ (for assets)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone <repo-url>
|
||||||
|
cd simpleshop_theme
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
mix setup
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit:
|
||||||
|
- **Shop**: http://localhost:4000
|
||||||
|
- **Theme Editor**: http://localhost:4000/admin/theme
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mix test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
lib/
|
||||||
|
├── simpleshop_theme/ # Core business logic
|
||||||
|
│ ├── settings.ex # Theme settings context
|
||||||
|
│ ├── settings/
|
||||||
|
│ │ └── theme_settings.ex # Theme settings schema
|
||||||
|
│ ├── media.ex # Image upload handling
|
||||||
|
│ └── theme/
|
||||||
|
│ ├── css_generator.ex # Generates CSS from settings
|
||||||
|
│ ├── css_cache.ex # ETS cache for generated CSS
|
||||||
|
│ ├── presets.ex # 8 theme presets
|
||||||
|
│ └── preview_data.ex # Mock data for previews
|
||||||
|
│
|
||||||
|
├── simpleshop_theme_web/ # Web layer
|
||||||
|
│ ├── components/
|
||||||
|
│ │ ├── layouts/ # App and shop layouts
|
||||||
|
│ │ ├── page_templates/ # Shared page templates (*.heex)
|
||||||
|
│ │ ├── page_templates.ex # PageTemplates module
|
||||||
|
│ │ └── shop_components.ex # Reusable shop UI components
|
||||||
|
│ ├── live/
|
||||||
|
│ │ ├── theme_live/ # Theme editor LiveView
|
||||||
|
│ │ └── shop_live/ # Public shop LiveViews
|
||||||
|
│ └── controllers/
|
||||||
|
│ └── error_html.ex # Themed error pages
|
||||||
|
|
||||||
|
assets/css/
|
||||||
|
├── app.css # Main stylesheet
|
||||||
|
├── theme-layer1-primitives.css # Design tokens
|
||||||
|
├── theme-layer2-attributes.css # Theme-specific values
|
||||||
|
└── theme-layer3-semantic.css # Component styles
|
||||||
|
```
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
| Path | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `/` | Shop home page |
|
||||||
|
| `/products` | Products listing |
|
||||||
|
| `/products/:id` | Product detail page |
|
||||||
|
| `/cart` | Shopping cart |
|
||||||
|
| `/about` | About page |
|
||||||
|
| `/contact` | Contact page |
|
||||||
|
| `/admin/theme` | Theme editor (requires auth) |
|
||||||
|
| `/dev/errors/404` | Preview 404 page (dev only) |
|
||||||
|
| `/dev/errors/500` | Preview 500 page (dev only) |
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [ROADMAP.md](ROADMAP.md) - Future features and improvements
|
||||||
|
- [docs/spec.md](docs/spec.md) - Theme system specification
|
||||||
|
- [docs/plans/](docs/plans/) - Feature plans
|
||||||
|
- [docs/research/](docs/research/) - Design research and guidelines
|
||||||
|
|
||||||
|
## Design Philosophy
|
||||||
|
|
||||||
|
1. **"One theme, infinite variations"** - Rather than multiple themes, one solid foundation with curated customisation
|
||||||
|
2. **Constrained creativity** - Limit choices to prevent poor design outcomes
|
||||||
|
3. **No professional photography required** - Works with product mockups
|
||||||
|
4. **Mobile-first** - All features work on touch devices
|
||||||
|
5. **Ethical design** - No dark patterns or fake urgency
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
Summary of Improvements
|
|
||||||
|
|
||||||
I've successfully implemented all four requested improvements to make the SimpleShop Theme Studio more aligned with the demo file and UK-centric:
|
|
||||||
1. ✅ Made all links and buttons functional
|
|
||||||
|
|
||||||
Updated components:
|
|
||||||
|
|
||||||
Header navigation (preview_pages.ex): Home, Shop, About, and Contact links now trigger page navigation
|
|
||||||
Product cards (home.html.heex, collection.html.heex): Clicking any product card navigates to the PDP page
|
|
||||||
Footer links (preview_pages.ex): All footer navigation links are now functional
|
|
||||||
CTA buttons (home.html.heex): "Shop the collection" and "View all products" buttons navigate to collection page
|
|
||||||
About link (home.html.heex): "Learn more about the studio" link navigates to about page
|
|
||||||
|
|
||||||
All links now use phx-click="change_preview_page" with phx-value-page to switch between the 7 preview pages seamlessly.
|
|
||||||
2. ✅ Implemented floating cart drawer/sidebar
|
|
||||||
|
|
||||||
New component added (preview_pages.ex:229-320):
|
|
||||||
|
|
||||||
Created cart_drawer/1 function component matching the demo's design exactly
|
|
||||||
Includes cart header with "Your basket" title
|
|
||||||
Displays cart items with images, names, variants, and prices
|
|
||||||
Shows subtotal (£72.00)
|
|
||||||
Checkout button and "View basket" link
|
|
||||||
Close button and overlay with click-to-close functionality
|
|
||||||
Smooth slide-in animation from the right
|
|
||||||
Added to all 7 preview pages (home, collection, pdp, cart, about, contact, error)
|
|
||||||
|
|
||||||
Cart drawer data (preview_data.ex:34-49):
|
|
||||||
|
|
||||||
Added cart_drawer_items/0 function with UK-themed products:
|
|
||||||
Autumn Fern (A4 / Unframed) - £24.00
|
|
||||||
Wild Roses (A3 / Oak frame) - £48.00
|
|
||||||
|
|
||||||
3. ✅ Added cart count badge to cart icon
|
|
||||||
|
|
||||||
Updated header (preview_pages.ex:98-112):
|
|
||||||
|
|
||||||
Added circular badge showing "2" items
|
|
||||||
Badge styled with accent color background
|
|
||||||
Positioned at top-right of cart icon
|
|
||||||
Font size: 11px, white text, rounded pill shape
|
|
||||||
Matches the demo implementation exactly
|
|
||||||
|
|
||||||
Cart button functionality:
|
|
||||||
|
|
||||||
Clicking the cart icon now opens the cart drawer
|
|
||||||
Uses Phoenix.LiveView.JS.add_class to toggle visibility
|
|
||||||
|
|
||||||
4. ✅ Updated all content to UK English and currency
|
|
||||||
|
|
||||||
Language changes:
|
|
||||||
|
|
||||||
"Shopping Cart" → "Your basket"
|
|
||||||
"Add to Cart" → "Add to basket"
|
|
||||||
"Free Shipping" → "Free Delivery"
|
|
||||||
"Shipping" (in fees) → "Delivery"
|
|
||||||
"You May Also Like" → "You might also like"
|
|
||||||
Footer: "Shipping" → "Delivery"
|
|
||||||
|
|
||||||
Currency changes:
|
|
||||||
|
|
||||||
All product prices: $ → £ (across all 7 preview pages)
|
|
||||||
Announcement bar: "$50" → "£40"
|
|
||||||
Free delivery threshold: "$50" → "£40"
|
|
||||||
Cart delivery fee: "$10.00" → "£8.00"
|
|
||||||
Cart tax: "$8.50" (Tax) → "£7.20" (VAT 20%)
|
|
||||||
Cart drawer subtotal: "$72.00" → "£72.00"
|
|
||||||
|
|
||||||
Files modified:
|
|
||||||
|
|
||||||
lib/simpleshop_theme_web/live/theme_live/preview_pages.ex - Header, footer, announcement bar, new cart drawer component
|
|
||||||
lib/simpleshop_theme/theme/preview_data.ex - Added cart drawer items function
|
|
||||||
All 7 preview page files (home, collection, pdp, cart, about, contact, error) - Currency symbols and cart drawer component
|
|
||||||
|
|
||||||
The implementation now closely matches the demo.html file functionality while being fully localized for the UK market with British English tone and £ currency throughout.
|
|
||||||
140
ROADMAP.md
Normal file
140
ROADMAP.md
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
# SimpleshopTheme Roadmap
|
||||||
|
|
||||||
|
This document tracks future improvements, features, and known gaps.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Medium Features
|
||||||
|
|
||||||
|
### Sample Content Refresh ("Wildprint Studio")
|
||||||
|
**Status:** Planned (see `docs/plans/sample-content.md`)
|
||||||
|
**Effort:** Medium
|
||||||
|
|
||||||
|
Replace current preview data with POD-focused content:
|
||||||
|
- 16 products across 5 categories (Art Prints, Apparel, Homewares, Stationery, Accessories)
|
||||||
|
- Nature/botanical primary theme with variety
|
||||||
|
- UK-focused (prices in £)
|
||||||
|
- Printify API integration for automated mockup generation
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Completed (For Reference)
|
||||||
|
|
||||||
|
### 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
|
||||||
Loading…
Reference in New Issue
Block a user