- 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>
16 KiB
Editor Panel Reorganisation
Status: In Progress
Problem
The current 3-tab editor panel (Page | Theme | Settings) has gaps and unintuitive organisation:
- Missing functionality — can't edit announcement bar text, social links, header/footer nav, footer about text
- Scattered concerns — shop name is in Theme (visual design) but it's business info; navigation settings are spread across multiple places
- Settings tab is underused — on system/product pages it's essentially read-only with links elsewhere
Vision
A user editing their shop should find everything in a logical place based on what they're trying to do:
- "I want to change what's on this page" → Page tab
- "I want to change how my site looks" → Theme tab
- "I want to change site-wide content" → Site tab
Proposed Structure
Page tab (content on this specific page)
For all pages:
- Block editor (add, reorder, duplicate, delete, edit)
- Undo/redo
For custom CMS pages only (inline, not separate Settings tab):
- Page title
- URL slug
- Meta description
- Published toggle
- Show in navigation toggle
- Navigation label & position (if show_in_nav)
For system pages:
- Block editor only (title/slug not editable)
- "Reset to defaults" for resettable pages
For product/collection pages:
- Read-only info with link to admin
Theme tab (visual design — how it looks)
Keep existing structure but remove shop name and logo (move to Site tab):
Quick settings:
- Preset grid (8 themes)
- Accent colour, hover colour, sale colour
Advanced (Customise accordion):
- Typography: font style, size, heading weight
- Colours: mood (warm, neutral, cool, dark)
- Layout: grid columns, density, header layout, sticky header
- Shape: corners, shadows, button style
- Products: content width, image aspect, text alignment, hover image, show prices
- Product page: trust badges, reviews, related products
Site tab (site-wide content — what appears everywhere)
New tab replacing "Settings". Contains everything that appears on every page:
Branding:
- Shop name
- Logo (upload, show/hide, size, recolour, colour)
- Favicon (upload)
- Header background image (upload, zoom, position)
Announcement bar:
- Show announcement bar (toggle)
- Announcement text (NEW — currently hardcoded)
- Announcement link URL (optional)
- Announcement style (info, sale, warning)
Header navigation:
- Editable list of nav items
- Each item: label, link (page picker or URL), position
- "Add nav item" button
Footer:
- About text (NEW — the footer blurb)
- Copyright text (NEW — currently auto-generated)
- Show newsletter signup (toggle)
Footer navigation:
- Editable list of footer nav items ("Help" section)
- Each item: label, link, position
Social links:
- Editable list of social links (NEW)
- Each link: platform (dropdown), URL
- Reorder via drag or arrows
Data Model Changes
New fields in Settings
# Announcement bar
:announcement_text, :string, default: ""
:announcement_link, :string, default: ""
:announcement_style, :string, default: "info" # info | sale | warning
# Footer content
:footer_about, :string, default: ""
:footer_copyright, :string, default: "" # empty = auto-generate
:show_newsletter, :boolean, default: true
New schema: SocialLink
schema "social_links" do
field :platform, :string # instagram, bluesky, mastodon, twitter, youtube, tiktok, etc.
field :url, :string
field :position, :integer
timestamps()
end
New schema: NavItem
schema "nav_items" do
field :location, :string # "header" | "footer"
field :label, :string
field :url, :string # can be relative (/about) or absolute
field :page_id, :binary_id # optional, links to a Page
field :position, :integer
timestamps()
end
Migration Path
Phase 1: Data model + Site tab skeleton (3h)
- Add new Settings fields (announcement_text, footer_about, etc.)
- Create SocialLink schema and migration
- Create NavItem schema and migration
- Seed default nav items from current hardcoded values
- Seed default social links from current hardcoded values
- Create Site tab component (skeleton)
Phase 2: Announcement bar (1.5h)
- Add announcement text/link/style inputs to Site tab
- Update shop header to read from Settings instead of hardcoded
- Style variants for info/sale/warning
Phase 3: Social links editor (2h)
- Add social links editor to Site tab (list with add/remove/reorder)
- Platform dropdown with icons
- Update footer to read from SocialLink table
- Delete hardcoded social links
Phase 4: Navigation editors (3h)
- Add header nav editor to Site tab
- Add footer nav editor to Site tab
- Page picker component (dropdown of published pages)
- Update header/footer to read from NavItem table
- Delete hardcoded nav arrays
Phase 5: Footer content (1h)
- Add footer about text input
- Add copyright text input (with placeholder showing auto-generated)
- Add newsletter toggle
- Update footer component to use these values
Phase 6: Move branding from Theme to Site (1.5h)
- Move shop name input to Site tab
- Move logo settings to Site tab
- Move favicon settings to Site tab
- Move header background settings to Site tab
- Update Theme tab to remove these sections
- Ensure Theme tab still feels complete (may need UI polish)
Phase 7: Merge Settings into Page (1h)
- For custom pages: move title/slug/meta/published/nav settings inline into Page tab
- Remove Settings tab entirely
- Update tab switching logic
Phase 8: Polish and testing (2h)
- Responsive design for Site tab
- Empty states
- Validation and error handling
- Test all combinations (custom page, system page, product page, collection)
- Update onboarding/help text if needed
Task Breakdown
| # | Task | Depends | Est | Status |
|---|---|---|---|---|
| 1 | Data model: new Settings fields + migrations | — | 1h | done |
| 2 | Data model: SocialLink schema + seed defaults | 1 | 45m | done |
| 3 | Data model: NavItem schema + seed defaults | 1 | 45m | done |
| 4 | Site tab skeleton component | 1 | 30m | done |
| 5 | Announcement bar: editor UI | 4 | 45m | done |
| 6 | Announcement bar: read from Settings | 5 | 30m | done |
| 7 | Announcement bar: style variants | 6 | 15m | done |
| 8 | Social links: editor UI (list, add, remove, reorder) | 4 | 1.5h | done |
| 9 | Social links: read from database | 8 | 30m | done |
| 10 | Header nav: editor UI | 4 | 1h | done |
| 11 | Header nav: page picker component | 10 | 30m | done |
| 12 | Header nav: read from database | 11 | 30m | done |
| 13 | Footer nav: editor UI | 10 | 30m | done |
| 14 | Footer nav: read from database | 13 | 30m | done |
| 15 | Footer content: about, copyright, newsletter toggle | 4 | 45m | done |
| 16 | Footer: read new fields | 15 | 30m | done |
| 17 | Move branding settings from Theme to Site | 4 | 1h | planned |
| 18 | Theme tab: remove branding, polish remaining | 17 | 30m | planned |
| 19 | Merge page settings into Page tab | — | 45m | planned |
| 20 | Remove Settings tab | 19 | 15m | planned |
| 21 | Polish: responsive, empty states, validation | 1-20 | 1.5h | planned |
| 22 | Testing: all page types, edge cases | 21 | 30m | planned |
Total estimate: ~15h
Implementation Notes
Completed: Social Links (Tasks 8-9)
The social links feature supports 40+ platforms grouped into 9 categories:
- Social (Instagram, Threads, TikTok, Facebook, Twitter, Snapchat, LinkedIn)
- Video & streaming (YouTube, Twitch, Vimeo, Kick, Rumble)
- Music & podcasts (Spotify, SoundCloud, Bandcamp, Apple Podcasts)
- Creative (Pinterest, Behance, Dribbble, Tumblr, Medium)
- Support & sales (Patreon, Ko-fi, Etsy, Gumroad, Substack)
- Federated (Mastodon, Pixelfed, Bluesky, PeerTube, Lemmy, Matrix)
- Developer (GitHub, GitLab, Codeberg, SourceHut, Reddit)
- Messaging (Discord, Telegram, Signal, WhatsApp)
- Other (Linktree, RSS, Website, Custom)
URL handling:
normalize_url/1trims whitespace and addshttps://to bare domains- Preserves existing protocols (
http://,https://, app deep links liketg://,spotify:) - Preserves other URI schemes (
mailto:,tel:,rss://)
Platform detection:
detect_platform/1auto-detects platform from pasted URLs- Works with scheme-based detection (e.g.,
tg://→ telegram,spotify:→ spotify) - Works with host-based detection (e.g.,
github.com→ github,bsky.app→ bluesky) - Handles subdomain patterns (e.g.,
artist.bandcamp.com→ bandcamp) - Returns "custom" for unknown domains
Data flow:
- Database →
Site.list_social_links/0→ raw structs loaded into original and current state - Shop display →
Site.social_links_for_shop/0→ filtered (no empty URLs), formatted maps - Editor preview →
format_social_links_for_shop/1in layout.ex → same filtering - Social links card block now uses database data via
assigns[:social_links] - Changes preview immediately, persist on Save button via
sync_social_links/2
Files:
lib/berrypod/site.ex- Site context with social links CRUDlib/berrypod/site/social_link.ex- Schema, URL normalization, platform detectionlib/berrypod_web/components/shop_components/site_editor.ex- Editor UIlib/berrypod_web/page_editor_hook.ex- Event handlerstest/berrypod/site_test.exs- 35 tests covering all functionality
Completed: Navigation Editors (Tasks 10-14)
Header and footer navigation items are managed through a unified nav_editor component.
Features:
- Add/edit/delete/reorder nav items
- Label input with live preview
- Link type selector: Page (dropdown) or Custom URL (text input)
- Page picker shows linkable system pages (home, about, delivery, privacy, terms, contact) and published custom pages
- Move up/down buttons with disabled state at boundaries
- Changes preview immediately, persist on Save button
Event handlers:
site_add_nav_item- Creates temporary item in local statesite_update_nav_item- Updates label, URL, or page selection in local state based on_targetsite_remove_nav_item- Removes item from local statesite_move_nav_item- Reorders items in local state
Save/revert behavior:
- All nav item and social link changes participate in the unified dirty state system
compute_site_dirty/1compares current state against original state loaded at editor open- On Save:
sync_nav_items/2andsync_social_links/2diff current vs original and create/update/delete as needed - On Discard: reverts to original state from
site_editor_original - Navigation warning modal appears when navigating with unsaved changes
Data flow:
- Database →
Site.list_nav_items/1→ raw structs loaded into original and current state - Shop display →
Site.nav_items_for_shop/1→ formatted maps with computed href/slug - Header/footer components already wired to use
@header_nav_items/@footer_nav_items
Files:
lib/berrypod/site.ex- NavItem CRUD and reorder functionslib/berrypod/site/nav_item.ex- Schema with location, label, url, page_id, positionlib/berrypod_web/components/shop_components/site_editor.ex-nav_editorcomponentlib/berrypod_web/page_editor_hook.ex- Event handlers, sync functions, dirty stateassets/css/admin/components.css- Styles for nav editor
UI Wireframes
Site tab layout
┌─────────────────────────────────────┐
│ [Page] [Theme] [Site] │ ← tabs
├─────────────────────────────────────┤
│ │
│ ▼ Branding │ ← collapsible section
│ Shop name: [____________] │
│ Logo: [Upload] [Remove] │
│ □ Show logo □ Show shop name │
│ Logo size: ●────────○ 48px │
│ □ Recolour logo │
│ Favicon: [Upload] │
│ │
│ ▼ Announcement bar │
│ □ Show announcement bar │
│ Text: [Free shipping over £50___] │
│ Link: [/delivery______________] │
│ Style: ○ Info ● Sale ○ Warning │
│ │
│ ▼ Header navigation │
│ ┌─────────────────────────────┐ │
│ │ Shop /products ↕ │ │
│ │ About /about ↕ │ │
│ │ Contact /contact ↕ │ │
│ └─────────────────────────────┘ │
│ [+ Add nav item] │
│ │
│ ▼ Footer │
│ About text: │
│ [Handmade with love in London___] │
│ [_______________________________] │
│ Copyright: [________________] │
│ (Leave blank for "© 2026 Shop") │
│ □ Show newsletter signup │
│ │
│ ▼ Footer navigation │
│ ┌─────────────────────────────┐ │
│ │ Delivery /delivery ↕ │ │
│ │ Returns /returns ↕ │ │
│ │ Privacy /privacy ↕ │ │
│ └─────────────────────────────┘ │
│ [+ Add nav item] │
│ │
│ ▼ Social links │
│ ┌─────────────────────────────┐ │
│ │ 📷 Instagram @myshop ↕ │ │
│ │ 🦋 Bluesky @my.bsky ↕ │ │
│ │ ☕ Ko-fi /myshop ↕ │ │
│ └─────────────────────────────┘ │
│ [+ Add social link] │
│ │
└─────────────────────────────────────┘
Page tab with inline settings (custom page)
┌─────────────────────────────────────┐
│ [Page] [Theme] [Site] │
├─────────────────────────────────────┤
│ │
│ ▼ Page settings │ ← new section for custom pages
│ Title: [About Us_______________] │
│ URL: berrypod.com/[about_______] │
│ Meta description: │
│ [Learn about our story...______] │
│ □ Published │
│ □ Show in navigation │
│ │
│ ▼ Content blocks │
│ [Hero block] [↑][↓][×]│
│ [Text block] [↑][↓][×]│
│ [Image block] [↑][↓][×]│
│ [+ Add block] │
│ │
│ [↩ Undo] [↪ Redo] │
│ │
└─────────────────────────────────────┘
Open Questions
-
Should header/footer nav support nested items (dropdowns)? — Probably not for v1, keep it flat.
-
Should social links include "custom" option? — Yes, allow custom label + URL for platforms we don't have icons for.
-
What happens to existing hardcoded nav when we migrate? — Seed the database with current values, delete hardcoded arrays.
-
Should announcement bar support scheduling? — Defer to profit-aware-pricing plan (task 71) which already includes this.
-
Should we keep the Settings tab for "advanced" page settings? — No, inline is simpler. Power users can use admin pages panel.