Website Builder UX Improvements
Current State
Section titled “Current State”- “Open in Builder” button exists on each WebPage form
- Users must navigate to each page individually to edit it
- Preview only refreshes after clicking Save
- Builder is page-centric, not website-centric
Proposed Improvements
Section titled “Proposed Improvements”1. Website-Level Builder Access
Section titled “1. Website-Level Builder Access”Add “Open Website Builder” button on the Website form that opens the upgraded PageBuilder where users can:
- See all pages in the website (left sidebar)
- Switch between pages without leaving the builder
- Build the entire website from one interface
Decision: Upgrade existing PageBuilder (not a separate component)
2. Real-Time Preview Updates (Auto-update with Debounce)
Section titled “2. Real-Time Preview Updates (Auto-update with Debounce)”Currently: Preview refreshes only after Save Decision: Auto-update preview as user types (debounced 300-500ms)
Implementation approach:
- Add debounced
onChangehandlers to SectionSchemaRenderer fields - On change: Update local state immediately
- After debounce (300ms): Reload iframe to show changes
- Keep explicit Save button for persisting to database
Simple approach for initial implementation:
- Reload iframe on debounced change (no backend changes needed)
- Iframe reloads from database state + pending changes
- Works with existing templates without modification
Future optimization (Phase 4):
- Add
?preview=1&draft=<base64>to iframe URL - Backend reads draft data from query and uses it for rendering
- Avoids database writes for preview-only changes
3. Enhanced Page Builder UI
Section titled “3. Enhanced Page Builder UI”New Layout (Website Builder):
┌─────────────────────────────────────────────────────────────────┐│ Website: Main Website [Preview] [Save] [×] │├──────────┬──────────────────────────────────────┬───────────────┤│ PAGES │ │ SECTION ││ │ │ SETTINGS ││ • Home │ │ ││ • About │ LIVE PREVIEW │ [Form fields] ││ • Contact│ (iframe) │ ││ │ │ ││ [+Page] │ │ │├──────────┤ │ ││ SECTIONS │ │ ││ on page │ │ ││ │ │ ││ □ Hero │ │ ││ □ Feature│ │ ││ □ CTA │ │ ││ [+Section│ │ │└──────────┴──────────────────────────────────────┴───────────────┘Changes from current PageBuilder:
- Add “Pages” panel above “Sections” panel in left sidebar
- Clicking a page switches the preview and section list
- Track unsaved changes per page
- Warn before switching pages with unsaved changes
Implementation Files
Section titled “Implementation Files”Backend:
fullfinity/modules/website/models/website.py- Addaction_open_website_buildermethodfullfinity/modules/website/views/website_views.json- Add actionButton to Website form
Frontend (upgrade existing components):
app/src/components/Website/PageBuilder.jsx- Add page list sidebar, real-time previewapp/src/components/Website/PageBuilderPage.jsx- Support bothpageIdandwebsiteIdparamsapp/src/components/Website/SectionEditor.jsx- Add onChange callback for real-time updatesapp/src/components/Website/SectionSchemaRenderer.jsx- Add debounced onChange handlersapp/src/App.jsx- Add route for/app/website/builder/:websiteId
Real-Time Preview Implementation Details
Section titled “Real-Time Preview Implementation Details”SectionSchemaRenderer changes:
// Add debounced onChange callbackconst debouncedOnChange = useMemo( () => debounce((newSettings) => { onPreviewUpdate?.(newSettings); }, 300), [onPreviewUpdate]);
// Call on every field changeconst handleFieldChange = (field, value) => { const newSettings = { ...settings, [field]: value }; setLocalSettings(newSettings); debouncedOnChange(newSettings);};PageBuilder changes:
// Track draft settings separately from saved settingsconst [draftSettings, setDraftSettings] = useState({});
// Update preview URL to include draft dataconst previewUrl = useMemo(() => { const base = page?.slug ? `/${page.slug}` : ''; if (Object.keys(draftSettings).length > 0) { const draft = btoa(JSON.stringify(draftSettings)); return `${base}?preview=1&draft=${draft}`; } return base;}, [page?.slug, draftSettings]);Backend route changes (web_website.py):
async def _get_page(self, path: str): # ... existing code ...
# Check for preview mode with draft data if self._current_request: preview = self._current_request.query_params.get("preview") draft = self._current_request.query_params.get("draft") if preview and draft: # Store draft data for use in _render_block import base64 import json self._draft_settings = json.loads(base64.b64decode(draft))Phased Implementation
Section titled “Phased Implementation”Phase 1: Real-time preview (quick win)
- Add debounced onChange to SectionSchemaRenderer
- Pass changes up to PageBuilder via callback
- Reload iframe on debounced change
- Simple but effective improvement
Phase 2: Page switching in PageBuilder
- Add “Pages” panel to left sidebar (above Sections)
- Fetch all pages for the website
- Track current page, switch preview and sections on page click
- Warn about unsaved changes when switching
Phase 3: Website-level entry point
- Add
action_open_website_builderto Website model - Add route
/app/website/builder/:websiteId - Update PageBuilderPage to handle websiteId param
Phase 4 (Future): Draft preview optimization
- Add draft query param support to backend
- Avoid full database save for preview
- Better performance for rapid changes
Verification
Section titled “Verification”- Edit section setting → Preview updates within 500ms
- Switch between pages → Correct sections shown
- Unsaved changes warning when switching pages
- Save persists all changes to database
- Preview works in both page builder and website builder