wire order pages and theme preview to page renderer, remove old templates
Some checks failed
deploy / deploy (push) Has been cancelled

All 14 pages now render through PageRenderer. Theme editor preview
unified from 10 preview_page clauses to one function + page-context
helpers. PageTemplates module and 10 .heex template files deleted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-02-26 19:32:50 +00:00
parent 16ebc29fa9
commit 24ad3b8b60
18 changed files with 79 additions and 743 deletions

View File

@ -458,7 +458,7 @@ See: [plan](docs/plans/shipping-sync.md) for implementation details
See: [docs/plans/analytics-v2.md](docs/plans/analytics-v2.md) for v2 plan See: [docs/plans/analytics-v2.md](docs/plans/analytics-v2.md) for v2 plan
### Page Editor ### Page Editor
**Status:** In progress — Stage 4 of 9 complete, 1284 tests **Status:** In progress — Stage 5 of 9 complete, 1284 tests
Database-driven page builder. Every page is a flat list of blocks stored as JSON — add, remove, reorder, and edit blocks on any page. One generic renderer for all pages (no page-specific render functions). Portable blocks (hero, featured_products, image_text, etc.) work on any page. Page-specific blocks (product_hero, cart_items, etc.) are restricted to their native page. Block data loaders dynamically load data based on which blocks are on the page. ETS-cached page definitions. Mobile-first admin editor with live preview, undo/redo, accessible reordering (no drag-and-drop), inline settings forms, and "reset to defaults". CSS-driven page layout (not renderer-driven). Database-driven page builder. Every page is a flat list of blocks stored as JSON — add, remove, reorder, and edit blocks on any page. One generic renderer for all pages (no page-specific render functions). Portable blocks (hero, featured_products, image_text, etc.) work on any page. Page-specific blocks (product_hero, cart_items, etc.) are restricted to their native page. Block data loaders dynamically load data based on which blocks are on the page. ETS-cached page definitions. Mobile-first admin editor with live preview, undo/redo, accessible reordering (no drag-and-drop), inline settings forms, and "reset to defaults". CSS-driven page layout (not renderer-driven).
@ -467,8 +467,8 @@ Database-driven page builder. Every page is a flat list of blocks stored as JSON
2. ~~Page renderer — generic renderer tested in isolation~~ ✅ (`32f54c7`) 2. ~~Page renderer — generic renderer tested in isolation~~ ✅ (`32f54c7`)
3. ~~Wire simple pages — Home, Content (x4), Contact, Error~~ 3. ~~Wire simple pages — Home, Content (x4), Contact, Error~~
4. ~~Wire shop pages — Collection, PDP, Cart, Search~~ 4. ~~Wire shop pages — Collection, PDP, Cart, Search~~
5. **Next →** Wire order pages + theme preview — CheckoutSuccess, Orders, OrderDetail, theme editor 5. ~~Wire order pages + theme preview — CheckoutSuccess, Orders, OrderDetail, theme editor~~ ✅
6. Admin editor — page list + block management (reorder, add, remove, duplicate, save) 6. **Next →** Admin editor — page list + block management (reorder, add, remove, duplicate, save)
7. Admin editor — inline block settings editing 7. Admin editor — inline block settings editing
8. Live preview — split layout with real-time preview 8. Live preview — split layout with real-time preview
9. Undo/redo + polish — history stacks, keyboard shortcuts, animations 9. Undo/redo + polish — history stacks, keyboard shortcuts, animations

View File

