'use client'; /** * TemplateLibrary — modal de biblioteca de templates de mensagens. * * Permite: * - Navegar templates por tipo * - Pesquisar por nome * - Selecionar um template para usar (retorna o payload ao pai) * - Deletar templates * - Ver contagem de uso */ import React, { useEffect, useState, useCallback } from 'react' import { createPortal } from 'react-dom' import { motion, AnimatePresence } from 'framer-motion' import { X, Search, Trash2, Clock, Type, MousePointerClick, Link, List, BarChart2, LayoutGrid, BookOpen, CheckCircle2, } from 'lucide-react' import { templateApi, type MessageTemplate, type TemplateType } from '../services/chatApiService' // ─── Helpers ───────────────────────────────────────────────────────────────── const TYPE_META: Record = { TEXT: { label: 'Texto', icon: , color: '#667781', bg: '#f0f2f5' }, BUTTONS: { label: 'Botões', icon: , color: '#0ea5e9', bg: '#e0f2fe' }, INTERACTIVE: { label: 'CTA', icon: , color: '#8b5cf6', bg: '#ede9fe' }, LIST: { label: 'Lista', icon: , color: '#f59e0b', bg: '#fef3c7' }, POLL: { label: 'Enquete', icon: , color: '#10b981', bg: '#d1fae5' }, CAROUSEL: { label: 'Carrossel', icon: , color: '#ef4444', bg: '#fee2e2' }, } function TypeBadge({ type }: { type: TemplateType }) { const meta = TYPE_META[type] return ( {meta.icon} {meta.label} ) } function PayloadPreview({ template }: { template: MessageTemplate }) { const p = template.payload if (template.type === 'TEXT') return {(p as any).text || '—'} if (template.type === 'BUTTONS') return {(p as any).text || ''} [{((p as any).nativeButtons ?? []).length} botões] if (template.type === 'INTERACTIVE') return {(p as any).text || ''} [{((p as any).nativeButtons ?? []).length} CTAs] if (template.type === 'LIST') return {(p as any).text || ''} — {(p as any).nativeList?.buttonText} if (template.type === 'POLL') return 📊 {(p as any).poll?.name} ({((p as any).poll?.values ?? []).length} opções) if (template.type === 'CAROUSEL') return 🎠 {((p as any).nativeCarousel?.cards ?? []).length} cards return null } // ─── Props ──────────────────────────────────────────────────────────────────── interface Props { isOpen: boolean onClose: () => void onSelect: (template: MessageTemplate) => void } // ─── Component ──────────────────────────────────────────────────────────────── const ALL = 'ALL' type FilterType = TemplateType | typeof ALL export default function TemplateLibrary({ isOpen, onClose, onSelect }: Props) { const [templates, setTemplates] = useState([]) const [loading, setLoading] = useState(false) const [search, setSearch] = useState('') const [filter, setFilter] = useState(ALL) const [deleting, setDeleting] = useState(null) const [selected, setSelected] = useState(null) const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) const load = useCallback(async () => { setLoading(true) try { const data = await templateApi.list({ ...(filter !== ALL ? { type: filter } : {}), ...(search ? { search } : {}), }) setTemplates(data) } finally { setLoading(false) } }, [filter, search]) useEffect(() => { if (isOpen) load() }, [isOpen, load]) const handleDelete = async (e: React.MouseEvent, id: string) => { e.stopPropagation() if (!confirm('Remover este template?')) return setDeleting(id) try { await templateApi.delete(id) setTemplates(prev => prev.filter(t => t.id !== id)) } finally { setDeleting(null) } } const handleSelect = (template: MessageTemplate) => { setSelected(template.id) setTimeout(() => { onSelect(template) setSelected(null) onClose() }, 200) } const filterTabs: Array<{ key: FilterType; label: string }> = [ { key: ALL, label: 'Todos' }, ...Object.entries(TYPE_META).map(([k, v]) => ({ key: k as TemplateType, label: v.label })), ] if (!mounted) return null return createPortal( {isOpen && ( { if (e.target === e.currentTarget) onClose() }} > {/* Header */}

Biblioteca de Templates

{templates.length} template{templates.length !== 1 ? 's' : ''} salvos

{/* Search */}
setSearch(e.target.value)} />
{/* Filter tabs */}
{filterTabs.map(tab => ( ))}
{/* Template list */}
{loading ? (
) : templates.length === 0 ? (

Nenhum template encontrado

Crie um template no compositor de mensagens

) : (
{templates.map(tmpl => ( handleSelect(tmpl)} className={`w-full text-left rounded-xl border p-4 transition-all hover:shadow-md active:scale-[0.99] group ${ selected === tmpl.id ? 'border-[#00a884] bg-[#e7f8f2]' : 'border-gray-200 hover:border-gray-300 bg-white' }`} >
{tmpl.usageCount > 0 && ( {tmpl.usageCount}x usado )}

{tmpl.name}

{tmpl.description && (

{tmpl.description}

)}

{tmpl.tags.length > 0 && (
{tmpl.tags.map(tag => ( #{tag} ))}
)}
{selected === tmpl.id ? ( ) : ( <> )}
))}
)}
)}
, document.body ) }