'use client'; import { useState, useEffect } from 'react'; import { Search, Plus, X, Edit, Trash2, AlertTriangle, Ban, CheckCircle } from 'lucide-react'; interface Tenant { id: string; name: string; slug: string; type: string; plan: string; status: string; email: string; phone: string; mrr: number; createdAt: string; trialEnds: string; password?: string; } const statusMap: Record = { active: { label: 'Ativo', cls: 'badge-success' }, trial: { label: 'Trial', cls: 'badge-info' }, past_due: { label: 'Inadimplente', cls: 'badge-danger' }, cancelled: { label: 'Cancelado', cls: 'badge-warning' }, suspended: { label: 'Suspenso', cls: 'badge-danger' }, }; export default function Tenants() { const [tenants, setTenants] = useState([]); const [search, setSearch] = useState(''); const [filterStatus, setFilterStatus] = useState('all'); const [showModal, setShowModal] = useState(false); const [editing, setEditing] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [form, setForm] = useState>({}); const [loading, setLoading] = useState(true); const fetchTenants = async () => { try { const res = await fetch('/api/tenants'); if (res.ok) { const data = await res.json(); setTenants(data); } } catch (e) { console.error(e); } finally { setLoading(false); } }; useEffect(() => { fetchTenants(); }, []); const filtered = tenants.filter(t => { const matchSearch = t.name.toLowerCase().includes(search.toLowerCase()) || (t.email && t.email.toLowerCase().includes(search.toLowerCase())); const matchStatus = filterStatus === 'all' || t.status === filterStatus; return matchSearch && matchStatus; }); const openEdit = (t: Tenant) => { setEditing(t); setForm({ ...t }); setShowModal(true); }; const openAdd = () => { setEditing(null); setForm({ status: 'trial', plan: 'Starter', type: 'Barbearia' }); setShowModal(true); }; const handleSave = async () => { if (!form.name?.trim() || !form.slug?.trim()) return; const method = editing ? 'PUT' : 'POST'; try { const res = await fetch('/api/tenants', { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) }); if (res.ok) { fetchTenants(); setShowModal(false); } } catch (e) { console.error(e); } }; const handleDelete = async (id: string) => { try { await fetch(`/api/tenants?id=${id}`, { method: 'DELETE' }); setTenants(prev => prev.filter(t => t.id !== id)); setDeleteConfirm(null); } catch (e) { console.error(e); } }; const toggleStatus = async (t: Tenant, newStatus: string) => { try { const res = await fetch('/api/tenants', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...t, status: newStatus }) }); if (res.ok) { fetchTenants(); } } catch (err) { console.error(err); } }; if (loading) return
Carregando estabelecimentos...
; return (
setSearch(e.target.value)} />
{filtered.map(t => ( ))} {filtered.length === 0 && }
EstabelecimentoTipoPlanoStatusMRRDesdeAções
{t.name[0]}
{t.name}
{t.email || t.slug}
{t.type} {t.plan} {statusMap[t.status]?.label || t.status} {t.mrr > 0 ? `R$ ${t.mrr.toFixed(2)}` : '-'} {t.createdAt ? new Date(t.createdAt + 'T12:00:00').toLocaleDateString('pt-BR') : '-'} {deleteConfirm === t.id ? (
) : (
{t.status === 'active' && } {t.status === 'suspended' && }
)}
Nenhum assinante encontrado.
{showModal && (
setShowModal(false)}>
e.stopPropagation()}>

{editing ? 'Editar Assinante' : 'Novo Assinante'}

setForm(p => ({ ...p, name: e.target.value }))} />
setForm(p => ({ ...p, slug: e.target.value }))} disabled={!!editing} />
setForm(p => ({ ...p, email: e.target.value }))} />
setForm(p => ({ ...p, phone: e.target.value }))} />
setForm(p => ({ ...p, password: e.target.value }))} />
)}
); }