@ -1,6 +1,6 @@
# Page builder plan # Page builder plan
Status: In progress (Stage 4 complete) Status: In progress (Stage 5 complete)
## Context ## Context
@ -651,21 +651,17 @@ Each stage is a commit point. Tests pass, all pages work, nothing is broken. Pic
--- ---
### Stage 5: Wire up order pages + theme preview ### Stage 5: Wire up order pages + theme preview
**Goal:** remaining pages switch over. Theme editor uses PageRenderer. All old page templates are now unused. **Status:** Complete
- [ ] Update `Shop.CheckoutSuccess``Pages.get_page("checkout_success")`, keep PubSub subscription - [x] Update `Shop.CheckoutSuccess``Pages.get_page("checkout_success")`, keep PubSub subscription
- [ ] Update `Shop.Orders``Pages.get_page("orders")` - [x] Update `Shop.Orders``Pages.get_page("orders")`
- [ ] Update `Shop.OrderDetail``Pages.get_page("order_detail")` - [x] Update `Shop.OrderDetail``Pages.get_page("order_detail")`
- [ ] Update theme editor — replace 10 `preview_page/1` clauses with `Pages.get_page(slug)` + `load_block_data/2` + PageRenderer - [x] Update theme editor — unified `preview_page/1` with `Pages.get_page(slug)` + `load_block_data/2` + PageRenderer (10 clauses → 1 + page-context helpers)
- [ ] Verify theme preview still works: page switching, CSS injection, mode: :preview - [x] Removed `PageTemplates` module + 10 `.heex` template files (zero references remain)
- [ ] Remove old page templates (the `.heex` files) if no longer referenced - [x] `layout_assigns/1` already lives in `ShopComponents.Layout` — no move needed
- [ ] Move `layout_assigns/1` and any shared helpers to PageRenderer or a shared module - [x] 1284 tests pass, `mix precommit` clean
**Commit:** `wire order pages and theme preview to page renderer, remove old templates`
**Verify:** `mix test` passes, theme editor preview works for all 10 pages, checkout flow works end to end
--- ---

View File

@ -1,30 +0,0 @@
defmodule BerrypodWeb.PageTemplates do
@moduledoc """
Shared page templates used by both the public shop and theme preview.
These templates accept a `mode` parameter to control navigation behavior:
- `:shop` - Links navigate normally (real shop pages)
- `:preview` - Links send events to parent LiveView (theme editor)
All templates expect these common assigns:
- `theme_settings` - Current theme configuration
- `logo_image` - Logo image struct or nil
- `header_image` - Header image struct or nil
- `mode` - `:shop` or `:preview`
- `cart_items` - List of cart items (can be empty)
- `cart_count` - Number of items in cart
"""
use Phoenix.Component
use BerrypodWeb.ShopComponents
embed_templates "page_templates/*"
def format_order_status("unfulfilled"), do: "Being prepared"
def format_order_status("submitted"), do: "Sent to printer"
def format_order_status("processing"), do: "In production"
def format_order_status("shipped"), do: "On its way"
def format_order_status("delivered"), do: "Delivered"
def format_order_status("failed"), do: "Issue — contact us"
def format_order_status("cancelled"), do: "Cancelled"
def format_order_status(status), do: status
end

View File

@ -1,37 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="cart">
<main id="main-content" class="page-container">
<.page_title text="Your basket" />
<%= if @cart_items == [] do %>
<.cart_empty_state mode={@mode} />
<% else %>
<div class="cart-grid">
<div>
<ul
role="list"
aria-label="Cart items"
class="cart-page-list"
>
<%= for item <- @cart_items do %>
<li>
<.shop_card class="cart-page-card">
<.cart_item_row item={item} size={:default} show_quantity_controls mode={@mode} />
</.shop_card>
</li>
<% end %>
</ul>
</div>
<div>
<.order_summary
subtotal={@cart_page_subtotal}
shipping_estimate={assigns[:shipping_estimate]}
country_code={assigns[:country_code] || "GB"}
available_countries={assigns[:available_countries] || []}
mode={@mode}
/>
</div>
</div>
<% end %>
</main>
</.shop_layout>

View File

