Files
mini/IMPLEMENTATION_PLAN_VISUAL_BUILDER.md

11 KiB

🎨 Visual Builder Implementation Plan

Craft.js + AstroWind + Direct DB Shim

Objective: Build a "Squarespace-style" visual editor that saves directly to PostgreSQL via the Shim, without CMS overhead.


🏗️ ARCHITECTURE

┌─────────────────────────────────────────────────────────┐
│  PUBLIC SITE (SSR - Lightning Fast)                    │
│  https://[site-domain]/[slug]                          │
│  - Reads sites.config JSONB                            │
│  - Renders AstroWind components                        │
│  - Zero editor overhead                                │
└─────────────────────────────────────────────────────────┘
                        ▲
                        │ Reads config
                        │
┌─────────────────────────────────────────────────────────┐
│  POSTGRESQL (sites.config JSONB)                       │
│  {                                                      │
│    "template": "astrowind",                            │
│    "blocks": [                                         │
│      { "type": "Hero", "props": {...} },              │
│      { "type": "Features", "props": {...} }           │
│    ]                                                   │
│  }                                                     │
└─────────────────────────────────────────────────────────┘
                        ▲
                        │ Writes config via Shim
                        │
┌─────────────────────────────────────────────────────────┐
│  VISUAL EDITOR (React + Craft.js)                      │
│  https://[admin-domain]/admin/editor/[id]              │
│  - Drag-and-drop blocks                                │
│  - Live preview                                        │
│  - Saves to PostgreSQL via /api/shim/sites/save-config │
└─────────────────────────────────────────────────────────┘

📋 IMPLEMENTATION TASKS

Phase 1: Editor Infrastructure (60 min)

Task 1.1: Create Craft.js User Components

  • src/components/editor/blocks/HeroBlock.tsx - Wraps AstroWind Hero
  • src/components/editor/blocks/FeaturesBlock.tsx - Wraps Features
  • src/components/editor/blocks/ContentBlock.tsx - Wraps Content
  • src/components/editor/blocks/CTABlock.tsx - Wraps Call-to-Action
  • src/components/editor/blocks/index.ts - Exports all blocks

Task 1.2: Create Editor Canvas

  • src/components/editor/EditorCanvas.tsx - Main Craft.js editor
  • src/components/editor/ToolboxPanel.tsx - Drag-and-drop panel
  • src/components/editor/SettingsPanel.tsx - Block properties editor
  • src/components/editor/TopBar.tsx - Save/Preview buttons

Task 1.3: Create Editor Utilities

  • src/lib/editor/serializer.ts - Convert Craft.js → JSONB
  • src/lib/editor/deserializer.ts - Convert JSONB → Craft.js
  • src/lib/editor/templates.ts - Pre-built template configs

Phase 2: Template Factory (45 min)

Task 2.1: AstroWind Component Adapters

  • src/components/templates/astrowind/Hero.astro - Reads from JSONB
  • src/components/templates/astrowind/Features.astro - Reads from JSONB
  • src/components/templates/astrowind/Content.astro - Reads from JSONB
  • src/components/templates/astrowind/CTA.astro - Reads from JSONB

Task 2.2: Template Registry

  • src/lib/templates/registry.ts - Maps template names to components
  • src/lib/templates/schemas.ts - Zod schemas for block configs
  • src/lib/templates/defaults.ts - Default block configurations

Phase 3: API Routes (30 min)

Task 3.1: Editor API Routes

  • POST /api/shim/sites/save-config - Save JSONB to PostgreSQL
  • GET /api/shim/sites/[id]/config - Load JSONB for editor
  • POST /api/shim/sites/[id]/preview - Generate preview URL

Task 3.2: Template API Routes

  • GET /api/templates/list - Available templates
  • POST /api/templates/apply - Apply template to site

Phase 4: Editor Pages (30 min)

Task 4.1: Editor Route

  • src/pages/admin/editor/[id].astro - Main editor page
  • Token validation wrapper
  • Load site config from Shim

Task 4.2: Template Selector

  • src/pages/admin/templates.astro - Template gallery
  • Preview thumbnails
  • One-click apply

Phase 5: Public Rendering (30 min)

Task 5.1: Dynamic Template Renderer

  • src/pages/[...slug].astro - Reads sites.config and renders blocks
  • Block component resolver
  • SEO metadata injection

Task 5.2: Site Utilities

  • src/lib/templates/renderer.ts - Render blocks from JSONB
  • src/lib/templates/seo.ts - Extract SEO from config

🔧 CORE COMPONENTS

1. Editor Canvas (EditorCanvas.tsx)

import { Editor, Frame, Element } from '@craftjs/core';
import { HeroBlock, FeaturesBlock, ContentBlock } from './blocks';

