# Org Landing Page Theme Picker + Unified Content Editor ## Status - Done (shipped) ## Context The org has 3 landing page theme templates (Minimal, Bold, Classic) in `packages/ui/` that all accept `OrgLandingPageProps` uniformly. However: 1. The templates are **only used in Storybook** — the actual public landing page (`landing-page.svelte`) renders its own custom legacy HTML 2. The stored `OrgLandingPageJson` is a complex format (header, aboutUs, faq, contact, mailinglist, etc.) that **doesn't align** with what the templates need 3. There is no theme selection UI 4. There is no edit flow like the course landing page (split-screen with live preview) This plan: simplifies the stored format to match `OrgLandingPageProps`, replaces the legacy public landing page renderer with the theme components, adds a theme picker in settings, and adds a split-screen content editor modeled after the course landing page editor. --- ## Simplified Data Model Replace `OrgLandingPageJson` (the DB-stored type) with a structure that maps 1:1 to `OrgLandingPageProps`: ```ts // apps/dashboard/src/lib/utils/types/org.ts export interface OrgLandingPageJson { theme: 'minimal' | 'bold' | 'classic'; hero: { heading: string; subheading: string; primaryAction: { label: string; href: string }; secondaryAction?: { label: string; href: string }; image?: string; }; navItems: Array<{ label: string; href: string }>; footerLinks: Array<{ label: string; href: string }>; footerText: string; } ``` Sections removed from old format: `header` (with banner/background/show toggles), `aboutUs`, `courses`, `faq`, `contact`, `mailinglist`, `customLinks`, `footer` (social links only). Fields from org data (not stored in `landingpage`): `orgName` → `org.name`, `logoUrl` → `org.avatarUrl`, `courses` → `orgApi.publicCourses`. --- ## Changes ### 1. Update `OrgLandingPageJson` type **File:** `apps/dashboard/src/lib/utils/types/org.ts` Replace the full type with the simplified version above. ### 2. Update settings store with new defaults **File:** `apps/dashboard/src/lib/features/settings/utils/store.ts` Replace `landingPageSettings` writable defaults with the new schema: ```ts { theme: 'minimal', hero: { heading: 'Master the Skills of Tomorrow', subheading: 'Join thousands of learners and accelerate your career.', primaryAction: { label: 'Explore Courses', href: '#courses' }, secondaryAction: { label: 'Learn More', href: '#about' }, image: '' }, navItems: [ { label: 'Courses', href: '#courses' }, { label: 'About', href: '#about' } ], footerLinks: [ { label: 'Terms', href: '#terms' }, { label: 'Privacy', href: '#privacy' } ], footerText: '© 2026 All rights reserved.' } ``` Also update (or delete and inline) the old store at: `apps/dashboard/src/lib/features/org/components/settings/landing-page-store.ts` (currently used by the legacy landing-page.svelte — this store will go away when we replace the renderer). ### 3. Redesign `landingpage.svelte` settings page — theme picker only **File:** `apps/dashboard/src/lib/features/settings/pages/landingpage.svelte` Remove all existing form fields. The page now shows only: - **Theme picker** (`Field.Set` "Choose a Theme"): 3 cards in `grid grid-cols-3 gap-4` using `RadioGroup.Root bind:value={$landingPageSettings.theme}`. Each card: - Inline SVG thumbnail (distinct per theme — see SVG specs below) - Theme name label - Radio selection state: `border-primary ring-2 ring-primary/20` vs `border-border` - `onValueChange={() => (hasUnsavedChanges = true)}` - **Save button** already in the route page header — saves only `theme` (plus full JSON as fallback) - **"Edit Content"** button below the theme picker (`Button variant="outline"`) → navigates to `/org/[slug]/settings/landingpage/edit` - `UnsavedChanges` component stays SVG thumbnail specs: - **Minimal**: white bg, light gray nav bar top, 2 text lines (heading/subheading), 2 course card outlines in a 2-col grid, thin footer line - **Bold**: dark `#111` bg top-half, large bold heading text block, course cards with image-left layout, dark inverted footer - **Classic**: dark navy `#1e3a5f` header bar, white body, centered heading, 3-col course card grid, minimal footer Subscriber fallback for old data (users who previously saved the old format): ```ts if (!landingpage.theme || !landingpage.hero) { // Set minimal defaults — discard old incompatible structure $landingPageSettings = { ...defaultLandingPageSettings }; } else { $landingPageSettings = landingpage; } ``` ### 4. New split-screen content editor route **File (new):** `apps/dashboard/src/routes/org/[slug]/settings/landingpage/edit/+page.svelte` Modeled after `apps/dashboard/src/routes/courses/[id]/landingpage/+page.svelte`. Layout: ```svelte