@ -1,134 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="checkout">
<main id="main-content" class="page-container checkout-main">
<%= if @order && @order.payment_status == "paid" do %>
<div class="checkout-header">
<div class="checkout-icon">
<svg
width="32"
height="32"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.5"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
</div>
<h1 class="checkout-heading">
Thank you for your order
</h1>
<p class="checkout-meta">
Order <strong>{@order.order_number}</strong>
</p>
<%= if @order.customer_email do %>
<p class="checkout-meta">
A confirmation will be sent to <strong>{@order.customer_email}</strong>
</p>
<% end %>
</div>
<.shop_card class="checkout-card">
<h2 class="checkout-heading">
Order details
</h2>
<ul class="checkout-items">
<%= for item <- @order.items do %>
<li class="checkout-item">
<div>
<p class="checkout-item-name">
{item.product_name}
</p>
<%= if item.variant_title do %>
<p class="checkout-item-detail">
{item.variant_title}
</p>
<% end %>
<p class="checkout-item-detail">
Qty: {item.quantity}
</p>
</div>
<span class="checkout-item-price">
{Berrypod.Cart.format_price(item.unit_price * item.quantity)}
</span>
</li>
<% end %>
</ul>
<div class="checkout-total-border">
<div class="checkout-total">
<span class="checkout-total-label">Total</span>
<span class="checkout-total-amount">
{Berrypod.Cart.format_price(@order.total)}
</span>
</div>
</div>
</.shop_card>
<%= if @order.shipping_address != %{} do %>
<.shop_card class="checkout-card">
<h2 class="checkout-heading">
Shipping to
</h2>
<div class="checkout-shipping-address">
<p>{@order.shipping_address["name"]}</p>
<p>{@order.shipping_address["line1"]}</p>
<%= if @order.shipping_address["line2"] do %>
<p>{@order.shipping_address["line2"]}</p>
<% end %>
<p>
{@order.shipping_address["city"]}, {@order.shipping_address["postal_code"]}
</p>
<p>{@order.shipping_address["country"]}</p>
</div>
</.shop_card>
<% end %>
<div class="checkout-actions">
<.shop_link_button href="/collections/all" class="checkout-cta">
Continue shopping
</.shop_link_button>
</div>
<% else %>
<%!-- Payment pending or order not found --%>
<div class="checkout-header">
<div class="checkout-pending-icon">
<span class="checkout-pending-spinner">
<svg
width="32"
height="32"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</span>
</div>
<h1 class="checkout-heading">
Processing your payment
</h1>
<p class="checkout-pending-text">
Please wait while we confirm your payment. This usually takes a few seconds.
</p>
<p class="checkout-pending-hint">
If this page doesn't update, please <.link
navigate="/contact"
class="checkout-contact-link"
>contact us</.link>.
</p>
</div>
<% end %>
</main>
</.shop_layout>

View File

@ -1,21 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="collection">
<main id="main-content">
<.collection_header title="All Products" product_count={length(assigns[:products] || [])} />
<div class="page-container">
<.filter_bar categories={assigns[:categories] || []} />
<.product_grid theme_settings={@theme_settings}>
<%= for product <- assigns[:products] || [] do %>
<.product_card
product={product}
theme_settings={@theme_settings}
mode={@mode}
variant={:default}
show_category={true}
/>
<% end %>
</.product_grid>
</div>
</main>
</.shop_layout>

View File

@ -1,30 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="contact">
<main id="main-content" class="page-container contact-main">
<.hero_section
variant={:page}
title="Get in touch"
description="Sample contact page for the demo store. Add your own message here something friendly about how customers can reach you."
/>
<div class="contact-grid">
<.contact_form email="hello@example.com" />
<div class="contact-sidebar">
<.order_tracking_card tracking_state={assigns[:tracking_state] || :idle} />
<.info_card
title="Handy to know"
items={[
%{label: "Printing", value: "Example: 2-5 business days"},
%{label: "Delivery", value: "Example: 3-7 business days after printing"},
%{label: "Issues", value: "Example: Reprints for any defects"}
]}
/>
<.newsletter_card />
<.social_links_card />
</div>
</div>
</main>
</.shop_layout>

View File

@ -1,33 +0,0 @@
<.shop_layout {layout_assigns(assigns)}>
<main id="main-content" class="content-page">
<%= if assigns[:hero_background] do %>
<.hero_section
title={@hero_title}
description={@hero_description}
background={@hero_background}
/>
<% else %>
<.hero_section
variant={:page}
title={@hero_title}
description={@hero_description}
/>
<% end %>
<div class="content-body">
<%= if assigns[:image_src] do %>
<div class="content-image">
<.responsive_image
src={@image_src}
source_width={1200}
alt={@image_alt}
sizes="(max-width: 800px) 100vw, 800px"
class="content-hero-image"
/>
</div>
<% end %>
<.rich_text blocks={@content_blocks} />
</div>
</main>
</.shop_layout>