export default function EditorCanvas({ initialState, siteId }) {
  const handleSave = async (json) => {
    await fetch(`/api/shim/sites/save-config`, {
      method: 'POST',
      body: JSON.stringify({ siteId, config: json })
    });
  };

  return (
    <Editor resolver={{ HeroBlock, FeaturesBlock, ContentBlock }}>
      <div className="flex h-screen">
        <ToolboxPanel />
        <div className="flex-1">
          <TopBar onSave={handleSave} />
          <Frame json={initialState}>
            <Element is="div" canvas>
              {/* Editable canvas */}
            </Element>
          </Frame>
        </div>
        <SettingsPanel />
      </div>
    </Editor>
  );
}

2. User Component Example (HeroBlock.tsx)

import { useNode } from '@craftjs/core';

export const HeroBlock = ({ title, subtitle, image, ctaText }) => {
  const { connectors: { connect, drag } } = useNode();

  return (
    <div ref={ref => connect(drag(ref))} className="hero">
      <h1>{title}</h1>
      <p>{subtitle}</p>
      {image && <img src={image} alt={title} />}
      <button>{ctaText}</button>
    </div>
  );
};

HeroBlock.craft = {
  props: {
    title: 'Hero Title',
    subtitle: 'Hero subtitle',
    image: '/placeholder.jpg',
    ctaText: 'Get Started'
  },
  rules: {
    canDrag: true,
    canDrop: false
  },
  related: {
    settings: HeroSettings // Property panel component
  }
};

3. Save Config API (save-config.ts)

import type { APIRoute } from 'astro';
import { updateSite } from '@/lib/shim/sites';

export const POST: APIRoute = async ({ request }) => {
  try {
    const { siteId, config } = await request.json();
    
    // Update site config via Shim
    await updateSite(siteId, { 
      config: JSON.stringify(config) 
    });
    
    return new Response(JSON.stringify({ success: true }), {
      status: 200
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500
    });
  }
};

4. Public Renderer ([...slug].astro)

---
import { getSiteByDomain } from '@/lib/shim/sites';
import { renderBlocks } from '@/lib/templates/renderer';

const site = await getSiteByDomain(Astro.url.hostname);
const config = site?.config || {};
const blocks = config.blocks || [];
---

<html>
  <body>
    {renderBlocks(blocks)}
  </body>
</html>

🎨 TEMPLATE FACTORY STRUCTURE

Default Templates

Template Blocks Use Case
Corporate Hero + Features + Stats + Team + CTA Business sites
Landing Hero + Benefits + Testimonials + Pricing + CTA SaaS landing pages
Blog Header + Posts Grid + Sidebar + Footer Content sites
Portfolio Hero + Projects Grid + About + Contact Personal branding

JSONB Structure

{
  "template": "astrowind",
  "theme": {
    "primaryColor": "#3B82F6",
    "font": "Inter"
  },
  "blocks": [
    {
      "id": "hero-1",
      "type": "Hero",
      "props": {
        "title": "Welcome to Our Site",
        "subtitle": "Build amazing things",
        "ctaText": "Get Started",
        "ctaLink": "/signup"
      }
    },
    {
      "id": "features-1",
      "type": "Features",
      "props": {
        "title": "Key Features",
        "items": [
          { "icon": "⚡", "title": "Fast", "description": "Lightning quick" },
          { "icon": "🔒", "title": "Secure", "description": "Bank-level security" }
        ]
      }
    }
  ]
}

🔒 SECURITY MODEL

  1. Editor Access:

    • Only accessible at /admin/editor/[id]
    • Requires GOD_MODE_TOKEN validation
    • Token checked in Astro middleware
  2. Save Operations:

    • All saves go through Shim (/api/shim/sites/save-config)
    • Zod validation on JSONB structure
    • Sanitize user input before SQL
  3. Public Rendering:

    • No editor JavaScript loaded
    • Pure SSR from JSONB config
    • No exposure of admin endpoints

📊 PERFORMANCE COMPARISON

Operation Traditional CMS God Mode Visual Builder
Load Editor ~2000ms (API + DB + Render) ~300ms (Direct DB)
Save Changes ~1500ms (API → CMS → DB) ~50ms (Shim → DB)
Public Page Load ~800ms (CMS overhead) ~10ms (Pure SSR)
Scale 100s of sites 10,000s of sites

🎯 SUCCESS CRITERIA

  1. Can drag-and-drop AstroWind blocks in /admin/editor/[id]
  2. Changes save directly to sites.config JSONB
  3. Public site re-renders instantly with new config
  4. Zero performance impact on public-facing pages
  5. Can manage 1000+ sites with different layouts
  6. Full Zod validation on all block configs
  7. SEO metadata auto-extracted from blocks

🚀 DEPLOYMENT CHECKLIST

  • Install Craft.js dependencies (npm install @craftjs/core @craftjs/utils)
  • Create all editor components
  • Create template adapters
  • Test save/load flow
  • Verify public rendering
  • Security audit (token validation)
  • Performance test (1000+ blocks)

📈 ROADMAP EXTENSIONS

Phase 6 (Future):

  • A/B Testing - Save multiple configs, split traffic
  • Version History - Keep history of config changes
  • Template Marketplace - Share templates between sites
  • AI Block Generator - Generate blocks from text prompts
  • Responsive Preview - Mobile/tablet/desktop view
  • Component Library - Custom reusable blocks