import React, { useState, useMemo, useEffect, useRef } from 'react'; import { FileText, Mail, Newspaper, ClipboardList, UserPlus, Package, Shield, Calendar, Search, X, ArrowLeft, Plus, Layers, Monitor, Tablet, Smartphone, CheckCircle2, ChevronLeft, ChevronRight, } from 'lucide-react'; import TemplatePreview from './TemplatePreview'; import { useTemplateCatalog } from '../extensions/templates'; import { applyFilters, Slot } from '../extensions/registry'; const categoryIcons = { all: FileText, contact: Mail, newsletter: Newspaper, survey: ClipboardList, register: UserPlus, wholesale: Package, warranty: Shield, booking: Calendar, }; /** * Template gallery. Templates come from the catalog (bundled + registerTemplates()). Extensions can add layout * switcher entries (`templates.layouts` filter: [{ id, label, icon, match(template, key) }]) and card badges (slot * `templates.cardBadges`, receives { template }). */ export default function TemplateGalleryScreen({ onBackToForms, onCreateForm, onCreateBlank, }) { const { templates: TEMPLATE_CONFIGURATIONS, categories, categoryMap: TEMPLATE_CATEGORY_MAP, preferred: PREFERRED_BY_CATEGORY } = useTemplateCatalog(); const layouts = applyFilters('templates.layouts', []) || []; const [selectedLayoutType, setSelectedLayoutType] = useState('all'); // 'all' or a layout id const [selectedCategory, setSelectedCategory] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); // the reference builder select-template: 9 templates per page, back to page 1 when the filters change. const TEMPLATES_PER_PAGE = 9; const [currentPage, setCurrentPage] = useState(1); const gridTopRef = useRef(null); // Modals const [previewTemplate, setPreviewTemplate] = useState(null); const [previewDevice, setPreviewDevice] = useState('desktop'); // 'desktop' | 'tablet' | 'mobile' const [createModalTemplate, setCreateModalTemplate] = useState(null); const [formTitle, setFormTitle] = useState(''); const [formDescription, setFormDescription] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const activeTemplates = useMemo(() => { return Object.entries(TEMPLATE_CONFIGURATIONS).filter(([, t]) => !t.isDisabled); }, [TEMPLATE_CONFIGURATIONS]); // Categories with at least one template ("All Templates" always). const TEMPLATE_CATEGORIES = categories.filter((c) => c.id === 'all' || activeTemplates.some(([key]) => (TEMPLATE_CATEGORY_MAP[key] || 'all') === c.id)); const filteredTemplates = useMemo(() => { return activeTemplates .filter(([key, template]) => { const layout = layouts.find((l) => l.id === selectedLayoutType); if (layout && !layout.match(template, key)) return false; const category = TEMPLATE_CATEGORY_MAP[key] || 'all'; const categoryMatch = selectedCategory === 'all' || category === selectedCategory; const searchMatch = (template.name || '').toLowerCase().includes(searchQuery.toLowerCase()) || (template.description || '').toLowerCase().includes(searchQuery.toLowerCase()); return categoryMatch && searchMatch; }) .sort(([keyA], [keyB]) => { const order = PREFERRED_BY_CATEGORY[selectedCategory] || PREFERRED_BY_CATEGORY.all || []; const indexA = order.indexOf(keyA); const indexB = order.indexOf(keyB); if (indexA !== -1 && indexB !== -1) return indexA - indexB; if (indexA !== -1) return -1; if (indexB !== -1) return 1; return 0; }); }, [activeTemplates, selectedLayoutType, selectedCategory, searchQuery, layouts, TEMPLATE_CATEGORY_MAP, PREFERRED_BY_CATEGORY]); useEffect(() => { setCurrentPage(1); }, [selectedLayoutType, selectedCategory, searchQuery]); const totalPages = Math.max(1, Math.ceil(filteredTemplates.length / TEMPLATES_PER_PAGE)); const safePage = Math.min(currentPage, totalPages); const firstIndex = (safePage - 1) * TEMPLATES_PER_PAGE; const pageTemplates = filteredTemplates.slice(firstIndex, firstIndex + TEMPLATES_PER_PAGE); const goToPage = (n) => { setCurrentPage(Math.min(Math.max(1, n), totalPages)); if (gridTopRef.current && gridTopRef.current.scrollIntoView) gridTopRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; const pageNumbers = () => { if (totalPages <= 7) return Array.from({ length: totalPages }, (_, i) => i + 1); const out = [1]; if (safePage > 3) out.push('...'); for (let n = Math.max(2, safePage - 1); n <= Math.min(totalPages - 1, safePage + 1); n++) out.push(n); if (safePage < totalPages - 2) out.push('...'); out.push(totalPages); return out; }; const activeCategoryName = (TEMPLATE_CATEGORIES.find((c) => c.id === selectedCategory) || {}).name; const handleOpenCreateModal = (key, template) => { setCreateModalTemplate({ key, ...template }); setFormTitle(template.name || 'New Form'); setFormDescription(template.description || ''); }; const handleConfirmCreate = () => { if (!formTitle.trim()) return; setIsSubmitting(true); try { onCreateForm({ name: formTitle.trim(), description: formDescription.trim(), template: createModalTemplate, }); } finally { setIsSubmitting(false); setCreateModalTemplate(null); } }; return (
{/* Top Header / Nav Bar */}

Select a Template

Choose from {activeTemplates.length} professionally designed templates or start fresh

{/* Main Body */}
{/* Controls: Search, Layout Filter, Categories */}
{/* Search Input */}
setSearchQuery(e.target.value)} className="w-full pl-9 pr-8 py-2 border border-slate-200 rounded-xl text-xs focus:ring-2 focus:ring-gray-900 focus:border-gray-900 bg-slate-50/50" /> {searchQuery && ( )}
{/* Layout Switcher (extension layouts) */} {layouts.length > 0 && (
{[{ id: 'all', label: 'All Types', icon: Layers, match: () => true }, ...layouts].map((layout) => { const LayoutIcon = layout.icon || FileText; const count = activeTemplates.filter(([key, t]) => layout.match(t, key)).length; return ( ); })}
)}
{/* Category Tabs */}
{TEMPLATE_CATEGORIES.map((cat) => { const isActive = selectedCategory === cat.id; const IconComp = cat.Icon || categoryIcons[cat.id] || FileText; return ( ); })}
{/* Results counter (the reference builder "Showing X-Y of N templates in …") */}
{filteredTemplates.length > 0 && (

Showing {firstIndex + 1}-{Math.min(firstIndex + TEMPLATES_PER_PAGE, filteredTemplates.length)} of {filteredTemplates.length} templates {selectedCategory !== 'all' && activeCategoryName ? ` in ${activeCategoryName}` : ''}

)} {/* Templates Grid */} {filteredTemplates.length === 0 ? (

No templates found

Try adjusting your search query or choosing a different category filter.

) : (
{pageTemplates.map(([key, template]) => { const catObj = TEMPLATE_CATEGORIES.find( (c) => c.id === (TEMPLATE_CATEGORY_MAP[key] || 'all') ); const CatIcon = catObj ? catObj.Icon || categoryIcons[catObj.id] || FileText : FileText; return (
{/* Thumbnail / Preview */}
{/* Body Content */}
{/* Badges */}
{catObj?.name || 'General'} {template.fields?.length || 0} fields

{template.name}

{template.description}

{/* Actions */}
); })}
)} {totalPages > 1 && ( )}
{/* Interactive Responsive Device Preview Modal */} {previewTemplate && (
{/* Modal Top Bar */}
{previewTemplate.name} {previewTemplate.fields?.length || 0} fields
{/* Device Viewport Selector */}
{/* Preview Viewport Canvas */}
)} {/* Form Title & Create Confirmation Modal */} {createModalTemplate && (
{/* Header */}

Name Your Form

Based on {createModalTemplate.name}

{/* Body */}
setFormTitle(e.target.value)} placeholder="e.g., Customer Inquiry Form" className="w-full px-3 py-2 text-xs border border-slate-300 rounded-lg focus:ring-2 focus:ring-slate-900 focus:border-slate-900" autoFocus />