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:
@@ -8,6 +8,7 @@ defmodule BerrypodWeb.Shop.Pages.CheckoutSuccess do
|
||||
import Phoenix.LiveView, only: [connected?: 1, redirect: 2]
|
||||
|
||||
alias Berrypod.{Analytics, Orders, Pages}
|
||||
alias BerrypodWeb.R
|
||||
|
||||
def init(socket, %{"session_id" => session_id}, _uri) do
|
||||
order = Orders.get_order_by_stripe_session(session_id)
|
||||
@@ -21,7 +22,7 @@ defmodule BerrypodWeb.Shop.Pages.CheckoutSuccess do
|
||||
if order && connected?(socket) && socket.assigns[:analytics_visitor_hash] do
|
||||
attrs =
|
||||
BerrypodWeb.AnalyticsHook.attrs(socket)
|
||||
|> Map.merge(%{pathname: "/checkout/success", revenue: order.total})
|
||||
|> Map.merge(%{pathname: R.checkout_success(), revenue: order.total})
|
||||
|
||||
Analytics.track_event("purchase", attrs)
|
||||
end
|
||||
@@ -54,7 +55,7 @@ defmodule BerrypodWeb.Shop.Pages.CheckoutSuccess do
|
||||
end
|
||||
|
||||
def init(socket, _params, _uri) do
|
||||
{:redirect, redirect(socket, to: "/")}
|
||||
{:redirect, redirect(socket, to: R.home())}
|
||||
end
|
||||
|
||||
def handle_params(_params, _uri, socket) do
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule BerrypodWeb.Shop.Pages.Collection do
|
||||
import Phoenix.LiveView, only: [push_patch: 2, push_navigate: 2, put_flash: 3]
|
||||
|
||||
alias Berrypod.{Pages, Pagination, Products}
|
||||
alias BerrypodWeb.R
|
||||
|
||||
@sort_options [
|
||||
{"featured", "Featured"},
|
||||
@@ -29,6 +30,11 @@ defmodule BerrypodWeb.Shop.Pages.Collection do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
# When accessed via custom URL (e.g. /shop) without a collection slug, show all products
|
||||
def handle_params(params, uri, socket) when not is_map_key(params, "slug") do
|
||||
handle_params(Map.put(params, "slug", "all"), uri, socket)
|
||||
end
|
||||
|
||||
def handle_params(%{"slug" => slug} = params, _uri, socket) do
|
||||
sort = params["sort"] || "featured"
|
||||
page_num = Pagination.parse_page(params)
|
||||
@@ -39,7 +45,7 @@ defmodule BerrypodWeb.Shop.Pages.Collection do
|
||||
socket
|
||||
|> assign(:page_title, title)
|
||||
|> assign(:page_description, collection_description(title))
|
||||
|> assign(:og_url, BerrypodWeb.Endpoint.url() <> "/collections/#{slug}")
|
||||
|> assign(:og_url, R.url(R.collection(slug)))
|
||||
|> assign(:collection_title, title)
|
||||
|> assign(:collection_slug, slug)
|
||||
|> assign(:current_category, category)
|
||||
@@ -53,7 +59,7 @@ defmodule BerrypodWeb.Shop.Pages.Collection do
|
||||
socket =
|
||||
socket
|
||||
|> put_flash(:error, "Collection not found")
|
||||
|> push_navigate(to: "/collections/all")
|
||||
|> push_navigate(to: R.collection("all"))
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
@@ -67,7 +73,7 @@ defmodule BerrypodWeb.Shop.Pages.Collection do
|
||||
category -> category.slug
|
||||
end
|
||||
|
||||
{:noreply, push_patch(socket, to: "/collections/#{slug}?sort=#{sort}")}
|
||||
{:noreply, push_patch(socket, to: R.collection(slug) <> "?sort=#{sort}")}
|
||||
end
|
||||
|
||||
def handle_event(_event, _params, _socket), do: :cont
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule BerrypodWeb.Shop.Pages.Contact do
|
||||
import Phoenix.LiveView, only: [push_navigate: 2, put_flash: 3]
|
||||
|
||||
alias Berrypod.{ContactNotifier, Orders}
|
||||
alias BerrypodWeb.R
|
||||
alias Berrypod.Orders.OrderNotifier
|
||||
alias Berrypod.Pages
|
||||
alias BerrypodWeb.OrderLookupController
|
||||
@@ -21,7 +22,7 @@ defmodule BerrypodWeb.Shop.Pages.Contact do
|
||||
:page_description,
|
||||
"Get in touch with us for any questions or help with your order."
|
||||
)
|
||||
|> assign(:og_url, BerrypodWeb.Endpoint.url() <> "/contact")
|
||||
|> assign(:og_url, R.url(R.contact()))
|
||||
|> assign(:tracking_state, :idle)
|
||||
|> assign(:page, page)
|
||||
|
||||
@@ -54,7 +55,7 @@ defmodule BerrypodWeb.Shop.Pages.Contact do
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Message sent! We'll get back to you soon.")
|
||||
|> push_navigate(to: "/contact")}
|
||||
|> push_navigate(to: R.contact())}
|
||||
|
||||
{:error, :invalid_params} ->
|
||||
{:noreply, put_flash(socket, :error, "Please fill in all required fields.")}
|
||||
|
||||
@@ -9,6 +9,7 @@ defmodule BerrypodWeb.Shop.Pages.Content do
|
||||
alias Berrypod.LegalPages
|
||||
alias Berrypod.Pages
|
||||
alias Berrypod.Theme.PreviewData
|
||||
alias BerrypodWeb.R
|
||||
|
||||
def init(socket, _params, _uri) do
|
||||
# Content pages load in handle_params based on live_action
|
||||
@@ -38,7 +39,7 @@ defmodule BerrypodWeb.Shop.Pages.Content do
|
||||
%{
|
||||
page_title: "About",
|
||||
page_description: "Your story goes here – this is sample content for the demo shop",
|
||||
og_url: BerrypodWeb.Endpoint.url() <> "/about"
|
||||
og_url: R.url(R.about())
|
||||
},
|
||||
PreviewData.about_content()
|
||||
}
|
||||
@@ -49,7 +50,7 @@ defmodule BerrypodWeb.Shop.Pages.Content do
|
||||
%{
|
||||
page_title: "Delivery & returns",
|
||||
page_description: "Everything you need to know about shipping and returns.",
|
||||
og_url: BerrypodWeb.Endpoint.url() <> "/delivery"
|
||||
og_url: R.url(R.delivery())
|
||||
},
|
||||
LegalPages.delivery_content()
|
||||
}
|
||||
@@ -60,7 +61,7 @@ defmodule BerrypodWeb.Shop.Pages.Content do
|
||||
%{
|
||||
page_title: "Privacy policy",
|
||||
page_description: "How we handle your personal information.",
|
||||
og_url: BerrypodWeb.Endpoint.url() <> "/privacy"
|
||||
og_url: R.url(R.privacy())
|
||||
},
|
||||
LegalPages.privacy_content()
|
||||
}
|
||||
@@ -71,7 +72,7 @@ defmodule BerrypodWeb.Shop.Pages.Content do
|
||||
%{
|
||||
page_title: "Terms of service",
|
||||
page_description: "The terms and conditions governing purchases from our shop.",
|
||||
og_url: BerrypodWeb.Endpoint.url() <> "/terms"
|
||||
og_url: R.url(R.terms())
|
||||
},
|
||||
LegalPages.terms_content()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule BerrypodWeb.Shop.Pages.OrderDetail do
|
||||
import Phoenix.LiveView, only: [push_navigate: 2]
|
||||
|
||||
alias Berrypod.{Orders, Pages}
|
||||
alias BerrypodWeb.R
|
||||
alias Berrypod.Products
|
||||
alias Berrypod.Products.ProductImage
|
||||
|
||||
@@ -54,7 +55,7 @@ defmodule BerrypodWeb.Shop.Pages.OrderDetail do
|
||||
|
||||
{:noreply, socket}
|
||||
else
|
||||
{:noreply, push_navigate(socket, to: "/orders")}
|
||||
{:noreply, push_navigate(socket, to: R.orders())}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
import Phoenix.LiveView, only: [connected?: 1, push_navigate: 2]
|
||||
|
||||
alias Berrypod.{Analytics, Cart, Pages}
|
||||
alias BerrypodWeb.R
|
||||
alias Berrypod.Images.Optimizer
|
||||
alias Berrypod.Products
|
||||
alias Berrypod.Products.{Product, ProductImage}
|
||||
@@ -15,11 +16,11 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
# Try to get product by slug, including discontinued products
|
||||
case Products.get_product_by_slug(slug, preload: [:variants, :images]) do
|
||||
nil ->
|
||||
{:noreply, push_navigate(socket, to: "/collections/all")}
|
||||
{:noreply, push_navigate(socket, to: R.collection("all"))}
|
||||
|
||||
%{visible: false, status: status} when status != "discontinued" ->
|
||||
# Hidden but not discontinued - redirect away
|
||||
{:noreply, push_navigate(socket, to: "/collections/all")}
|
||||
{:noreply, push_navigate(socket, to: R.collection("all"))}
|
||||
|
||||
product ->
|
||||
all_images =
|
||||
@@ -42,12 +43,12 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
if connected?(socket) and socket.assigns[:analytics_visitor_hash] do
|
||||
Analytics.track_event(
|
||||
"product_view",
|
||||
Map.put(BerrypodWeb.AnalyticsHook.attrs(socket), :pathname, "/products/#{slug}")
|
||||
Map.put(BerrypodWeb.AnalyticsHook.attrs(socket), :pathname, R.product(slug))
|
||||
)
|
||||
end
|
||||
|
||||
base = BerrypodWeb.Endpoint.url()
|
||||
og_url = base <> "/products/#{slug}"
|
||||
og_url = R.url(R.product(slug))
|
||||
og_image = og_image_url(all_images)
|
||||
|
||||
page = Pages.get_page("pdp")
|
||||
@@ -107,7 +108,7 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
Map.put(
|
||||
BerrypodWeb.AnalyticsHook.attrs(socket),
|
||||
:pathname,
|
||||
"/products/#{socket.assigns.product.slug}"
|
||||
R.product(socket.assigns.product.slug)
|
||||
)
|
||||
)
|
||||
end
|
||||
@@ -204,7 +205,7 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
opt_type.values
|
||||
|> Enum.map(fn value ->
|
||||
params = Map.put(selected_options, opt_type.name, value.title)
|
||||
{value.title, "/products/#{slug}?#{URI.encode_query(params)}"}
|
||||
{value.title, R.product(slug) <> "?#{URI.encode_query(params)}"}
|
||||
end)
|
||||
|> Map.new()
|
||||
|
||||
@@ -278,7 +279,7 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
|
||||
# ── JSON-LD and meta helpers ─────────────────────────────────────────
|
||||
|
||||
defp product_json_ld(product, url, image, base) do
|
||||
defp product_json_ld(product, url, image, _base) do
|
||||
category_slug =
|
||||
if product.category,
|
||||
do: product.category |> String.downcase() |> String.replace(" ", "-"),
|
||||
@@ -286,13 +287,13 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
|
||||
breadcrumbs =
|
||||
[
|
||||
%{"@type" => "ListItem", "position" => 1, "name" => "Home", "item" => base <> "/"},
|
||||
%{"@type" => "ListItem", "position" => 1, "name" => "Home", "item" => R.url(R.home())},
|
||||
product.category &&
|
||||
%{
|
||||
"@type" => "ListItem",
|
||||
"position" => 2,
|
||||
"name" => product.category,
|
||||
"item" => base <> "/collections/#{category_slug}"
|
||||
"item" => R.url(R.collection(category_slug))
|
||||
},
|
||||
%{
|
||||
"@type" => "ListItem",
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule BerrypodWeb.Shop.Pages.Search do
|
||||
import Phoenix.LiveView, only: [push_patch: 2]
|
||||
|
||||
alias Berrypod.{Pages, Search}
|
||||
alias BerrypodWeb.R
|
||||
|
||||
def init(socket, _params, _uri) do
|
||||
page = Pages.get_page("search")
|
||||
@@ -32,7 +33,7 @@ defmodule BerrypodWeb.Shop.Pages.Search do
|
||||
end
|
||||
|
||||
def handle_event("search_submit", %{"q" => query}, socket) do
|
||||
{:noreply, push_patch(socket, to: "/search?q=#{query}")}
|
||||
{:noreply, push_patch(socket, to: R.search() <> "?q=#{query}")}
|
||||
end
|
||||
|
||||
def handle_event(_event, _params, _socket), do: :cont
|
||||
|
||||
Reference in New Issue
Block a user