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:
jamey
2026-04-01 00:36:17 +01:00
parent c115f08cb8
commit a41771efc8
28 changed files with 938 additions and 160 deletions

View File

@@ -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