View File

@ -1,33 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="error" error_page>
<main
id="main-content"
class="error-main"
>
<div class="page-container error-container">
<.hero_section
variant={:error}
pre_title={@error_code}
title={@error_title}
description={@error_description}
cta_text="Go to Homepage"
cta_page="home"
cta_href="/"
secondary_cta_text="Browse Products"
secondary_cta_page="collection"
secondary_cta_href="/collections/all"
mode={@mode}
/>
<.product_grid columns={:fixed_4}>
<%= for product <- Enum.take(assigns[:products] || [], 4) do %>
<.product_card
product={product}
theme_settings={@theme_settings}
mode={@mode}
variant={:minimal}
/>
<% end %>
</.product_grid>
</div>
</main>
</.shop_layout>

View File

@ -1,31 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="home">
<main id="main-content">
<.hero_section
title="Original designs, printed on demand"
description="Welcome to the Berrypod demo store. This is where your hero text goes something short and punchy about what makes your shop worth a browse."
cta_text="Shop the collection"
cta_page="collection"
cta_href="/collections/all"
mode={@mode}
/>
<.category_nav categories={assigns[:categories] || []} mode={@mode} />
<.featured_products_section
title="Featured products"
products={assigns[:products] || []}
theme_settings={@theme_settings}
mode={@mode}
/>
<.image_text_section
title="Made with passion, printed with care"
description="This is an example content section. Use it to share your story, highlight what makes your products special, or link to your about page."
image_url="/mockups/mountain-sunrise-print-3-800.webp"
link_text="Learn more about the studio →"
link_page="about"
link_href="/about"
mode={@mode}
/>
</main>
</.shop_layout>

View File

@ -1,120 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="contact">
<main id="main-content" class="page-container order-detail-main">
<%= if @order do %>
<div class="order-detail-header">
<.link navigate="/orders" class="order-detail-back">
← Back to orders
</.link>
<h1 class="checkout-heading" style="margin-top: 1.5rem;">{@order.order_number}</h1>
<p class="checkout-meta">
{Calendar.strftime(@order.inserted_at, "%-d %B %Y")}
</p>
<span class={"order-status-badge order-status-badge--#{@order.fulfilment_status} order-status-badge--lg"}>
{format_order_status(@order.fulfilment_status)}
</span>
</div>
<%= if @order.tracking_number || @order.tracking_url do %>
<.shop_card class="order-detail-tracking-card">
<div class="order-detail-tracking">
<svg
width="20"
height="20"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.25 18.75a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 0 1-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 0 0-3.213-9.193 2.056 2.056 0 0 0-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 0 0-10.026 0 1.106 1.106 0 0 0-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12"
/>
</svg>
<div>
<p class="order-detail-tracking-label">Shipment tracking</p>
<%= if @order.tracking_number do %>
<p class="order-detail-tracking-number">{@order.tracking_number}</p>
<% end %>
</div>
<%= if @order.tracking_url do %>
<a
href={@order.tracking_url}
target="_blank"
rel="noopener noreferrer"
class="order-detail-tracking-btn themed-button"
>
Track parcel
</a>
<% end %>
</div>
</.shop_card>
<% end %>
<.shop_card class="checkout-card">
<h2 class="checkout-heading">Items ordered</h2>
<ul class="checkout-items">
<%= for item <- @order.items do %>
<% info = @thumbnails[item.variant_id] %>
<li class="checkout-item">
<%= if info && info.thumb do %>
<img src={info.thumb} alt={item.product_name} class="checkout-item-thumb" />
<% end %>
<div>
<%= if info && info.slug do %>
<.link
navigate={"/products/#{info.slug}"}
class="checkout-item-name checkout-item-link"
>
{item.product_name}
</.link>
<% else %>
<p class="checkout-item-name">{item.product_name}</p>
<% end %>
<%= if item.variant_title && item.variant_title != "" do %>
<p class="checkout-item-detail">{item.variant_title}</p>
<% end %>
<p class="checkout-item-detail">Qty: {item.quantity}</p>
</div>
<span class="checkout-item-price">
{Berrypod.Cart.format_price(item.unit_price * item.quantity)}
</span>
</li>
<% end %>
</ul>
<div class="checkout-total-border">
<div class="checkout-total">
<span class="checkout-total-label">Total</span>
<span class="checkout-total-amount">
{Berrypod.Cart.format_price(@order.total)}
</span>
</div>
</div>
</.shop_card>
<%= if @order.shipping_address != %{} do %>
<.shop_card class="checkout-card">
<h2 class="checkout-heading">Shipping to</h2>
<div class="checkout-shipping-address">
<p>{@order.shipping_address["name"]}</p>
<p>{@order.shipping_address["line1"]}</p>
<%= if @order.shipping_address["line2"] do %>
<p>{@order.shipping_address["line2"]}</p>
<% end %>
<p>
{@order.shipping_address["city"]}, {@order.shipping_address["postal_code"]}
</p>
<p>{@order.shipping_address["country"]}</p>
</div>
</.shop_card>
<% end %>
<div class="checkout-actions">
<.shop_link_button href="/collections/all">
Continue shopping
</.shop_link_button>
</div>
<% end %>
</main>
</.shop_layout>

