Files
2025-12-17 13:36:23 -03:00

507 lines
26 KiB
TypeScript

'use client';
import {
BuildingOfficeIcon,
ArrowLeftIcon,
PaintBrushIcon,
MapPinIcon,
UserGroupIcon,
ChartBarIcon,
FolderIcon,
LifebuoyIcon,
CreditCardIcon,
DocumentTextIcon,
ArchiveBoxIcon,
ShareIcon
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { useParams } from 'next/navigation';
import Tabs, { TabItem } from '@/components/ui/Tabs';
// Mapeamento de ícones para cada solução
const SOLUTION_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
'crm': UserGroupIcon,
'erp': ChartBarIcon,
'projetos': FolderIcon,
'helpdesk': LifebuoyIcon,
'pagamentos': CreditCardIcon,
'contratos': DocumentTextIcon,
'documentos': ArchiveBoxIcon,
'social': ShareIcon,
};
interface AgencyTenant {
id: string;
name: string;
subdomain: string;
domain: string;
email: string;
phone: string;
website: string;
cnpj: string;
razao_social: string;
description: string;
industry: string;
team_size: string;
address: string;
neighborhood: string;
number: string;
complement: string;
city: string;
state: string;
zip: string;
primary_color: string;
secondary_color: string;
logo_url: string;
logo_horizontal_url: string;
is_active: boolean;
created_at: string;
updated_at: string;
}
interface AgencyDetails {
tenant: AgencyTenant;
admin?: {
id: string;
email: string;
name: string;
};
subscription?: {
plan_id: string;
plan_name: string;
status: string;
solutions: Array<{
id: string;
name: string;
slug: string;
icon: string;
}>;
};
access_url: string;
}
export default function AgencyDetailPage() {
const params = useParams();
const [details, setDetails] = useState<AgencyDetails | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (params.id) {
fetchAgency(params.id as string);
}
}, [params.id]);
const fetchAgency = async (id: string) => {
try {
const response = await fetch(`/api/admin/agencies/${id}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
});
if (response.ok) {
const data = await response.json();
// Handle both flat (legacy) and nested (new) responses
if (data.tenant) {
setDetails(data);
} else {
// Fallback for legacy flat response
setDetails({
tenant: data,
access_url: `http://${data.subdomain}.localhost`, // Fallback URL
});
}
}
} catch (error) {
console.error('Error fetching agency:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div className="p-8 flex items-center justify-center min-h-[400px]">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
);
}
if (!details || !details.tenant) {
return (
<div className="p-8">
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded relative" role="alert">
<strong className="font-bold">Erro!</strong>
<span className="block sm:inline"> Agência não encontrada.</span>
</div>
<Link
href="/superadmin/agencies"
className="mt-4 inline-flex items-center gap-2 text-gray-600 hover:text-gray-900"
>
<ArrowLeftIcon className="w-4 h-4" />
Voltar para Agências
</Link>
</div>
);
}
const { tenant } = details;
const tabsConfig: TabItem[] = [
{
name: 'Visão Geral',
icon: BuildingOfficeIcon,
content: (
<div className="space-y-6">
{/* Informações Básicas */}
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">Dados da Empresa</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Razão Social</dt>
<dd className="mt-1 text-sm font-medium text-gray-900 dark:text-white">{tenant.razao_social || '-'}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">CNPJ</dt>
<dd className="mt-1 text-sm font-medium text-gray-900 dark:text-white">{tenant.cnpj || '-'}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Setor</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.industry || '-'}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Tamanho da Equipe</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.team_size || '-'}</dd>
</div>
<div className="md:col-span-2">
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Descrição</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.description || '-'}</dd>
</div>
</div>
</div>
<div className="border-t border-gray-200 dark:border-gray-800 pt-6"></div>
{/* Localização */}
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<MapPinIcon className="w-5 h-5 text-gray-500" />
Localização
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="md:col-span-2">
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Endereço</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">
{tenant.address ? (
<>
{tenant.address}
{tenant.number ? `, ${tenant.number}` : ''}
{tenant.complement ? ` - ${tenant.complement}` : ''}
</>
) : '-'}
</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Bairro</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.neighborhood || '-'}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cidade / UF</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">
{tenant.city && tenant.state ? `${tenant.city} - ${tenant.state}` : '-'}
</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">CEP</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.zip || '-'}</dd>
</div>
</div>
</div>
<div className="border-t border-gray-200 dark:border-gray-800 pt-6"></div>
{/* Contato */}
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">Contato</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Email</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white break-all">{tenant.email || '-'}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Telefone</dt>
<dd className="mt-1 text-sm text-gray-900 dark:text-white">{tenant.phone || '-'}</dd>
</div>
<div className="md:col-span-2">
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Website</dt>
<dd className="mt-1">
{tenant.website ? (
<a
href={tenant.website}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-[var(--brand-color)] hover:underline break-all"
>
{tenant.website}
</a>
) : '-'}
</dd>
</div>
</div>
</div>
<div className="border-t border-gray-200 dark:border-gray-800 pt-6"></div>
{/* Metadados */}
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">Metadados</h3>
<dl className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Criada em</dt>
<dd className="mt-1 text-sm font-medium text-gray-900 dark:text-white">
{new Date(tenant.created_at).toLocaleDateString('pt-BR')}
</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Última atualização</dt>
<dd className="mt-1 text-sm font-medium text-gray-900 dark:text-white">
{new Date(tenant.updated_at).toLocaleDateString('pt-BR')}
</dd>
</div>
</dl>
</div>
</div>
)
},
{
name: 'Identidade Visual',
icon: PaintBrushIcon,
content: (
<div className="space-y-8">
{/* Cores da Marca */}
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">Cores da Marca</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<dt className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Cor Primária</dt>
<div className="flex items-center gap-4">
<div
className="w-20 h-20 rounded-lg border-2 border-gray-200 dark:border-gray-700 shadow-sm"
style={{ backgroundColor: tenant.primary_color || '#000000' }}
/>
<div>
<span className="text-xs font-mono text-gray-900 dark:text-white block mb-1">
{tenant.primary_color || '-'}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
Cor principal da marca
</span>
</div>
</div>
</div>
<div>
<dt className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Cor Secundária</dt>
<div className="flex items-center gap-4">
<div
className="w-20 h-20 rounded-lg border-2 border-gray-200 dark:border-gray-700 shadow-sm"
style={{ backgroundColor: tenant.secondary_color || '#ffffff' }}
/>
<div>
<span className="text-xs font-mono text-gray-900 dark:text-white block mb-1">
{tenant.secondary_color || '-'}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
Cor de apoio da marca
</span>
</div>
</div>
</div>
</div>
</div>
{/* Logos */}
<div className="border-t border-gray-200 dark:border-gray-800 pt-8">
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">Logotipos</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Logo Principal */}
<div>
<dt className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Logo Principal</dt>
<div className="bg-gray-50 dark:bg-gray-800 p-6 rounded-lg border border-gray-200 dark:border-gray-700 flex items-center justify-center min-h-[120px]">
{tenant.logo_url ? (
<img src={tenant.logo_url} alt="Logo" className="max-h-20 max-w-full object-contain" />
) : (
<span className="text-sm text-gray-400">Sem logo</span>
)}
</div>
</div>
{/* Logo Horizontal */}
<div>
<dt className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Logo Horizontal</dt>
<div className="bg-gray-50 dark:bg-gray-800 p-6 rounded-lg border border-gray-200 dark:border-gray-700 flex items-center justify-center min-h-[120px]">
{tenant.logo_horizontal_url ? (
<img src={tenant.logo_horizontal_url} alt="Logo Horizontal" className="max-h-16 max-w-full object-contain" />
) : (
<span className="text-sm text-gray-400">Sem logo horizontal</span>
)}
</div>
</div>
</div>
</div>
</div>
)
},
{
name: 'Plano e Soluções',
content: (
<div className="space-y-6">
{details.subscription ? (
<>
{/* Informações do Plano */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Plano</dt>
<dd className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">{details.subscription.plan_name}</dd>
</div>
<div>
<dt className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">Status</dt>
<span className={`px-3 py-1 inline-flex text-sm font-medium rounded-full ${details.subscription.status === 'active'
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
: 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400'
}`}>
{details.subscription.status === 'active' ? 'Ativa' : details.subscription.status}
</span>
</div>
</div>
<div className="border-t border-gray-200 dark:border-gray-800 pt-6"></div>
{/* Soluções Disponíveis */}
{details.subscription.solutions && details.subscription.solutions.length > 0 && (
<div>
<h3 className="text-base font-semibold text-gray-900 dark:text-white mb-4">
Soluções Disponíveis ({details.subscription.solutions.length})
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{details.subscription.solutions.map((solution) => {
const Icon = SOLUTION_ICONS[solution.slug] || FolderIcon;
return (
<div
key={solution.id}
className="flex items-center gap-3 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"
>
<div className="p-2.5 rounded-xl bg-white dark:bg-zinc-800 border border-gray-200 dark:border-gray-700">
<Icon className="w-5 h-5 text-[var(--brand-color)]" />
</div>
<div>
<span className="text-sm font-medium text-gray-900 dark:text-white block">
{solution.name}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
{solution.slug}
</span>
</div>
</div>
);
})}
</div>
</div>
)}
<div className="border-t border-gray-200 dark:border-gray-800 pt-6"></div>
{/* Ações */}
<div className="flex gap-3">
<Link
href={`/superadmin/plans/${details.subscription.plan_id}`}
className="inline-flex items-center px-4 py-2 bg-[var(--brand-color)] text-white rounded-lg hover:opacity-90 transition-all font-medium text-sm"
>
Ver Detalhes do Plano
</Link>
<Link
href={`/superadmin/plans/${details.subscription.plan_id}`}
className="inline-flex items-center px-4 py-2 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors font-medium text-sm"
>
Gerenciar Soluções
</Link>
</div>
</>
) : (
<div className="text-center py-12">
<svg className="w-16 h-16 text-gray-300 dark:text-gray-700 mx-auto mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">Nenhuma Assinatura</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">
Esta agência ainda não possui um plano ativo.
</p>
</div>
)}
</div>
)
}
];
return (
<div className="p-8 max-w-7xl mx-auto">
<div className="mb-8">
<Link
href="/superadmin/agencies"
className="inline-flex items-center gap-2 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200 mb-6 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4" />
Voltar para Agências
</Link>
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-center gap-4">
<div className="h-16 w-16 rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center p-2">
{tenant.logo_url ? (
<img src={tenant.logo_url} alt={tenant.name} className="max-h-full max-w-full object-contain" />
) : (
<BuildingOfficeIcon className="w-8 h-8 text-gray-400" />
)}
</div>
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{tenant.name}</h1>
<div className="flex items-center gap-2 mt-1">
<a
href={details.access_url}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-[var(--brand-color)] hover:underline flex items-center gap-1"
>
{tenant.subdomain}.aggios.app
<ArrowLeftIcon className="w-3 h-3 rotate-135" />
</a>
<span className="text-gray-300 dark:text-gray-600">|</span>
<span className={`px-2 py-0.5 inline-flex text-xs font-medium rounded-full ${tenant.is_active
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
}`}>
{tenant.is_active ? 'Ativa' : 'Inativa'}
</span>
</div>
</div>
</div>
<div className="flex gap-3">
<Link
href={`/superadmin/agencies/${tenant.id}/edit`}
className="px-4 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors font-medium text-sm"
>
Editar Dados
</Link>
<button
className="px-4 py-2 bg-[var(--brand-color)] text-white rounded-lg hover:opacity-90 transition-all font-medium text-sm"
>
Acessar Painel
</button>
</div>
</div>
</div>
<Tabs tabs={tabsConfig} />
</div>
);
}