'use client' import React from 'react' import { LandingObject, LandingSection, LandingHeroSection, LandingTextAndImageSection, LandingLogos, LandingPeople, LandingBackground, LandingButton, LandingHeading, LandingImage, LandingFeaturedCourses } from './landing_types' import { Plus, Eye, ArrowUpDown, Trash2, GripVertical, LayoutTemplate, ImageIcon, Users, Award, ArrowRight, Edit, Link, Upload, Save, BookOpen } from 'lucide-react' import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' import { Input } from "@components/ui/input" import { Textarea } from "@components/ui/textarea" import { Label } from "@components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select" import { Button } from "@components/ui/button" import { useOrg } from '@components/Contexts/OrgContext' import { useLHSession } from '@components/Contexts/LHSessionContext' import { updateOrgLanding } from '@services/organizations/orgs' import { getOrgCourses } from '@services/courses/courses' import toast from 'react-hot-toast' import useSWR from 'swr' const SECTION_TYPES = { hero: { icon: LayoutTemplate, label: 'Hero', description: 'Add a hero section with heading and call-to-action' }, 'text-and-image': { icon: ImageIcon, label: 'Text & Image', description: 'Add a section with text and an image' }, logos: { icon: Award, label: 'Logos', description: 'Add a section to showcase logos' }, people: { icon: Users, label: 'People', description: 'Add a section to highlight team members' }, 'featured-courses': { icon: BookOpen, label: 'Courses', description: 'Add a section to showcase selected courses' } } as const const PREDEFINED_GRADIENTS = { 'sunrise': { colors: ['#fef9f3', '#ffecd2'] as Array, direction: '45deg' }, 'mint-breeze': { colors: ['#f0fff4', '#dcfce7'] as Array, direction: '45deg' }, 'lavender-mist': { colors: ['#faf5ff', '#f3e8ff'] as Array, direction: '45deg' }, 'ocean-spray': { colors: ['#f0f9ff', '#e0f2fe'] as Array, direction: '45deg' }, 'peach-cream': { colors: ['#fff7ed', '#ffedd5'] as Array, direction: '45deg' } } as const const GRADIENT_DIRECTIONS = { '45deg': '↗️ Top Right', '90deg': '⬆️ Top', '135deg': '↖️ Top Left', '180deg': '⬅️ Left', '225deg': '↙️ Bottom Left', '270deg': '⬇️ Bottom', '315deg': '↘️ Bottom Right', '0deg': '➡️ Right' } as const const getSectionDisplayName = (section: LandingSection) => { return SECTION_TYPES[section.type as keyof typeof SECTION_TYPES].label } const OrgEditLanding = () => { const org = useOrg() as any const session = useLHSession() as any const access_token = session?.data?.tokens?.access_token const [isLandingEnabled, setIsLandingEnabled] = React.useState(false) const [landingData, setLandingData] = React.useState({ sections: [], enabled: false }) const [selectedSection, setSelectedSection] = React.useState(null) const [isSaving, setIsSaving] = React.useState(false) // Initialize landing data from org config React.useEffect(() => { if (org?.config?.config?.landing) { const landingConfig = org.config.config.landing setLandingData({ sections: landingConfig.sections || [], enabled: landingConfig.enabled || false }) setIsLandingEnabled(landingConfig.enabled || false) } }, [org]) const addSection = (type: string) => { const newSection: LandingSection = createEmptySection(type) setLandingData(prev => ({ ...prev, sections: [...prev.sections, newSection] })) } const createEmptySection = (type: string): LandingSection => { switch (type) { case 'hero': return { type: 'hero', title: 'New Hero Section', background: { type: 'solid', color: '#ffffff' }, heading: { text: 'Welcome', color: '#000000', size: 'large' }, subheading: { text: 'Start your learning journey', color: '#666666', size: 'medium' }, buttons: [] } case 'text-and-image': return { type: 'text-and-image', title: 'New Text & Image Section', text: 'Add your content here', flow: 'left', image: { url: '', alt: '' }, buttons: [] } case 'logos': return { type: 'logos', logos: [] } case 'people': return { type: 'people', title: 'New People Section', people: [] } case 'featured-courses': return { type: 'featured-courses', title: 'Courses', courses: [] } default: throw new Error('Invalid section type') } } const updateSection = (index: number, updatedSection: LandingSection) => { const newSections = [...landingData.sections] newSections[index] = updatedSection setLandingData(prev => ({ ...prev, sections: newSections })) } const deleteSection = (index: number) => { setLandingData(prev => ({ ...prev, sections: prev.sections.filter((_, i) => i !== index) })) setSelectedSection(null) } const onDragEnd = (result: any) => { if (!result.destination) return const items = Array.from(landingData.sections) const [reorderedItem] = items.splice(result.source.index, 1) items.splice(result.destination.index, 0, reorderedItem) setLandingData(prev => ({ ...prev, sections: items })) setSelectedSection(result.destination.index) } const handleSave = async () => { if (!org?.id) { toast.error('Organization ID not found') return } setIsSaving(true) try { const res = await updateOrgLanding(org.id, { sections: landingData.sections, enabled: isLandingEnabled }, access_token) if (res.status === 200) { toast.success('Landing page saved successfully') } else { toast.error('Error saving landing page') } } catch (error) { toast.error('Error saving landing page') console.error('Error saving landing page:', error) } finally { setIsSaving(false) } } return (
{/* Enable/Disable Landing Page */}