View File

@ -1,71 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="contact">
<main id="main-content" class="page-container orders-main">
<div class="orders-header">
<h1 class="orders-page-title">Your orders</h1>
<%= if @lookup_email do %>
<p class="orders-email-label">
Orders for <strong>{@lookup_email}</strong>
</p>
<% end %>
</div>
<%= cond do %>
<% is_nil(@orders) -> %>
<div class="orders-empty">
<p>This link has expired or is invalid.</p>
<p class="orders-empty-hint">
Head back to the <.link navigate="/contact">contact page</.link> to request a new one.
</p>
</div>
<% @orders == [] -> %>
<div class="orders-empty">
<p>No orders found for that email address.</p>
<p class="orders-empty-hint">
If something doesn't look right, <.link navigate="/contact">get in touch</.link>.
</p>
</div>
<% true -> %>
<div class="orders-list">
<%= for order <- @orders do %>
<.link navigate={"/orders/#{order.order_number}"} class="order-summary-card">
<div class="order-summary-top">
<div>
<p class="order-summary-number">{order.order_number}</p>
<p class="order-summary-date">
{Calendar.strftime(order.inserted_at, "%-d %B %Y")}
</p>
</div>
<span class={"order-status-badge order-status-badge--#{order.fulfilment_status}"}>
{format_order_status(order.fulfilment_status)}
</span>
</div>
<ul class="order-summary-items">
<%= for item <- Enum.take(order.items, 2) do %>
<li class="order-summary-item">
{item.quantity}× {item.product_name}
<%= if item.variant_title && item.variant_title != "" do %>
<span class="order-summary-variant">· {item.variant_title}</span>
<% end %>
</li>
<% end %>
<%= if length(order.items) > 2 do %>
<li class="order-summary-more">
+{length(order.items) - 2} more
</li>
<% end %>
</ul>
<div class="order-summary-footer">
<span class="order-summary-total">
{Berrypod.Cart.format_price(order.total)}
</span>
<span class="order-summary-arrow">→</span>
</div>
</.link>
<% end %>
</div>
<% end %>
</main>
</.shop_layout>

View File

