wire collection, PDP, cart, and search pages to page renderer
Stage 4 of the page builder: all shop pages now render via PageRenderer instead of inline templates or PageTemplates. - Collection: full filter bar moved to renderer (category pills, sort dropdown, CollectionFilters hook, empty state) - PDP: related_products and reviews loaded via block data loaders instead of manual queries - Cart: page definition loaded in mount, subtotal computed in render - Search: page definition loaded in mount, handle_params unchanged - Added Phoenix.VerifiedRoutes to PageRenderer for ~p sigil - Net -55 lines (128 added, 183 removed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
defmodule BerrypodWeb.Shop.Cart do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.Cart
|
||||
alias Berrypod.{Cart, Pages}
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, assign(socket, page_title: "Cart")}
|
||||
page = Pages.get_page("cart")
|
||||
{:ok, socket |> assign(:page_title, "Cart") |> assign(:page, page)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@@ -13,7 +14,7 @@ defmodule BerrypodWeb.Shop.Cart do
|
||||
assigns = assign(assigns, :cart_page_subtotal, Cart.calculate_subtotal(assigns.cart_items))
|
||||
|
||||
~H"""
|
||||
<BerrypodWeb.PageTemplates.cart {assigns} />
|
||||
<BerrypodWeb.PageRenderer.render_page {assigns} />
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule BerrypodWeb.Shop.Collection do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.Products
|
||||
alias Berrypod.{Pages, Products}
|
||||
|
||||
@sort_options [
|
||||
{"featured", "Featured"},
|
||||
@@ -14,8 +14,11 @@ defmodule BerrypodWeb.Shop.Collection do
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
page = Pages.get_page("collection")
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:page, page)
|
||||
|> assign(:sort_options, @sort_options)
|
||||
|> assign(:current_sort, "featured")
|
||||
|
||||
@@ -81,113 +84,10 @@ defmodule BerrypodWeb.Shop.Collection do
|
||||
defp collection_description("Sale"), do: "Browse our current sale items."
|
||||
defp collection_description(title), do: "Browse our #{String.downcase(title)} collection."
|
||||
|
||||
defp collection_path(slug, "featured"), do: ~p"/collections/#{slug}"
|
||||
defp collection_path(slug, sort), do: ~p"/collections/#{slug}?sort=#{sort}"
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<.shop_layout {layout_assigns(assigns)} active_page="collection">
|
||||
<main id="main-content">
|
||||
<.collection_header
|
||||
title={@collection_title}
|
||||
product_count={length(@products)}
|
||||
/>
|
||||
|
||||
<div class="page-container collection-body">
|
||||
<.collection_filter_bar
|
||||
categories={@categories}
|
||||
current_slug={
|
||||
case @current_category do
|
||||
:sale -> "sale"
|
||||
nil -> nil
|
||||
cat -> cat.slug
|
||||
end
|
||||
}
|
||||
sort_options={@sort_options}
|
||||
current_sort={@current_sort}
|
||||
/>
|
||||
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- @products do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
theme_settings={@theme_settings}
|
||||
mode={@mode}
|
||||
variant={:default}
|
||||
show_category={@current_category in [nil, :sale]}
|
||||
/>
|
||||
<% end %>
|
||||
</.product_grid>
|
||||
|
||||
<%= if @products == [] do %>
|
||||
<div class="collection-empty">
|
||||
<p>No products found in this collection.</p>
|
||||
<.link navigate={~p"/collections/all"} class="collection-empty-link">
|
||||
View all products
|
||||
</.link>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</main>
|
||||
</.shop_layout>
|
||||
"""
|
||||
end
|
||||
|
||||
defp collection_filter_bar(assigns) do
|
||||
~H"""
|
||||
<div class="filter-bar">
|
||||
<nav
|
||||
aria-label="Collection filters"
|
||||
id="collection-filters"
|
||||
phx-hook="CollectionFilters"
|
||||
class="collection-filters"
|
||||
>
|
||||
<ul class="collection-filter-pills">
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path("all", @current_sort)}
|
||||
aria-current={@current_slug == nil && "page"}
|
||||
class={["collection-filter-pill", @current_slug == nil && "active"]}
|
||||
>
|
||||
All
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path("sale", @current_sort)}
|
||||
aria-current={@current_slug == "sale" && "page"}
|
||||
class={["collection-filter-pill", @current_slug == "sale" && "active"]}
|
||||
>
|
||||
Sale
|
||||
</.link>
|
||||
</li>
|
||||
<%= for category <- @categories do %>
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path(category.slug, @current_sort)}
|
||||
aria-current={@current_slug == category.slug && "page"}
|
||||
class={["collection-filter-pill", @current_slug == category.slug && "active"]}
|
||||
>
|
||||
{category.name}
|
||||
</.link>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<form action={~p"/collections/#{@current_slug || "all"}"} method="get" phx-change="sort_changed">
|
||||
<.shop_select
|
||||
name="sort"
|
||||
options={@sort_options}
|
||||
selected={@current_sort}
|
||||
aria-label="Sort products"
|
||||
/>
|
||||
<noscript>
|
||||
<button type="submit" class="themed-button collection-sort-submit">Sort</button>
|
||||
</noscript>
|
||||
</form>
|
||||
</div>
|
||||
<BerrypodWeb.PageRenderer.render_page {assigns} />
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule BerrypodWeb.Shop.ProductShow do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.{Analytics, Cart}
|
||||
alias Berrypod.{Analytics, Cart, Pages}
|
||||
alias Berrypod.Images.Optimizer
|
||||
alias Berrypod.Products
|
||||
alias Berrypod.Products.{Product, ProductImage}
|
||||
@@ -13,13 +13,6 @@ defmodule BerrypodWeb.Shop.ProductShow do
|
||||
{:ok, push_navigate(socket, to: ~p"/collections/all")}
|
||||
|
||||
product ->
|
||||
related_products =
|
||||
Products.list_visible_products(
|
||||
category: product.category,
|
||||
limit: 4,
|
||||
exclude: product.id
|
||||
)
|
||||
|
||||
all_images =
|
||||
(product.images || [])
|
||||
|> Enum.sort_by(& &1.position)
|
||||
@@ -48,6 +41,8 @@ defmodule BerrypodWeb.Shop.ProductShow do
|
||||
og_url = base <> "/products/#{slug}"
|
||||
og_image = og_image_url(all_images)
|
||||
|
||||
page = Pages.get_page("pdp")
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:page_title, product.title)
|
||||
@@ -58,12 +53,15 @@ defmodule BerrypodWeb.Shop.ProductShow do
|
||||
|> assign(:json_ld, product_json_ld(product, og_url, og_image, base))
|
||||
|> assign(:product, product)
|
||||
|> assign(:all_images, all_images)
|
||||
|> assign(:related_products, related_products)
|
||||
|> assign(:quantity, 1)
|
||||
|> assign(:option_types, option_types)
|
||||
|> assign(:variants, variants)
|
||||
|> assign(:page, page)
|
||||
|
||||
{:ok, socket}
|
||||
# Block data loaders (related_products, reviews) run after product is assigned
|
||||
extra = Pages.load_block_data(page.blocks, socket.assigns)
|
||||
|
||||
{:ok, assign(socket, extra)}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -269,7 +267,7 @@ defmodule BerrypodWeb.Shop.ProductShow do
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<BerrypodWeb.PageTemplates.pdp {assigns} />
|
||||
<BerrypodWeb.PageRenderer.render_page {assigns} />
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
defmodule BerrypodWeb.Shop.Search do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.Search
|
||||
alias Berrypod.{Pages, Search}
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, assign(socket, :page_title, "Search")}
|
||||
page = Pages.get_page("search")
|
||||
{:ok, socket |> assign(:page_title, "Search") |> assign(:page, page)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@@ -27,49 +28,7 @@ defmodule BerrypodWeb.Shop.Search do
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<.shop_layout {layout_assigns(assigns)} active_page="search">
|
||||
<main id="main-content" class="page-container">
|
||||
<.page_title text="Search" />
|
||||
|
||||
<form action="/search" method="get" phx-submit="search_submit" class="search-page-form">
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
value={@search_page_query}
|
||||
placeholder="Search products..."
|
||||
class="themed-input"
|
||||
/>
|
||||
<button type="submit" class="themed-button">Search</button>
|
||||
</form>
|
||||
|
||||
<%= if @search_page_results != [] do %>
|
||||
<p class="search-page-count">
|
||||
{length(@search_page_results)} {if length(@search_page_results) == 1,
|
||||
do: "result",
|
||||
else: "results"} for "{@search_page_query}"
|
||||
</p>
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- @search_page_results do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
theme_settings={@theme_settings}
|
||||
mode={@mode}
|
||||
variant={:default}
|
||||
/>
|
||||
<% end %>
|
||||
</.product_grid>
|
||||
<% else %>
|
||||
<%= if @search_page_query != "" do %>
|
||||
<div class="collection-empty">
|
||||
<p>No products found for "{@search_page_query}"</p>
|
||||
<.link navigate="/collections/all" class="collection-empty-link">
|
||||
Browse all products
|
||||
</.link>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</main>
|
||||
</.shop_layout>
|
||||
<BerrypodWeb.PageRenderer.render_page {assigns} />
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user