// DEPLOY_CONFIG: {"triggers": [{"name": "notify_owner_on_new_signup", "on": "collection.add", "collection": "subscribers", "actions": [{"type": "email", "to": "{{site_owner_email}}", "subject": "New subscriber signup: {{name || email}}", "body": "A new subscriber has signed up.\n\nName: {{name}}\nEmail: {{email}}\nCreated At: {{created_at}}\n\nFull record:\n{{json}}"}]}], "secrets": [{"name": "site_owner_email", "description": "Email address of the site owner or team inbox that receives new subscriber notifications."}, {"name": "email_delivery_provider", "description": "Configured email delivery mechanism/credential (e.g. SMTP/API credentials) used to send notification emails."}]} import { useState, useEffect, useMemo } from 'react'; import { useCollection, renderChart } from '@deplixo/sdk'; import { EditorPanel } from './components/EditorPanel.jsx'; import { PreviewPanel } from './components/PreviewPanel.jsx'; // PROGRESS:sc_001:complete:Setting up landing page builder structure const THEME_COLLECTION = 'landing-page-theme'; const DEFAULT_THEME = { primary: '#BF360C', secondary: '#E64A19', accent: '#FF8F00', light: '#FFB74D', bg: '#FFF3E0', white: '#FFFFFF', text: '#3E2723', textLight: '#6D4C41', textMuted: '#8D6E63', border: '#FFCCBC', borderLight: '#FFE0B2', cardBg: '#FFFFFF', shadowColor: '191, 54, 12' }; const PRESET_THEMES = [ { id: 'warm', name: 'Warm Ember', preview: ['#BF360C', '#E64A19', '#FF8F00'], values: { primary: '#BF360C', secondary: '#E64A19', accent: '#FF8F00', light: '#FFB74D', bg: '#FFF3E0', white: '#FFFFFF', text: '#3E2723', textLight: '#6D4C41', textMuted: '#8D6E63', border: '#FFCCBC', borderLight: '#FFE0B2', cardBg: '#FFFFFF', shadowColor: '191, 54, 12' } }, { id: 'forest', name: 'Forest', preview: ['#1B5E20', '#388E3C', '#81C784'], values: { primary: '#1B5E20', secondary: '#388E3C', accent: '#66BB6A', light: '#A5D6A7', bg: '#F1F8E9', white: '#FFFFFF', text: '#1B2E1F', textLight: '#4E6B57', textMuted: '#6C8A74', border: '#C8E6C9', borderLight: '#E8F5E9', cardBg: '#FFFFFF', shadowColor: '27, 94, 32' } }, { id: 'ocean', name: 'Ocean', preview: ['#01579B', '#0288D1', '#4FC3F7'], values: { primary: '#01579B', secondary: '#0288D1', accent: '#4FC3F7', light: '#81D4FA', bg: '#E1F5FE', white: '#FFFFFF', text: '#102A43', textLight: '#486581', textMuted: '#627D98', border: '#B3E5FC', borderLight: '#E0F7FA', cardBg: '#FFFFFF', shadowColor: '1, 87, 155' } }, { id: 'violet', name: 'Violet', preview: ['#4A148C', '#7B1FA2', '#BA68C8'], values: { primary: '#4A148C', secondary: '#7B1FA2', accent: '#BA68C8', light: '#CE93D8', bg: '#F3E5F5', white: '#FFFFFF', text: '#2E1A47', textLight: '#5E4B7C', textMuted: '#7B6D9A', border: '#E1BEE7', borderLight: '#F3E5F5', cardBg: '#FFFFFF', shadowColor: '74, 20, 140' } } ]; function buildThemeVars(theme) { return { '--primary': theme.primary, '--secondary': theme.secondary, '--accent': theme.accent, '--light': theme.light, '--bg': theme.bg, '--white': theme.white, '--text': theme.text, '--text-light': theme.textLight, '--text-muted': theme.textMuted, '--border': theme.border, '--border-light': theme.borderLight, '--card-bg': theme.cardBg, '--shadow-sm': `0 1px 3px rgba(${theme.shadowColor}, 0.06)`, '--shadow-md': `0 4px 12px rgba(${theme.shadowColor}, 0.08)`, '--shadow-lg': `0 8px 30px rgba(${theme.shadowColor}, 0.12)` }; } function applyThemeToDocument(theme) { if (typeof document === 'undefined') return; const root = document.documentElement; const vars = buildThemeVars(theme); Object.entries(vars).forEach(([key, value]) => { root.style.setProperty(key, value); }); } function App() { const [activeTab, setActiveTab] = useState('editor'); const { items, loading, add, update } = useCollection('landing-page', { personal: true }); const { items: signupItems, loading: signupLoading } = useCollection('email-signups', { personal: true }); const { items: themeItems, loading: themeLoading, add: addTheme, update: updateTheme } = useCollection(THEME_COLLECTION, { personal: true }); const pageData = items.length > 0 ? items[0].value : null; const pageId = items.length > 0 ? items[0].id : null; const defaultData = { headline: '', subheadline: '', description: '', features: [], ctaText: 'Get Started', ctaUrl: '#', heroImageUrl: '' }; const defaultThemeRecord = { name: 'Current Theme', themeId: 'warm', values: PRESET_THEMES[0].values, custom: false, updatedAt: new Date().toISOString() }; useEffect(() => { if (!loading && items.length === 0) { add(defaultData); } }, [loading, items.length]); useEffect(() => { if (!themeLoading && themeItems.length === 0) { addTheme(defaultThemeRecord); } }, [themeLoading, themeItems.length]); const themeRecord = themeItems.length > 0 ? themeItems[0].value : defaultThemeRecord; const selectedThemeId = themeRecord?.themeId || 'warm'; const selectedThemeValues = themeRecord?.values || PRESET_THEMES[0].values; const selectedTheme = useMemo(() => ({ ...DEFAULT_THEME, ...selectedThemeValues }), [selectedThemeValues]); useEffect(() => { applyThemeToDocument(selectedTheme); }, [selectedTheme]); const handleUpdate = (newData) => { if (pageId) { update(pageId, { ...pageData, ...newData }); } }; const handleThemeChange = (themeId) => { const preset = PRESET_THEMES.find((theme) => theme.id === themeId); if (!preset) return; const nextTheme = { ...defaultThemeRecord, themeId: preset.id, name: preset.name, values: preset.values, custom: false, updatedAt: new Date().toISOString() }; if (themeItems.length > 0) { updateTheme(themeItems[0].id, nextTheme); } else { addTheme(nextTheme); } }; const handleCustomThemeChange = (key, value) => { const nextValues = { ...selectedThemeValues, [key]: value }; const nextTheme = { ...themeRecord, themeId: 'custom', name: 'Custom Theme', values: nextValues, custom: true, updatedAt: new Date().toISOString() }; if (themeItems.length > 0) { updateTheme(themeItems[0].id, nextTheme); } else { addTheme(nextTheme); } }; const signupChartData = useMemo(() => { const countsByDay = signupItems.reduce((acc, item) => { const value = item?.value || {}; const rawDate = value.createdAt || item.createdAt || item.updatedAt || item.id; const date = rawDate ? new Date(rawDate) : null; const key = date && !Number.isNaN(date.getTime()) ? date.toISOString().slice(0, 10) : 'Unknown'; acc[key] = (acc[key] || 0) + 1; return acc; }, {}); return Object.entries(countsByDay) .filter(([label]) => label !== 'Unknown') .sort(([a], [b]) => a.localeCompare(b)) .map(([label, value]) => ({ label, value })); }, [signupItems]); if (loading || !pageData || themeLoading) { return (
); } return (

🍂 Landing Forge

Theme
{PRESET_THEMES.map((theme) => ( ))}

Pick a preset or fine-tune your own colors. Changes apply instantly across the editor and preview.

{[ ['primary', 'Primary'], ['secondary', 'Secondary'], ['accent', 'Accent'], ['bg', 'Background'], ['text', 'Text'], ['border', 'Border'] ].map(([key, label]) => ( ))}
{activeTab === 'editor' && } {activeTab === 'preview' && } {activeTab === 'admin' && (

Signup activity over time

{signupLoading ? 'Loading signup data...' : signupChartData.length > 0 ? `${signupChartData.reduce((sum, point) => sum + point.value, 0)} total signups across ${signupChartData.length} day${signupChartData.length === 1 ? '' : 's'}.` : 'No signup data yet.'}

{signupLoading ? (
Preparing chart...
) : signupChartData.length > 0 ? ( ) : (
📈

No signups to chart yet

When email signups are stored in your collection, they will appear here over time.

)}
)}
); } function SignupChart({ data }) { const canvasRef = useState(null)[0]; const chartRef = useState(null)[0]; useEffect(() => { if (canvasRef && data.length > 0) { renderChart(canvasRef, { type: 'bar', data: { labels: data.map((d) => d.label), datasets: [ { label: 'Signups', data: data.map((d) => d.value) } ] } }); } }, [data, canvasRef, chartRef]); return { if (node) { chartRef.current = node; } }} />; } ReactDOM.createRoot(document.getElementById('root')).render();