@ -1,78 +0,0 @@
<.shop_layout {layout_assigns(assigns)} active_page="pdp">
<main id="main-content" class="page-container">
<.breadcrumb
items={
if @product.category do
[
%{
label: @product.category,
page: "collection",
href:
"/collections/#{@product.category |> String.downcase() |> String.replace(" ", "-")}"
}
]
else
[]
end ++
[%{label: @product.title, current: true}]
}
mode={@mode}
/>
<div class="pdp-grid">
<.product_gallery images={@gallery_images} product_name={@product.title} />
<div>
<.product_info product={@product} display_price={@display_price} />
<form action="/cart/add" method="post" phx-submit="add_to_cart">
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
<input
type="hidden"
name="variant_id"
value={@selected_variant && @selected_variant.id}
/>
<%!-- quantity is provided by the quantity_selector input below --%>
<%!-- Dynamic variant selectors --%>
<%= for option_type <- @option_types do %>
<.variant_selector
option_type={option_type}
selected={@selected_options[option_type.name]}
available={@available_options[option_type.name] || []}
mode={@mode}
option_urls={(@option_urls || %{})[option_type.name] || %{}}
/>
<% end %>
<%!-- Fallback for products with no variant options --%>
<div
:if={@option_types == []}
class="pdp-variant-fallback"
>
One size
</div>
<.quantity_selector quantity={@quantity} in_stock={@product.in_stock} />
<.add_to_cart_button mode={@mode} />
</form>
<.trust_badges :if={@theme_settings.pdp_trust_badges} />
<.product_details product={@product} />
</div>
</div>
<.reviews_section
:if={@theme_settings.pdp_reviews}
reviews={Berrypod.Theme.PreviewData.reviews()}
average_rating={5}
total_count={24}
/>
<.related_products_section
:if={@theme_settings.pdp_related_products}
products={@related_products}
theme_settings={@theme_settings}
mode={@mode}
/>
</main>
</.shop_layout>

View File

