'use client' import { useState, useEffect, useMemo, useCallback } from 'react' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '../ui/dialog' import { Button } from '../ui/button' import { Badge } from '../ui/badge' import { ScrollArea } from '../ui/scroll-area' import { Input } from '../ui/input' import { FileText, Loader2, Search } from 'lucide-react' export interface EmailTemplate { id: string name: string description: string | null subject: string blocks: string | null category: string | null isDefault: boolean useCount: number /** Alternative field name for subject */ subjectTemplate?: string /** Alternative field name for body */ bodyTemplate?: string } interface TemplatePickerProps { open: boolean onOpenChange: (open: boolean) => void onSelectTemplate: (template: EmailTemplate) => void /** Fetch templates from the API. Should return an array of templates. */ onLoadTemplates: () => Promise /** Optional callback to seed/create default templates when none exist */ onSeedTemplates?: () => Promise /** Optional category filter -- when set, only templates matching this category are shown */ categoryFilter?: string } // Normalize API response to ensure subject/blocks fields are populated function normalizeTemplates(raw: EmailTemplate[]): EmailTemplate[] { return raw.map((t) => ({ ...t, subject: t.subject || t.subjectTemplate || '', blocks: t.blocks || t.bodyTemplate || null, })) } export function TemplatePicker({ open, onOpenChange, onSelectTemplate, onLoadTemplates, onSeedTemplates, categoryFilter, }: TemplatePickerProps) { const [templates, setTemplates] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [search, setSearch] = useState('') const fetchTemplates = useCallback(async () => { try { setLoading(true) setError(null) const data = await onLoadTemplates() // If no templates and a seed function is provided, try seeding if ((!data || data.length === 0) && onSeedTemplates) { await onSeedTemplates() const retryData = await onLoadTemplates() setTemplates(normalizeTemplates(retryData || [])) return } setTemplates(normalizeTemplates(data || [])) } catch (err) { console.error('Error fetching templates:', err) setError('Failed to load templates') } finally { setLoading(false) } }, [onLoadTemplates, onSeedTemplates]) useEffect(() => { if (open) { fetchTemplates() setSearch('') } }, [open, fetchTemplates]) const filteredTemplates = useMemo(() => { let result = templates if (categoryFilter) { result = result.filter((t) => t.category === categoryFilter) } if (search.trim()) { const q = search.toLowerCase() result = result.filter( (t) => t.name.toLowerCase().includes(q) || (t.description && t.description.toLowerCase().includes(q)) || t.subject.toLowerCase().includes(q) ) } return result }, [templates, categoryFilter, search]) const handleSelect = useCallback((template: EmailTemplate) => { onSelectTemplate(template) onOpenChange(false) }, [onSelectTemplate, onOpenChange]) return ( Choose a Template Start with a pre-built template to save time {loading ? (
) : error ? (

{error}

) : templates.length === 0 ? (

No templates available

{onSeedTemplates && ( )}
) : ( <> {/* Search */}
setSearch(e.target.value)} className="pl-9" />
{filteredTemplates.map((template) => ( ))} {filteredTemplates.length === 0 && (

No templates match your search

)}

{filteredTemplates.length} template{filteredTemplates.length !== 1 ? 's' : ''} available

)}
) }