add nav editors to Site tab with live preview
All checks were successful
deploy / deploy (push) Successful in 3m27s
All checks were successful
deploy / deploy (push) Successful in 3m27s
- Add header and footer nav editors to Site tab with drag-to-reorder, add/remove items, and destination picker (pages, collections, external) - Live preview updates as you edit nav items - Remove legacy /admin/navigation page and controller (was saving to Settings table, now uses nav_items table) - Update error_html.ex and pages/editor.ex to load nav from nav_items table - Update link_scanner to read from nav_items table, edit path now /?edit=site - Add Site.default_header_nav/0 and default_footer_nav/0 for previews/errors - Remove fallback logic from theme_hook.ex (database is now source of truth) - Seed default nav items and social links during setup Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,14 +149,8 @@ defmodule BerrypodWeb.ErrorHTML do
|
||||
|> Map.put(:cart_count, 0)
|
||||
|> Map.put(:cart_subtotal, "£0.00")
|
||||
|> Map.put(:page, page)
|
||||
|> Map.put(
|
||||
:header_nav_items,
|
||||
load_nav("header_nav", &BerrypodWeb.ThemeHook.default_header_nav/0)
|
||||
)
|
||||
|> Map.put(
|
||||
:footer_nav_items,
|
||||
load_nav("footer_nav", &BerrypodWeb.ThemeHook.default_footer_nav/0)
|
||||
)
|
||||
|> Map.put(:header_nav_items, load_nav_items("header"))
|
||||
|> Map.put(:footer_nav_items, load_nav_items("footer"))
|
||||
|
||||
# Load block data (e.g. products for featured_products block)
|
||||
extra = safe_load(fn -> Pages.load_block_data(page.blocks, assigns) end) || %{}
|
||||
@@ -223,10 +217,15 @@ defmodule BerrypodWeb.ErrorHTML do
|
||||
end
|
||||
end
|
||||
|
||||
defp load_nav(key, default_fn) do
|
||||
case safe_load(fn -> Settings.get_setting(key) end) do
|
||||
items when is_list(items) -> items
|
||||
_ -> default_fn.()
|
||||
# Load nav items from database, falling back to defaults if DB fails
|
||||
# (error pages might render when database is unavailable)
|
||||
defp load_nav_items(location) do
|
||||
case safe_load(fn -> Berrypod.Site.nav_items_for_shop(location) end) do
|
||||
items when is_list(items) and items != [] -> items
|
||||
_ -> default_nav_items(location)
|
||||
end
|
||||
end
|
||||
|
||||
defp default_nav_items("header"), do: Berrypod.Site.default_header_nav()
|
||||
defp default_nav_items("footer"), do: Berrypod.Site.default_footer_nav()
|
||||
end
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
defmodule BerrypodWeb.NavigationController do
|
||||
@moduledoc """
|
||||
No-JS fallback for navigation form submission.
|
||||
|
||||
With JS enabled, the LiveView handles everything. Without JS,
|
||||
the form POSTs here and we redirect back to the LiveView page.
|
||||
"""
|
||||
use BerrypodWeb, :controller
|
||||
|
||||
alias Berrypod.Settings
|
||||
|
||||
def save(conn, %{"header_nav" => header_json, "footer_nav" => footer_json}) do
|
||||
with {:ok, header_items} <- Jason.decode(header_json),
|
||||
{:ok, footer_items} <- Jason.decode(footer_json) do
|
||||
all_items = header_items ++ footer_items
|
||||
errors = validate_nav_items(all_items)
|
||||
|
||||
if errors == [] do
|
||||
Settings.put_setting("header_nav", header_items, "json")
|
||||
Settings.put_setting("footer_nav", footer_items, "json")
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Navigation saved")
|
||||
|> redirect(to: ~p"/admin/navigation")
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, Enum.join(errors, ". "))
|
||||
|> redirect(to: ~p"/admin/navigation")
|
||||
end
|
||||
else
|
||||
{:error, _} ->
|
||||
conn
|
||||
|> put_flash(:error, "Invalid navigation data")
|
||||
|> redirect(to: ~p"/admin/navigation")
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_nav_items(items) do
|
||||
items
|
||||
|> Enum.flat_map(fn item ->
|
||||
cond do
|
||||
item["label"] == "" || item["label"] == nil ->
|
||||
["All links need a label"]
|
||||
|
||||
item["href"] == "" || item["href"] == nil ->
|
||||
["\"#{item["label"]}\" needs a destination"]
|
||||
|
||||
item["external"] == true || is_external_url?(item["href"]) ->
|
||||
if valid_url?(item["href"]) do
|
||||
[]
|
||||
else
|
||||
["\"#{item["label"]}\" has an invalid URL"]
|
||||
end
|
||||
|
||||
true ->
|
||||
[]
|
||||
end
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp valid_url?(url) when is_binary(url) do
|
||||
case URI.parse(url) do
|
||||
%URI{scheme: scheme, host: host}
|
||||
when scheme in ["http", "https"] and is_binary(host) and host != "" ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_url?(_), do: false
|
||||
|
||||
defp is_external_url?(nil), do: false
|
||||
defp is_external_url?(""), do: false
|
||||
|
||||
defp is_external_url?(href) do
|
||||
String.starts_with?(href, "http://") || String.starts_with?(href, "https://")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user