@ -1,7 +1,7 @@
defmodule BerrypodWeb.Admin.Theme.Index do defmodule BerrypodWeb.Admin.Theme.Index do
use BerrypodWeb, :live_view use BerrypodWeb, :live_view
alias Berrypod.Settings alias Berrypod.{Pages, Settings}
alias Berrypod.Media alias Berrypod.Media
alias Berrypod.Theme.{CSSGenerator, Presets, PreviewData} alias Berrypod.Theme.{CSSGenerator, Presets, PreviewData}
alias Berrypod.Workers.FaviconGeneratorWorker alias Berrypod.Workers.FaviconGeneratorWorker
@ -403,7 +403,7 @@ defmodule BerrypodWeb.Admin.Theme.Index do
}) })
end end
# Preview page component — delegates to shared PageTemplates with preview-specific assigns # Unified preview — loads page definition, applies context, renders via PageRenderer
attr :page, :atom, required: true attr :page, :atom, required: true
attr :preview_data, :map, required: true attr :preview_data, :map, required: true
attr :theme_settings, :map, required: true attr :theme_settings, :map, required: true
@ -411,17 +411,24 @@ defmodule BerrypodWeb.Admin.Theme.Index do
attr :header_image, :any, required: true attr :header_image, :any, required: true
attr :cart_drawer_open, :boolean, default: false attr :cart_drawer_open, :boolean, default: false
defp preview_page(%{page: :home} = assigns) do defp preview_page(assigns) do
assigns = preview_assigns(assigns) slug = to_string(assigns.page)
~H"<BerrypodWeb.PageTemplates.home {assigns} />" page = Pages.get_page(slug)
assigns =
assigns
|> preview_assigns()
|> assign(:page, page)
|> preview_page_context(slug)
extra = Pages.load_block_data(page.blocks, assigns)
assigns = assign(assigns, extra)
~H"<BerrypodWeb.PageRenderer.render_page {assigns} />"
end end
defp preview_page(%{page: :collection} = assigns) do # Page-context data needed by specific page types in preview mode
assigns = preview_assigns(assigns) defp preview_page_context(assigns, "pdp") do
~H"<BerrypodWeb.PageTemplates.collection {assigns} />"
end
defp preview_page(%{page: :pdp} = assigns) do
product = List.first(assigns.preview_data.products) product = List.first(assigns.preview_data.products)
option_types = Map.get(product, :option_types) || [] option_types = Map.get(product, :option_types) || []
variants = Map.get(product, :variants) || [] variants = Map.get(product, :variants) || []
@ -441,23 +448,18 @@ defmodule BerrypodWeb.Admin.Theme.Index do
display_price = display_price =
if selected_variant, do: selected_variant.price, else: product.cheapest_price if selected_variant, do: selected_variant.price, else: product.cheapest_price
assigns =
assigns assigns
|> preview_assigns()
|> assign(:product, product) |> assign(:product, product)
|> assign(:gallery_images, build_gallery_images(product)) |> assign(:gallery_images, build_gallery_images(product))
|> assign(:related_products, Enum.slice(assigns.preview_data.products, 1, 4))
|> assign(:option_types, option_types) |> assign(:option_types, option_types)
|> assign(:selected_options, selected_options) |> assign(:selected_options, selected_options)
|> assign(:available_options, available_options) |> assign(:available_options, available_options)
|> assign(:display_price, display_price) |> assign(:display_price, display_price)
|> assign(:quantity, 1) |> assign(:quantity, 1)
|> assign(:option_urls, %{}) |> assign(:option_urls, %{})
~H"<BerrypodWeb.PageTemplates.pdp {assigns} />"
end end
defp preview_page(%{page: :cart} = assigns) do defp preview_page_context(assigns, "cart") do
cart_items = assigns.preview_data.cart_items cart_items = assigns.preview_data.cart_items
subtotal = subtotal =
@ -465,93 +467,38 @@ defmodule BerrypodWeb.Admin.Theme.Index do
acc + item.product.cheapest_price * item.quantity acc + item.product.cheapest_price * item.quantity
end) end)
assigns =
assigns assigns
|> preview_assigns()
|> assign(:cart_page_items, cart_items) |> assign(:cart_page_items, cart_items)
|> assign(:cart_page_subtotal, subtotal) |> assign(:cart_page_subtotal, subtotal)
~H"<BerrypodWeb.PageTemplates.cart {assigns} />"
end end
defp preview_page(%{page: :about} = assigns) do defp preview_page_context(assigns, "about") do
assigns = assign(assigns, :content_blocks, PreviewData.about_content())
assigns
|> preview_assigns()
|> assign(%{
active_page: "about",
hero_title: "About the studio",
hero_description: "Your story goes here this is sample content for the demo shop",
hero_background: :sunken,
image_src: "/mockups/night-sky-blanket-3",
image_alt: "Night sky blanket draped over a chair",
content_blocks: PreviewData.about_content()
})
~H"<BerrypodWeb.PageTemplates.content {assigns} />"
end end
defp preview_page(%{page: :delivery} = assigns) do defp preview_page_context(assigns, "delivery") do
assigns = assign(assigns, :content_blocks, PreviewData.delivery_content())
assigns
|> preview_assigns()
|> assign(%{
active_page: "delivery",
hero_title: "Delivery & returns",
hero_description: "Everything you need to know about shipping and returns",
content_blocks: PreviewData.delivery_content()
})
~H"<BerrypodWeb.PageTemplates.content {assigns} />"
end end
defp preview_page(%{page: :privacy} = assigns) do defp preview_page_context(assigns, "privacy") do
assigns = assign(assigns, :content_blocks, PreviewData.privacy_content())
assigns
|> preview_assigns()
|> assign(%{
active_page: "privacy",
hero_title: "Privacy policy",
hero_description: "How we handle your personal information",
content_blocks: PreviewData.privacy_content()
})
~H"<BerrypodWeb.PageTemplates.content {assigns} />"
end end
defp preview_page(%{page: :terms} = assigns) do defp preview_page_context(assigns, "terms") do
assigns = assign(assigns, :content_blocks, PreviewData.terms_content())
assigns
|> preview_assigns()
|> assign(%{
active_page: "terms",
hero_title: "Terms of service",
hero_description: "The legal bits",
content_blocks: PreviewData.terms_content()
})
~H"<BerrypodWeb.PageTemplates.content {assigns} />"
end end
defp preview_page(%{page: :contact} = assigns) do defp preview_page_context(assigns, "error") do
assigns = preview_assigns(assigns) assign(assigns, %{
~H"<BerrypodWeb.PageTemplates.contact {assigns} />"
end
defp preview_page(%{page: :error} = assigns) do
assigns =
assigns
|> preview_assigns()
|> assign(%{
error_code: "404", error_code: "404",
error_title: "Page Not Found", error_title: "Page Not Found",
error_description: error_description:
"Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved." "Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved."
}) })
~H"<BerrypodWeb.PageTemplates.error {assigns} />"
end end
defp preview_page_context(assigns, _slug), do: assigns
defp build_gallery_images(product) do defp build_gallery_images(product) do
alias Berrypod.Products.ProductImage alias Berrypod.Products.ProductImage

