add live page editor sidebar with collapsible UI
All checks were successful
deploy / deploy (push) Successful in 6m49s

Admins can now edit pages directly on the live shop by clicking the
pencil icon in the header. A sidebar slides in with block management
controls (add, remove, reorder, edit settings, save, reset, done).

Key features:
- PageEditorHook on_mount with handle_params/event/info hooks
- BlockEditor pure functions extracted from admin editor
- Shared BlockEditorComponents with event_prefix namespacing
- Collapsible sidebar: X closes it, header pencil reopens it
- Backdrop overlay dismisses sidebar on tap
- Conditional admin.css loading for logged-in users
- content_body block now portable (textarea setting + rich text fallback)

13 integration tests, 26 unit tests, 1370 total passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-27 16:22:35 +00:00
parent b340c24aa1
commit a039c8d53c
12 changed files with 1846 additions and 640 deletions

View File

@@ -51,7 +51,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
@layout_keys ~w(theme_settings logo_image header_image mode cart_items cart_count
cart_subtotal cart_total cart_drawer_open cart_status active_page error_page is_admin
search_query search_results search_open categories shipping_estimate
country_code available_countries)a
country_code available_countries editing editor_current_path editor_sidebar_open)a
@doc """
Extracts the assigns relevant to `shop_layout` from a full assigns map.
@@ -86,6 +86,9 @@ defmodule BerrypodWeb.ShopComponents.Layout do
attr :active_page, :string, required: true
attr :error_page, :boolean, default: false
attr :is_admin, :boolean, default: false
attr :editing, :boolean, default: false
attr :editor_current_path, :string, default: nil
attr :editor_sidebar_open, :boolean, default: true
attr :search_query, :string, default: ""
attr :search_results, :list, default: []
attr :search_open, :boolean, default: false
@@ -117,6 +120,9 @@ defmodule BerrypodWeb.ShopComponents.Layout do
mode={@mode}
cart_count={@cart_count}
is_admin={@is_admin}
editing={@editing}
editor_current_path={@editor_current_path}
editor_sidebar_open={@editor_sidebar_open}
/>
{render_slot(@inner_block)}
@@ -685,6 +691,9 @@ defmodule BerrypodWeb.ShopComponents.Layout do
attr :mode, :atom, default: :live
attr :cart_count, :integer, default: 0
attr :is_admin, :boolean, default: false
attr :editing, :boolean, default: false
attr :editor_current_path, :string, default: nil
attr :editor_sidebar_open, :boolean, default: true
def shop_header(assigns) do
~H"""
@@ -729,6 +738,23 @@ defmodule BerrypodWeb.ShopComponents.Layout do
</nav>
<div class="shop-actions">
<%!-- Pencil icon: enters edit mode, or re-opens sidebar if already editing --%>
<.link
:if={@is_admin && !@editing && @editor_current_path}
patch={"#{@editor_current_path}?edit=true"}
class="header-icon-btn"
aria-label="Edit page"
>
<.edit_pencil_svg />
</.link>
<button
:if={@is_admin && @editing && !@editor_sidebar_open}
phx-click="editor_toggle_sidebar"
class="header-icon-btn"
aria-label="Show editor sidebar"
>
<.edit_pencil_svg />
</button>
<.link
:if={@is_admin}
href="/admin"
@@ -933,6 +959,25 @@ defmodule BerrypodWeb.ShopComponents.Layout do
"""
end
defp edit_pencil_svg(assigns) do
~H"""
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
/>
</svg>
"""
end
defp open_cart_drawer_js do
Phoenix.LiveView.JS.push("open_cart_drawer")
end