berrypod/docs/plans/unified-editing-mode.md
jamey 168b6ce76f
All checks were successful
deploy / deploy (push) Successful in 1m10s
implement unified on-site editor phases 1-2
Add theme editing to the existing PageEditorHook, enabling on-site
theme customisation alongside page editing. The editor panel now has
three tabs (Page, Theme, Settings) and can be collapsed while
keeping editing state intact.

- Add theme editing state and event handlers to PageEditorHook
- Add 3-tab UI with tab switching logic
- Add transparent overlay for click-outside dismiss
- Add mobile drag-to-resize with height persistence
- Fix animation replay on drag release (has-dragged class)
- Preserve panel height across LiveView re-renders
- Default to Page tab on editable pages, Theme otherwise
- Show unsaved changes indicator on FAB when panel collapsed
- Fix handle_event grouping warning in admin theme

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-09 09:01:21 +00:00

271 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Unified On-Site Editing Mode
Status: In Progress (Phase 1-2 complete)
Replace the separate admin theme editor (`/admin/theme`) with an on-site editing experience. When the admin clicks "Theme" or "Edit page", they're taken to the actual shop where a sliding panel allows them to edit either page content or theme settings — seeing changes live on the real site.
## Current Architecture
The codebase already has a robust pattern for on-site editing via the **PageEditorHook**:
- `PageEditorHook` attaches to all shop pages, initializing ~15 editor-related assigns
- `editor_sheet` component in `ShopComponents.Layout` renders a FAB + sliding panel
- `PageRenderer` conditionally renders editor UI when `@is_admin` is true
- Changes preview instantly; state persists across the session
The theme editor currently lives at `/admin/theme` with its own LiveView and fake preview.
## Approach
**Extend the existing editor infrastructure** to support both page editing and theme editing:
1. Add theme editing state to the existing hook system
2. Add a tab switcher to the editor panel (Page | Theme | Settings)
3. Extract theme editor UI into a reusable component
4. Route `/admin/theme` to shop homepage with `?edit=theme` param
## User Flow
1. Admin clicks "Theme" in admin sidebar → redirects to `/?edit=theme`
2. Shop homepage loads with theme editing panel open
3. Admin can switch between "Page", "Theme", and "Settings" tabs
4. Admin can navigate to other shop pages while keeping editor open
5. "Done" or "×" button returns to admin or closes panel
## Files to Modify
| File | Change |
|------|--------|
| `lib/berrypod_web/page_editor_hook.ex` | Add theme/settings editing assigns, tab state, event handlers |
| `lib/berrypod_web/page_renderer.ex` | Add tab UI to editor_sheet, dispatch to theme/page/settings content |
| `lib/berrypod_web/components/shop_components/layout.ex` | Update editor_sheet for 3-tab support, add new keys to layout_keys |
| `lib/berrypod_web/components/shop_components/theme_editor.ex` | **New** - Extracted theme editor panel component |
| `lib/berrypod_web/components/shop_components/settings_editor.ex` | **New** - Page/shop settings panel component |
| `lib/berrypod_web/router.ex` | Redirect /admin/theme to /?edit=theme |
| `assets/css/shop/editor.css` | **New** - Editor panel styling (extract from admin) |
| `assets/js/hooks/theme_editor.js` | **New** - ColorSync and other theme editor hooks |
## Implementation
### Phase 1: Theme Editor Hook Integration
Add theme editing state to `PageEditorHook`:
```elixir
# New assigns in on_mount
:theme_editing -> false
:theme_settings -> nil (loaded when theme_editing activates)
:theme_active_preset -> nil
:theme_logo_image -> nil
:theme_header_image -> nil
:theme_icon_image -> nil
:theme_contrast_warning -> :ok
:theme_customise_open -> false
# New event handlers
"theme_*" events -> handled by attach_hook similar to editor_* events
```
### Phase 2: Editor Panel Tabs
Add three tabs to the editor panel:
| Tab | Content | When available |
|-----|---------|----------------|
| **Page** | Block editor for custom pages | Only on editable pages (home, about, custom CMS pages) |
| **Theme** | Global styles, mood, typography, branding | Always |
| **Settings** | Page-specific settings (SEO, slug) or global shop settings | Always (content varies by page) |
Update `editor_sheet` component:
```heex
<div class="editor-tabs">
<button
class={["editor-tab", @editor_active_tab == :page && "active"]}
phx-click="editor_set_tab"
phx-value-tab="page"
disabled={!@page}
>
Page
</button>
<button
class={["editor-tab", @editor_active_tab == :theme && "active"]}
phx-click="editor_set_tab"
phx-value-tab="theme"
>
Theme
</button>
<button
class={["editor-tab", @editor_active_tab == :settings && "active"]}
phx-click="editor_set_tab"
phx-value-tab="settings"
>
Settings
</button>
</div>
<%= case @editor_active_tab do %>
<% :page -> %>
<.editor_sheet_content ... />
<% :theme -> %>
<.theme_editor_content ... />
<% :settings -> %>
<.settings_editor_content ... />
<% end %>
```
**Settings tab content varies by context:**
- On custom CMS pages: Page title, slug, SEO (meta title, description)
- On product pages: Read-only product info, link to admin product editor
- On collection pages: Collection settings, link to admin
- Global fallback: Shop name, description, favicon settings
### Phase 3: Extract Theme Editor Component
Create `lib/berrypod_web/components/shop_components/theme_editor.ex`:
- Extract the sidebar content from current `/admin/theme` template
- Reuse same event names (`update_setting`, `toggle_setting`, `apply_preset`, etc.)
- Support image uploads via `allow_upload` passed from hook
Key sections to extract:
- Presets grid
- Mood/typography/shape/density selectors
- Logo upload + settings
- Header background upload + settings
- Accent color pickers
- Customise accordion (advanced settings)
### Phase 3b: Create Settings Editor Component
Create `lib/berrypod_web/components/shop_components/settings_editor.ex`:
**Content varies by page type:**
| Page type | Settings shown |
|-----------|---------------|
| Custom CMS page | Page title, slug, visibility, SEO meta |
| Home page | Shop name, description, SEO defaults |
| Product page | Read-only product name, link to admin product editor |
| Collection page | Read-only collection name, link to admin |
| Cart/checkout | Shop policies, checkout settings link |
**Common sections:**
- Page SEO (meta title, description, social image)
- Favicon settings (short name, icon upload)
- Shop identity (name, description)
### Phase 4: Image Upload Handling
Image uploads need special handling since we're in a hook, not a LiveView:
Option A: **Delegate to the parent LiveView**
- Pass `@uploads` from the LiveView through assigns
- Hook events trigger upload handling in the LiveView
- Cleanest approach, matches existing pattern
Option B: **Use a LiveComponent for uploads**
- Wrap upload sections in a stateful LiveComponent
- Component handles its own uploads
- More isolated but adds complexity
**Recommended: Option A** — follow the pattern the page editor already uses.
### Phase 5: URL-Based Mode Activation
Handle `?edit=theme` query param:
```elixir
# In PageEditorHook handle_params hook
defp handle_params_hook(params, _uri, socket) do
socket =
case params["edit"] do
"theme" -> enter_theme_edit_mode(socket)
"page" -> enter_page_edit_mode(socket)
_ -> socket
end
{:cont, socket}
end
```
### Phase 6: Admin Routing
Update router to redirect theme link:
```elixir
# In admin scope, redirect /admin/theme to shop
get "/theme", Plug.Redirect, to: "/?edit=theme"
```
Or keep the admin theme page as a fallback/redirect:
```elixir
live "/theme", Admin.Theme.Redirect # Simple LiveView that redirects
```
## State Management
**Theme editing state persists across navigation** via hook attach:
- `attach_hook(:theme_params, :handle_params, &handle_params_hook/3)` tracks URL
- Theme settings stored in socket assigns
- CSS regenerated and assigned to `@generated_css` on each change
- ThemeHook already loads CSS; we just update the assign
**Dirty tracking:**
- Theme changes save immediately (current behavior) OR
- Add "unsaved" state like page editor (better UX but more work)
**Recommendation:** Keep immediate save for now (it's the existing behavior and simpler).
## CSS Considerations
The editor panel uses admin CSS classes. Options:
1. **Extract shared styles** into `shop/editor.css` loaded on all shop pages
2. **Namespace admin styles** so they work in shop context
3. **Use inline styles** (not recommended)
**Recommended:** Create `assets/css/shop/editor.css` with editor panel styles, include it in shop layout when `@is_admin`.
## What Happens to /admin/theme?
Options:
1. **Remove it entirely** — redirects to `/?edit=theme`
2. **Keep as fallback** — simple page that redirects or links to on-site editor
3. **Keep for non-visual settings** — things like favicon short name, SEO defaults
**Recommended:** Redirect to `/?edit=theme` for now. Can always add back a minimal settings page later if needed.
## Verification
1. Click "Theme" in admin → lands on shop homepage with theme panel open
2. Change mood → see colors update instantly on real site
3. Upload logo → appears in real header immediately
4. Navigate to product page → theme panel stays open
5. Switch to "Page" tab on homepage → shows page block editor
6. Switch to "Page" tab on product page → tab disabled (no editable blocks)
7. Click "Done" → returns to admin dashboard
8. Mobile: panel slides up from bottom, fully usable
## Phased Rollout
### MVP (Phases 1-3)
- Theme editing works on-site
- Basic tab UI (Page | Theme | Settings)
- All current theme settings functional
- No image uploads yet (use existing images)
### Full Feature (Phases 4-6)
- Image uploads working
- URL-based mode activation
- Admin redirect in place
- Polish and edge cases
## Future Enhancements (Not in Scope)
- Undo/redo for theme changes
- "Preview without saving" mode
- Theme presets preview before applying
- Multi-page preview from single location