Landing Page

Customize your organization's landing page

{isLandingEnabled && ( <> {/* Section List */}
{/* Sections Panel */}

Sections

{(provided) => (
{landingData.sections.map((section, index) => ( {(provided, snapshot) => (
{React.createElement(SECTION_TYPES[section.type as keyof typeof SECTION_TYPES].icon, { size: 16, className: "text-gray-600" })} {getSectionDisplayName(section)}
)}
))} {provided.placeholder}
)}
{/* Editor Panel */}
{selectedSection !== null ? ( updateSection(selectedSection, updatedSection)} /> ) : (
Select a section to edit or add a new one
)}
)}
) } interface SectionEditorProps { section: LandingSection onChange: (section: LandingSection) => void } const SectionEditor: React.FC = ({ section, onChange }) => { switch (section.type) { case 'hero': return case 'text-and-image': return case 'logos': return case 'people': return case 'featured-courses': return default: return
Unknown section type
} } const HeroSectionEditor: React.FC<{ section: LandingHeroSection onChange: (section: LandingHeroSection) => void }> = ({ section, onChange }) => { const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) { const reader = new FileReader() reader.onloadend = () => { onChange({ ...section, background: { type: 'image', image: reader.result as string } }) } reader.readAsDataURL(file) } } return (

Hero Section

{/* Title */}
onChange({ ...section, title: e.target.value })} placeholder="Enter section title" />
{/* Background */}
{section.background.type === 'solid' && (
onChange({ ...section, background: { ...section.background, color: e.target.value } })} className="w-20 h-10 p-1" /> onChange({ ...section, background: { ...section.background, color: e.target.value } })} placeholder="#ffffff" className="font-mono" />
)} {section.background.type === 'gradient' && (
{!Object.values(PREDEFINED_GRADIENTS).some( preset => preset.colors[0] === section.background.colors?.[0] && preset.colors[1] === section.background.colors?.[1] ) ? (
onChange({ ...section, background: { ...section.background, colors: [e.target.value, section.background.colors?.[1] || '#f0f0f0'] } })} className="w-20 h-10 p-1" /> onChange({ ...section, background: { ...section.background, colors: [e.target.value, section.background.colors?.[1] || '#f0f0f0'] } })} placeholder="#ffffff" className="font-mono" />
onChange({ ...section, background: { ...section.background, colors: [section.background.colors?.[0] || '#ffffff', e.target.value] } })} className="w-20 h-10 p-1" /> onChange({ ...section, background: { ...section.background, colors: [section.background.colors?.[0] || '#ffffff', e.target.value] } })} placeholder="#f0f0f0" className="font-mono" />
) : (
)}
)} {section.background.type === 'image' && (
{section.background.image && (
Background preview
)}
)}
{/* Heading */}
onChange({ ...section, heading: { ...section.heading, text: e.target.value } })} placeholder="Enter heading text" />
onChange({ ...section, heading: { ...section.heading, color: e.target.value } })} className="w-20 h-10 p-1" /> onChange({ ...section, heading: { ...section.heading, color: e.target.value } })} placeholder="#000000" className="font-mono" />
{/* Subheading */}
onChange({ ...section, subheading: { ...section.subheading, text: e.target.value } })} placeholder="Enter subheading text" />
onChange({ ...section, subheading: { ...section.subheading, color: e.target.value } })} className="w-20 h-10 p-1" /> onChange({ ...section, subheading: { ...section.subheading, color: e.target.value } })} placeholder="#666666" className="font-mono" />
{/* Buttons */}
{section.buttons.map((button, index) => (
{ const newButtons = [...section.buttons] newButtons[index] = { ...button, text: e.target.value } onChange({ ...section, buttons: newButtons }) }} placeholder="Button text" />
{ const newButtons = [...section.buttons] newButtons[index] = { ...button, color: e.target.value } onChange({ ...section, buttons: newButtons }) }} className="w-10 h-8 p-1" /> { const newButtons = [...section.buttons] newButtons[index] = { ...button, background: e.target.value } onChange({ ...section, buttons: newButtons }) }} className="w-10 h-8 p-1" />
{ const newButtons = [...section.buttons] newButtons[index] = { ...button, link: e.target.value } onChange({ ...section, buttons: newButtons }) }} placeholder="Button link" />
))} {section.buttons.length < 2 && ( )}
) } const TextAndImageSectionEditor: React.FC<{ section: LandingTextAndImageSection onChange: (section: LandingTextAndImageSection) => void }> = ({ section, onChange }) => { return (

Text & Image Section

{/* Title */}
onChange({ ...section, title: e.target.value })} placeholder="Enter section title" />
{/* Text */}