integrate R module and add url editor ui
Replaces hardcoded paths with R module throughout: - Shop components: layout nav, cart, product links - Controllers: cart, checkout, contact, seo, order lookup - Shop pages: collection, product, search, checkout success, etc. - Site context: nav item url resolution Admin URL management: - Settings page: prefix editor with validation feedback - Page renderer: url_editor component for page URLs - CSS for url editor styling Test updates for cache isolation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
import BerrypodWeb.ShopComponents.Base
|
||||
|
||||
alias Berrypod.Products.{Product, ProductImage}
|
||||
alias BerrypodWeb.R
|
||||
|
||||
defp close_cart_drawer_js do
|
||||
Phoenix.LiveView.JS.push("close_cart_drawer")
|
||||
@@ -193,7 +194,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
>
|
||||
<%= if @mode != :preview do %>
|
||||
<.link
|
||||
patch={"/products/#{@item.product_id}"}
|
||||
patch={R.product(@item.product_id)}
|
||||
class={["cart-item-image", !@item.image && "cart-item-image--empty"]}
|
||||
data-size={if @size == :compact, do: "compact"}
|
||||
style={if @item.image, do: "background-image: url('#{@item.image}');"}
|
||||
@@ -212,7 +213,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
<h3 class="cart-item-name" data-size={if @size == :compact, do: "compact"}>
|
||||
<%= if @mode != :preview do %>
|
||||
<.link
|
||||
patch={"/products/#{@item.product_id}"}
|
||||
patch={R.product(@item.product_id)}
|
||||
class="cart-item-name-link"
|
||||
>
|
||||
{@item.name}
|
||||
@@ -321,7 +322,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
</button>
|
||||
<% else %>
|
||||
<.link
|
||||
patch="/collections/all"
|
||||
patch={R.collection("all")}
|
||||
class="cart-continue-link"
|
||||
>
|
||||
Continue shopping
|
||||
@@ -533,7 +534,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
</.shop_button>
|
||||
<p class="order-summary-notice">Checkout isn't available yet.</p>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
href={R.collection("all")}
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
@@ -544,7 +545,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
</.shop_button>
|
||||
<p class="order-summary-notice">Remove unavailable items to checkout.</p>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
href={R.collection("all")}
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
@@ -557,7 +558,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
</.shop_button>
|
||||
</form>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
href={R.collection("all")}
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
|
||||
@@ -4,6 +4,8 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
import BerrypodWeb.ShopComponents.Cart
|
||||
import BerrypodWeb.ShopComponents.Content
|
||||
|
||||
alias BerrypodWeb.R
|
||||
|
||||
@doc """
|
||||
Renders the announcement bar.
|
||||
|
||||
@@ -153,10 +155,18 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
# Extract a slug from a URL for nav item matching
|
||||
defp extract_slug_from_url(url) when is_binary(url) do
|
||||
cond do
|
||||
url == "/" -> "home"
|
||||
String.starts_with?(url, "/collections") -> "collection"
|
||||
String.starts_with?(url, "/products") -> "pdp"
|
||||
true -> url |> String.trim_leading("/") |> String.split("/") |> List.first() || ""
|
||||
url == "/" ->
|
||||
"home"
|
||||
|
||||
true ->
|
||||
# Extract first segment and check if it's a known prefix
|
||||
first_segment = url |> String.trim_leading("/") |> String.split("/") |> List.first() || ""
|
||||
|
||||
case R.prefix_type_from_segment(first_segment) do
|
||||
:collections -> "collection"
|
||||
:products -> "pdp"
|
||||
_ -> first_segment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -651,7 +661,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
aria-selected="false"
|
||||
>
|
||||
<.link
|
||||
patch={"/products/#{item.product.slug || item.product.id}"}
|
||||
patch={R.product(item.product.slug || item.product.id)}
|
||||
class="search-result"
|
||||
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
||||
>
|
||||
@@ -782,7 +792,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
</a>
|
||||
<% else %>
|
||||
<.link
|
||||
patch={"/collections/#{category.slug}"}
|
||||
patch={R.collection(category.slug)}
|
||||
class="mobile-nav-link"
|
||||
phx-click={
|
||||
Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")
|
||||
@@ -868,7 +878,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
<% else %>
|
||||
<li>
|
||||
<.link
|
||||
patch="/collections/all"
|
||||
patch={R.collection("all")}
|
||||
class="footer-link"
|
||||
>
|
||||
All products
|
||||
@@ -877,7 +887,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
<%= for category <- @categories do %>
|
||||
<li>
|
||||
<.link
|
||||
patch={"/collections/#{category.slug}"}
|
||||
patch={R.collection(category.slug)}
|
||||
class="footer-link"
|
||||
>
|
||||
{category.name}
|
||||
@@ -1013,7 +1023,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
<.admin_cog_svg />
|
||||
</.link>
|
||||
<a
|
||||
href="/search"
|
||||
href={R.search()}
|
||||
phx-click={Phoenix.LiveView.JS.dispatch("open-search", to: "#search-modal")}
|
||||
class="header-icon-btn"
|
||||
aria-label="Search"
|
||||
@@ -1031,7 +1041,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="/cart"
|
||||
href={R.cart()}
|
||||
phx-click={open_cart_drawer_js()}
|
||||
class="header-icon-btn"
|
||||
aria-label="Cart"
|
||||
@@ -1241,6 +1251,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
attr :editor_dirty, :boolean, default: false
|
||||
attr :theme_dirty, :boolean, default: false
|
||||
attr :site_dirty, :boolean, default: false
|
||||
attr :settings_dirty, :boolean, default: false
|
||||
attr :editor_sheet_state, :atom, default: :collapsed
|
||||
attr :editor_save_status, :atom, default: :idle
|
||||
attr :editor_active_tab, :atom, default: :page
|
||||
@@ -1263,7 +1274,8 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
any_editing = assigns.editing || assigns.theme_editing
|
||||
|
||||
# Any tab has unsaved changes
|
||||
any_dirty = assigns.editor_dirty || assigns.theme_dirty || assigns.site_dirty
|
||||
any_dirty =
|
||||
assigns.editor_dirty || assigns.theme_dirty || assigns.site_dirty || assigns.settings_dirty
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
|
||||
@@ -5,6 +5,7 @@ defmodule BerrypodWeb.ShopComponents.Product do
|
||||
import BerrypodWeb.ShopComponents.Content, only: [responsive_image: 1]
|
||||
|
||||
alias Berrypod.Products.{Product, ProductImage}
|
||||
alias BerrypodWeb.R
|
||||
|
||||
@doc """
|
||||
Renders a product card with configurable variants.
|
||||
@@ -98,7 +99,7 @@ defmodule BerrypodWeb.ShopComponents.Product do
|
||||
|
||||
product_url =
|
||||
if assigns.clickable && assigns.mode != :preview do
|
||||
"/products/#{Map.get(assigns.product, :slug) || Map.get(assigns.product, :id)}"
|
||||
R.product(Map.get(assigns.product, :slug) || Map.get(assigns.product, :id))
|
||||
end
|
||||
|
||||
assigns =
|
||||
@@ -157,7 +158,7 @@ defmodule BerrypodWeb.ShopComponents.Product do
|
||||
</p>
|
||||
<% else %>
|
||||
<.link
|
||||
patch={"/collections/#{Slug.slugify(@product.category)}"}
|
||||
patch={R.collection(Slug.slugify(@product.category))}
|
||||
class="product-card-category"
|
||||
>
|
||||
{@product.category}
|
||||
@@ -177,7 +178,7 @@ defmodule BerrypodWeb.ShopComponents.Product do
|
||||
</a>
|
||||
<% else %>
|
||||
<.link
|
||||
patch={"/products/#{Map.get(@product, :slug) || Map.get(@product, :id)}"}
|
||||
patch={R.product(Map.get(@product, :slug) || Map.get(@product, :id))}
|
||||
class="stretched-link"
|
||||
>
|
||||
{@product.title}
|
||||
@@ -629,7 +630,7 @@ defmodule BerrypodWeb.ShopComponents.Product do
|
||||
</a>
|
||||
<% else %>
|
||||
<.link
|
||||
patch={"/collections/#{category.slug}"}
|
||||
patch={R.collection(category.slug)}
|
||||
class="category-card"
|
||||
>
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user