View File

@ -1,7 +1,7 @@
defmodule BerrypodWeb.Shop.CheckoutSuccess do defmodule BerrypodWeb.Shop.CheckoutSuccess do
use BerrypodWeb, :live_view use BerrypodWeb, :live_view
alias Berrypod.{Analytics, Orders} alias Berrypod.{Analytics, Orders, Pages}
@impl true @impl true
def mount(%{"session_id" => session_id}, _session, socket) do def mount(%{"session_id" => session_id}, _session, socket) do
@ -29,10 +29,13 @@ defmodule BerrypodWeb.Shop.CheckoutSuccess do
socket socket
end end
page = Pages.get_page("checkout_success")
socket = socket =
socket socket
|> assign(:page_title, "Order confirmed") |> assign(:page_title, "Order confirmed")
|> assign(:order, order) |> assign(:order, order)
|> assign(:page, page)
{:ok, socket} {:ok, socket}
end end
@ -49,7 +52,7 @@ defmodule BerrypodWeb.Shop.CheckoutSuccess do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<BerrypodWeb.PageTemplates.checkout_success {assigns} /> <BerrypodWeb.PageRenderer.render_page {assigns} />
""" """
end end
end end

View File

@ -1,13 +1,20 @@
defmodule BerrypodWeb.Shop.OrderDetail do defmodule BerrypodWeb.Shop.OrderDetail do
use BerrypodWeb, :live_view use BerrypodWeb, :live_view
alias Berrypod.Orders alias Berrypod.{Orders, Pages}
alias Berrypod.Products alias Berrypod.Products
alias Berrypod.Products.ProductImage alias Berrypod.Products.ProductImage
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
{:ok, assign(socket, :lookup_email, session["order_lookup_email"])} page = Pages.get_page("order_detail")
socket =
socket
|> assign(:lookup_email, session["order_lookup_email"])
|> assign(:page, page)
{:ok, socket}
end end
@impl true @impl true
@ -49,7 +56,7 @@ defmodule BerrypodWeb.Shop.OrderDetail do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<BerrypodWeb.PageTemplates.order_detail {assigns} /> <BerrypodWeb.PageRenderer.render_page {assigns} />
""" """
end end
end end

View File

@ -1,16 +1,18 @@
defmodule BerrypodWeb.Shop.Orders do defmodule BerrypodWeb.Shop.Orders do
use BerrypodWeb, :live_view use BerrypodWeb, :live_view
alias Berrypod.Orders alias Berrypod.{Orders, Pages}
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
email = session["order_lookup_email"] email = session["order_lookup_email"]
page = Pages.get_page("orders")
socket = socket =
socket socket
|> assign(:page_title, "Your orders") |> assign(:page_title, "Your orders")
|> assign(:lookup_email, email) |> assign(:lookup_email, email)
|> assign(:page, page)
socket = socket =
if email do if email do
@ -28,7 +30,7 @@ defmodule BerrypodWeb.Shop.Orders do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<BerrypodWeb.PageTemplates.orders {assigns} /> <BerrypodWeb.PageRenderer.render_page {assigns} />
""" """
end end
end end

View File

@ -856,7 +856,6 @@ defmodule BerrypodWeb.PageRenderer do
defp collection_path(slug, "featured"), do: ~p"/collections/#{slug}" defp collection_path(slug, "featured"), do: ~p"/collections/#{slug}"
defp collection_path(slug, sort), do: ~p"/collections/#{slug}?sort=#{sort}" defp collection_path(slug, sort), do: ~p"/collections/#{slug}?sort=#{sort}"
# Reuse from PageTemplates
def format_order_status("unfulfilled"), do: "Being prepared" def format_order_status("unfulfilled"), do: "Being prepared"
def format_order_status("submitted"), do: "Sent to printer" def format_order_status("submitted"), do: "Sent to printer"
def format_order_status("processing"), do: "In production" def format_order_status("processing"), do: "In production"