simpleshop_theme/PHASE_9_PLAN.md

924 lines
29 KiB
Markdown

# 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