import React, { useState } from 'react'; import { AuthenticatedLayout } from '../layout/AuthenticatedLayout'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; import { Button } from '../ui/button'; import { Input } from '../ui/input'; import { Label } from '../ui/label'; import { Textarea } from '../ui/textarea'; import { Badge } from '../ui/badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '../ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../ui/select'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu'; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '../ui/sheet'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../ui/alert-dialog'; import { Tabs, TabsContent, TabsList, TabsTrigger, } from '../ui/tabs'; import { Checkbox } from '../ui/checkbox'; import { Separator } from '../ui/separator'; import { Switch } from '../ui/switch'; import { toast } from "sonner@2.0.3"; import { Search, Plus, Download, MoreHorizontal, Edit, Eye, Send, Archive, RotateCcw, History, Globe, ChevronLeft, ChevronRight, ExternalLink, X, CheckCircle, AlertCircle, Monitor, Tablet, Smartphone, Calendar, Clock, FileText, Search as SearchIcon, Link, Image as ImageIcon } from 'lucide-react'; interface LandingPagesProps { onNavigate: (route: string) => void; onLogout: () => void; user: any; } interface LandingPage { id: number; pageTitle: string; template: string; linkedProgramme?: string; urlSlug: string; status: 'Draft' | 'Published' | 'Archived'; updated: string; owner: string; } interface Programme { id: number; name: string; status: 'Published' | 'Draft'; } interface PageVersion { id: number; version: string; status: 'Draft' | 'Published' | 'Archived'; updated: string; actor: string; } const statusOptions = [ { value: "all", label: "All" }, { value: "draft", label: "Draft" }, { value: "published", label: "Published" }, { value: "archived", label: "Archived" } ]; const templateOptions = [ "Executive Programme", "Course Landing", "Webinar Registration", "Generic" ]; const mockProgrammes: Programme[] = [ { id: 1, name: "Executive Leadership Programme", status: "Published" }, { id: 2, name: "Digital Transformation Course", status: "Published" }, { id: 3, name: "Strategic Planning Workshop", status: "Draft" } ]; const mockVersions: PageVersion[] = [ { id: 1, version: "v1.0", status: "Published", updated: "2024-01-10", actor: "Admin User" }, { id: 2, version: "v1.1", status: "Draft", updated: "2024-01-15", actor: "Admin User" } ]; const mockOwners = [ "Admin User", "Marketing Team", "Content Team" ]; export function LandingPages({ onNavigate, onLogout, user }: LandingPagesProps) { const [searchTerm, setSearchTerm] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [templateFilter, setTemplateFilter] = useState("all"); const [ownerFilter, setOwnerFilter] = useState("all"); const [dateRangeFrom, setDateRangeFrom] = useState(""); const [dateRangeTo, setDateRangeTo] = useState(""); const [selectedPages, setSelectedPages] = useState([]); // Sheet/Modal state const [isCreateEditSheetOpen, setIsCreateEditSheetOpen] = useState(false); const [isPreviewDrawerOpen, setIsPreviewDrawerOpen] = useState(false); const [isSEODrawerOpen, setIsSEODrawerOpen] = useState(false); const [isVersionHistoryOpen, setIsVersionHistoryOpen] = useState(false); const [isAuditSheetOpen, setIsAuditSheetOpen] = useState(false); const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); // Form state const [editingPage, setEditingPage] = useState(null); const [pageForm, setPageForm] = useState({ pageTitle: "", template: "", linkedProgramme: "none", urlSlug: "", heroTitle: "", heroDescription: "", ctaText: "", bodyContent: "" }); // SEO state const [seoForm, setSeoForm] = useState({ metaTitle: "", metaDescription: "", canonicalUrl: "", ogTitle: "", ogDescription: "", ogImage: "", robotsIndex: true, robotsFollow: true }); // Preview state const [previewDevice, setPreviewDevice] = useState<'desktop' | 'tablet' | 'mobile'>('desktop'); // Publish state const [schedulePublish, setSchedulePublish] = useState(false); const [publishDate, setPublishDate] = useState(""); const [publishTime, setPublishTime] = useState(""); // Confirmation state const [confirmAction, setConfirmAction] = useState<{ type: string; page?: LandingPage; version?: PageVersion } | null>(null); const breadcrumbs = [ { label: "Admin", href: "/dashboard" }, { label: "Landing-Pages" } ]; // Mock landing pages data (minimal for placeholder purposes) const mockPages: LandingPage[] = []; const filteredPages = mockPages.filter(page => { const matchesSearch = page.pageTitle.toLowerCase().includes(searchTerm.toLowerCase()) || page.urlSlug.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === "all" || page.status.toLowerCase() === statusFilter; const matchesTemplate = templateFilter === "all" || page.template === templateFilter; const matchesOwner = ownerFilter === "all" || page.owner === ownerFilter; return matchesSearch && matchesStatus && matchesTemplate && matchesOwner; }); const getStatusBadgeVariant = (status: string) => { switch (status) { case "Published": return "default"; case "Draft": return "secondary"; case "Archived": return "outline"; default: return "secondary"; } }; const generateUrlSlug = (title: string) => { return title .toLowerCase() .replace(/[^a-z0-9\s]/g, '') .replace(/\s+/g, '-') .trim(); }; const handleRowSelection = (pageId: number, checked: boolean) => { if (checked) { setSelectedPages([...selectedPages, pageId]); } else { setSelectedPages(selectedPages.filter(id => id !== pageId)); } }; const handleSelectAll = (checked: boolean) => { if (checked) { const allIds = filteredPages.map(page => page.id); setSelectedPages(allIds); } else { setSelectedPages([]); } }; const openCreatePage = () => { setEditingPage(null); setPageForm({ pageTitle: "", template: "", linkedProgramme: "none", urlSlug: "", heroTitle: "", heroDescription: "", ctaText: "", bodyContent: "" }); setIsCreateEditSheetOpen(true); }; const openEditPage = (page: LandingPage) => { setEditingPage(page); setPageForm({ pageTitle: page.pageTitle, template: page.template, linkedProgramme: page.linkedProgramme || "none", urlSlug: page.urlSlug, heroTitle: "", heroDescription: "", ctaText: "", bodyContent: "" }); setIsCreateEditSheetOpen(true); }; const handleSavePage = () => { if (!pageForm.pageTitle.trim()) { toast.error("Page title is required"); return; } if (!pageForm.template) { toast.error("Template is required"); return; } if (!pageForm.urlSlug.trim()) { toast.error("URL slug is required"); return; } toast.success("Landing page saved."); setIsCreateEditSheetOpen(false); }; const openPreview = (page: LandingPage) => { setPreviewDevice('desktop'); setIsPreviewDrawerOpen(true); }; const handlePublish = (page: LandingPage) => { if (schedulePublish && (!publishDate || !publishTime)) { toast.error("Please set a publish date and time"); return; } if (schedulePublish) { toast.success(`Landing page scheduled for publish on ${publishDate} at ${publishTime}.`); } else { toast.success("Published."); } setConfirmAction(null); setIsConfirmDialogOpen(false); }; const handleUnpublish = (page: LandingPage) => { toast.success("Unpublished."); setConfirmAction(null); setIsConfirmDialogOpen(false); }; const handleArchiveRestore = (page: LandingPage) => { if (page.status === "Archived") { toast.success("Landing page restored."); } else { setConfirmAction({ type: "archive", page }); setIsConfirmDialogOpen(true); } }; const confirmArchive = () => { toast.success("Landing page archived."); setIsConfirmDialogOpen(false); setConfirmAction(null); }; const openSEOEditor = (page: LandingPage) => { setSeoForm({ metaTitle: "", metaDescription: "", canonicalUrl: "", ogTitle: "", ogDescription: "", ogImage: "", robotsIndex: true, robotsFollow: true }); setIsSEODrawerOpen(true); }; const handleSaveSEO = () => { toast.success("SEO updated."); setIsSEODrawerOpen(false); }; const openVersionHistory = (page: LandingPage) => { setIsVersionHistoryOpen(true); }; const handleRollback = (version: PageVersion) => { setConfirmAction({ type: "rollback", version }); setIsConfirmDialogOpen(true); }; const confirmRollback = () => { if (confirmAction?.version) { const version = confirmAction.version; toast.success(`Rolled back to ${version.version}.`); } setIsConfirmDialogOpen(false); setConfirmAction(null); }; const openAuditTrail = (page: LandingPage) => { setIsAuditSheetOpen(true); }; const handleBulkAction = (action: string) => { const count = selectedPages.length; switch (action) { case "publish": toast.success(`${count} pages published.`); setSelectedPages([]); break; case "unpublish": toast.success(`${count} pages unpublished.`); setSelectedPages([]); break; case "archive": setConfirmAction({ type: "bulk-archive" }); setIsConfirmDialogOpen(true); break; case "restore": toast.success(`${count} pages restored.`); setSelectedPages([]); break; case "export": toast.success("Export started."); break; } }; const confirmBulkArchive = () => { const count = selectedPages.length; toast.success(`${count} pages archived.`); setSelectedPages([]); setIsConfirmDialogOpen(false); setConfirmAction(null); }; const renderHeader = () => (

