consolidate shop pages into unified LiveView for editor state persistence
All checks were successful
deploy / deploy (push) Successful in 1m27s
All checks were successful
deploy / deploy (push) Successful in 1m27s
Replace individual shop LiveViews with a single Shop.Page that dispatches to page modules based on live_action. This enables patch navigation between pages, preserving socket state (including editor state) across transitions. Changes: - Add Shop.Page unified LiveView with handle_params dispatch - Extract page logic into Shop.Pages.* modules (Home, Product, Collection, etc.) - Update router to use Shop.Page with live_action for all shop routes - Change navigate= to patch= in shop component links - Add maybe_sync_editing_blocks to reload editor state when page changes - Track editor_page_slug to detect cross-page navigation while editing - Fix picture element height when hover image disabled - Extract ThemeEditor components for shared use Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
"""
|
||||
end
|
||||
|
||||
# Theme editor content - shows theme controls
|
||||
# Theme editor content - uses shared component
|
||||
attr :theme_editor_settings, :map, default: nil
|
||||
attr :theme_editor_active_preset, :atom, default: nil
|
||||
attr :theme_editor_presets, :list, default: []
|
||||
@@ -187,131 +187,14 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
|
||||
defp theme_editor_content(assigns) do
|
||||
~H"""
|
||||
<div class="editor-theme-content">
|
||||
<%= if @theme_editor_settings do %>
|
||||
<%!-- Shop name --%>
|
||||
<div class="theme-section">
|
||||
<label class="theme-section-label">Shop name</label>
|
||||
<form phx-change="theme_update_setting" phx-value-field="site_name">
|
||||
<input
|
||||
type="text"
|
||||
name="site_name"
|
||||
value={@site_name}
|
||||
placeholder="Your shop name"
|
||||
class="admin-input"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<%!-- Presets --%>
|
||||
<div class="theme-section">
|
||||
<label class="theme-section-label">Preset</label>
|
||||
<div class="theme-presets">
|
||||
<%= for {preset_name, description} <- @theme_editor_presets do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="theme_apply_preset"
|
||||
phx-value-preset={preset_name}
|
||||
class={[
|
||||
"theme-preset",
|
||||
@theme_editor_active_preset == preset_name && "theme-preset-active"
|
||||
]}
|
||||
>
|
||||
<div class="theme-preset-name">{preset_name}</div>
|
||||
<div class="theme-preset-desc">{description}</div>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- Mood --%>
|
||||
<div class="theme-section">
|
||||
<label class="theme-section-label">Colour mood</label>
|
||||
<div class="theme-chips">
|
||||
<%= for mood <- ["warm", "neutral", "cool", "dark"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="theme_update_setting"
|
||||
phx-value-field="mood"
|
||||
phx-value-setting_value={mood}
|
||||
class={["theme-chip", @theme_editor_settings.mood == mood && "theme-chip-active"]}
|
||||
>
|
||||
{mood}
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- Typography --%>
|
||||
<div class="theme-section">
|
||||
<label class="theme-section-label">Font style</label>
|
||||
<div class="theme-chips">
|
||||
<%= for typo <- ["clean", "editorial", "modern", "classic", "friendly", "minimal"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="theme_update_setting"
|
||||
phx-value-field="typography"
|
||||
phx-value-setting_value={typo}
|
||||
class={[
|
||||
"theme-chip",
|
||||
@theme_editor_settings.typography == typo && "theme-chip-active"
|
||||
]}
|
||||
>
|
||||
{typo}
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- Shape --%>
|
||||
<div class="theme-section">
|
||||
<label class="theme-section-label">Corner style</label>
|
||||
<div class="theme-chips">
|
||||
<%= for shape <- ["sharp", "soft", "round", "pill"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="theme_update_setting"
|
||||
phx-value-field="shape"
|
||||
phx-value-setting_value={shape}
|
||||
class={["theme-chip", @theme_editor_settings.shape == shape && "theme-chip-active"]}
|
||||
>
|
||||
{shape}
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- More options link --%>
|
||||
<details
|
||||
class="theme-customise"
|
||||
id="theme-customise-section"
|
||||
open={@theme_editor_customise_open}
|
||||
>
|
||||
<summary class="theme-customise-summary" phx-click="theme_toggle_customise">
|
||||
<span class="theme-customise-label">More options</span>
|
||||
<svg
|
||||
class="theme-customise-chevron"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="theme-customise-body">
|
||||
<p class="admin-text-secondary">
|
||||
For full theme customisation including branding, colours, and layout, <a
|
||||
href="/admin/theme"
|
||||
class="admin-link"
|
||||
>visit the theme editor</a>.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
<% else %>
|
||||
<p class="admin-text-secondary">Loading theme settings...</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<.compact_editor
|
||||
theme_settings={@theme_editor_settings}
|
||||
active_preset={@theme_editor_active_preset}
|
||||
presets={@theme_editor_presets}
|
||||
site_name={@site_name}
|
||||
customise_open={@theme_editor_customise_open}
|
||||
event_prefix="theme_"
|
||||
/>
|
||||
"""
|
||||
end
|
||||
|
||||
@@ -727,7 +610,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<ul class="collection-filter-pills">
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path("all", @current_sort)}
|
||||
patch={collection_path("all", @current_sort)}
|
||||
aria-current={@current_slug == nil && "page"}
|
||||
class={["collection-filter-pill", @current_slug == nil && "active"]}
|
||||
>
|
||||
@@ -736,7 +619,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
</li>
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path("sale", @current_sort)}
|
||||
patch={collection_path("sale", @current_sort)}
|
||||
aria-current={@current_slug == "sale" && "page"}
|
||||
class={["collection-filter-pill", @current_slug == "sale" && "active"]}
|
||||
>
|
||||
@@ -746,7 +629,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<%= for category <- assigns[:categories] || [] do %>
|
||||
<li>
|
||||
<.link
|
||||
navigate={collection_path(category.slug, @current_sort)}
|
||||
patch={collection_path(category.slug, @current_sort)}
|
||||
aria-current={@current_slug == category.slug && "page"}
|
||||
class={["collection-filter-pill", @current_slug == category.slug && "active"]}
|
||||
>
|
||||
@@ -813,7 +696,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<%= if (assigns[:products] || []) == [] do %>
|
||||
<div class="collection-empty">
|
||||
<p>No products found in this collection.</p>
|
||||
<.link navigate={~p"/collections/all"} class="collection-empty-link">
|
||||
<.link patch={~p"/collections/all"} class="collection-empty-link">
|
||||
View all products
|
||||
</.link>
|
||||
</div>
|
||||
@@ -1020,7 +903,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
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>.
|
||||
If this page doesn't update, please <.link patch="/contact" class="checkout-contact-link">contact us</.link>.
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -1045,20 +928,20 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<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.
|
||||
Head back to the <.link patch="/contact">contact page</.link> to request a new one.
|
||||
</p>
|
||||
</div>
|
||||
<% assigns[: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>.
|
||||
If something doesn't look right, <.link patch="/contact">get in touch</.link>.
|
||||
</p>
|
||||
</div>
|
||||
<% true -> %>
|
||||
<div class="orders-list">
|
||||
<%= for order <- assigns[:orders] do %>
|
||||
<.link navigate={"/orders/#{order.order_number}"} class="order-summary-card">
|
||||
<.link patch={"/orders/#{order.order_number}"} class="order-summary-card">
|
||||
<div class="order-summary-top">
|
||||
<div>
|
||||
<p class="order-summary-number">{order.order_number}</p>
|
||||
@@ -1100,7 +983,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
~H"""
|
||||
<%= if assigns[:order] do %>
|
||||
<div class="order-detail-header">
|
||||
<.link navigate="/orders" class="order-detail-back">← Back to orders</.link>
|
||||
<.link patch="/orders" class="order-detail-back">← Back to orders</.link>
|
||||
<h1 class="checkout-heading" style="margin-top: 1.5rem;">{assigns[:order].order_number}</h1>
|
||||
<p class="checkout-meta">{Calendar.strftime(assigns[:order].inserted_at, "%-d %B %Y")}</p>
|
||||
<span class={"order-status-badge order-status-badge-#{assigns[:order].fulfilment_status} order-status-badge-lg"}>
|
||||
@@ -1155,7 +1038,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<div>
|
||||
<%= if info && info.slug do %>
|
||||
<.link
|
||||
navigate={"/products/#{info.slug}"}
|
||||
patch={"/products/#{info.slug}"}
|
||||
class="checkout-item-name checkout-item-link"
|
||||
>
|
||||
{item.product_name}
|
||||
@@ -1245,7 +1128,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<%= if (assigns[:search_page_query] || "") != "" do %>
|
||||
<div class="collection-empty">
|
||||
<p>No products found for “{assigns[:search_page_query]}”</p>
|
||||
<.link navigate="/collections/all" class="collection-empty-link">Browse all products</.link>
|
||||
<.link patch="/collections/all" class="collection-empty-link">Browse all products</.link>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1285,7 +1168,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
~H"""
|
||||
<div class="block-button" data-align={@alignment}>
|
||||
<.link
|
||||
navigate={@href}
|
||||
patch={@href}
|
||||
class={if @btn_style == "outline", do: "themed-button-outline", else: "themed-button"}
|
||||
>
|
||||
{@text}
|
||||
|
||||
Reference in New Issue
Block a user