Landing-Pages

Create and manage programmatic landing pages

); const renderToolbar = () => (
{/* Search */}
setSearchTerm(e.target.value)} className="pl-10 min-h-[44px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50" />
{/* Status Filter Chips */}
{statusOptions.map((option) => ( ))}
{/* Template Filter */} {/* Owner Filter */} {/* Date Range */}
setDateRangeFrom(e.target.value)} className="w-[140px] min-h-[44px]" placeholder="From" /> to setDateRangeTo(e.target.value)} className="w-[140px] min-h-[44px]" placeholder="To" />
); const renderBulkActions = () => { if (selectedPages.length === 0) return null; return (
{selectedPages.length} selected
); }; const renderTable = () => { if (filteredPages.length === 0) { return (

No landing pages yet — create one.

); } return (
0} onCheckedChange={handleSelectAll} aria-label="Select all pages" /> Page Title Type / Template Linked Programme URL / Slug Status Updated Owner Actions {filteredPages.map((page) => ( handleRowSelection(page.id, checked as boolean)} aria-label={`Select ${page.pageTitle}`} /> {page.template} {page.linkedProgramme || "—"}
/pages/{page.urlSlug}
{page.status} {page.updated} {page.owner} openEditPage(page)}> Edit openPreview(page)}> Preview { setConfirmAction({ type: page.status === 'Published' ? 'unpublish' : 'publish', page }); setIsConfirmDialogOpen(true); }}> {page.status === "Published" ? ( <> Unpublish ) : ( <> Publish )} openVersionHistory(page)}> Rollback handleArchiveRestore(page)}> {page.status === "Archived" ? ( <> Restore ) : ( <> Archive )} openSEOEditor(page)}> SEO & Snippets openAuditTrail(page)}> Audit
))}
{/* Pagination Footer */}
Showing {filteredPages.length} of {mockPages.length} pages
); }; const renderCreateEditSheet = () => ( {editingPage ? "Edit Landing Page" : "Create Landing Page"} {editingPage ? "Update landing page content and settings" : "Create a new landing page"}
{ const title = e.target.value; setPageForm({ ...pageForm, pageTitle: title, urlSlug: generateUrlSlug(title) }); }} placeholder="Enter page title" className="focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50" required />
setPageForm({...pageForm, urlSlug: e.target.value})} placeholder="url-friendly-slug" className="focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50" required />

Full URL: https://klc.edu/pages/{pageForm.urlSlug || "url-slug"}

Content Blocks

setPageForm({...pageForm, heroTitle: e.target.value})} placeholder="Main headline" className="focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50" />