Files
advx/src/components/clientes/ClienteDetalhes.tsx
T
2025-04-10 23:07:21 -03:00

391 lines
13 KiB
TypeScript

import React, { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { useClientes } from "../../hooks/useClientes";
import { Tab } from "@headlessui/react";
import {
ArrowLeftIcon,
PencilIcon,
UserIcon,
HomeIcon,
BanknotesIcon,
UserGroupIcon,
DocumentTextIcon,
} from "@heroicons/react/24/outline";
import { ClienteModal } from "./ClienteModal";
import {
formatCPF,
formatTelefone,
formatNome,
formatEmail,
formatMoeda,
formatTextoSimples,
formatData,
} from "../../utils/formatters";
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
}
const tabs = [
{ id: "info", name: "Informações Básicas", icon: UserIcon },
{ id: "endereco", name: "Endereço", icon: HomeIcon },
{ id: "financeiro", name: "Informações Financeiras", icon: BanknotesIcon },
{ id: "familia", name: "Informações Familiares", icon: UserGroupIcon },
{ id: "processos", name: "Processos", icon: DocumentTextIcon },
];
interface InfoItemProps {
label: string;
value: string;
onEdit?: () => void;
isEditable?: boolean;
}
const InfoItem: React.FC<InfoItemProps> = ({
label,
value,
onEdit,
isEditable = true,
}) => (
<div className="relative group">
<dt className="text-sm font-medium text-gray-500">{label}</dt>
<dd className="mt-1 text-sm text-gray-900 flex items-center justify-between">
<span>{value}</span>
{isEditable && (
<button
onClick={onEdit}
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-gray-100 rounded"
>
<PencilIcon className="h-4 w-4 text-blue-600" />
</button>
)}
</dd>
</div>
);
export const ClienteDetalhes: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { clientes, isLoading } = useClientes();
const [selectedTab, setSelectedTab] = useState(0);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [editingField, setEditingField] = useState<string | null>(null);
const cliente = clientes.find((c) => c.id === id);
if (isLoading) {
return (
<div className="flex justify-center items-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>
);
}
if (!cliente) {
return <div>Cliente não encontrado</div>;
}
// Funções de formatação simplificadas
const formataCurrency = (valor: any): string => {
if (!valor) return "Não informado";
try {
return new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(Number(valor));
} catch {
return "Não informado";
}
};
const formataTexto = (texto: any): string => {
if (!texto && texto !== false) return "Não informado";
if (typeof texto === "boolean") return texto ? "Sim" : "Não";
return String(texto);
};
const getTabForField = (field: string | null) => {
switch (field) {
case "nome":
case "cpfCnpj":
case "rg":
case "email":
case "telefone":
case "instagram":
case "estadoCivil":
case "dataNascimento":
return 0;
case "cep":
case "endereco":
case "complemento":
case "bairro":
case "cidade":
case "estado":
return 1;
case "profissao":
case "vinculoEmpregaticio":
case "renda":
case "declaraIR":
case "tipoPrevidencia":
case "contribuiPrevidencia":
return 2;
case "nomePai":
case "nomeMae":
case "conjugeNome":
case "conjugeCpf":
return 3;
default:
return 0;
}
};
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<button
onClick={() => navigate("/clientes")}
className="p-2 hover:bg-gray-100 rounded-full"
>
<ArrowLeftIcon className="h-5 w-5 text-gray-500" />
</button>
<div>
<h1 className="text-2xl font-semibold text-gray-800">
{formatNome(cliente.nome)}
</h1>
<p className="text-sm text-gray-500">
Cadastrado em {new Date(cliente.dataCriacao).toLocaleDateString()}
</p>
</div>
</div>
<button
onClick={() => setIsEditModalOpen(true)}
className="flex items-center px-4 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-lg"
>
<PencilIcon className="h-4 w-4 mr-2" />
Editar
</button>
</div>
{/* Tabs */}
<Tab.Group selectedIndex={selectedTab} onChange={setSelectedTab}>
<Tab.List className="flex space-x-1 rounded-xl bg-blue-900/20 p-1">
{tabs.map((tab) => (
<Tab
key={tab.id}
className={({ selected }) =>
classNames(
"w-full rounded-lg py-2.5 text-sm font-medium leading-5",
"flex items-center justify-center",
selected
? "bg-white text-blue-700 shadow"
: "text-gray-600 hover:bg-white/[0.12] hover:text-blue-600",
)
}
>
<tab.icon className="h-4 w-4 mr-2" />
{tab.name}
</Tab>
))}
</Tab.List>
<Tab.Panels className="mt-2">
{/* Informações Básicas */}
<Tab.Panel className="bg-white rounded-xl p-6">
<dl className="grid grid-cols-1 md:grid-cols-2 gap-6">
<InfoItem
label="Nome"
value={formatNome(cliente.nome)}
onEdit={() => {
setEditingField("nome");
setIsEditModalOpen(true);
}}
/>
<InfoItem
label="CPF/CNPJ"
value={formatCPF(cliente.cpfCnpj)}
onEdit={() => {
setEditingField("cpfCnpj");
setIsEditModalOpen(true);
}}
/>
<InfoItem label="RG" value={formatTextoSimples(cliente.rg)} />
<InfoItem label="Email" value={formatEmail(cliente.email)} />
<InfoItem
label="Telefone"
value={formatTelefone(cliente.telefone)}
/>
<InfoItem
label="Instagram"
value={formatTextoSimples(cliente.instagram)}
/>
<InfoItem
label="Estado Civil"
value={formatTextoSimples(cliente.estadoCivil)}
/>
<InfoItem
label="Data de Nascimento"
value={formatData(cliente.dataNascimento)}
/>
</dl>
</Tab.Panel>
{/* Endereço */}
<Tab.Panel className="bg-white rounded-xl p-6">
<dl className="grid grid-cols-1 md:grid-cols-2 gap-6">
<InfoItem label="CEP" value={formataTexto(cliente.cep)} />
<InfoItem
label="Endereço"
value={formataTexto(`${cliente.endereco}, ${cliente.numero}`)}
/>
<InfoItem
label="Complemento"
value={formataTexto(cliente.complemento)}
/>
<InfoItem label="Bairro" value={formataTexto(cliente.bairro)} />
<InfoItem label="Cidade" value={formataTexto(cliente.cidade)} />
<InfoItem label="Estado" value={formataTexto(cliente.estado)} />
</dl>
</Tab.Panel>
{/* Informações Financeiras */}
<Tab.Panel className="bg-white rounded-xl p-6">
<dl className="grid grid-cols-1 md:grid-cols-2 gap-6">
<InfoItem
label="Profissão"
value={formatTextoSimples(cliente.profissao)}
/>
<InfoItem
label="Vínculo"
value={formatTextoSimples(cliente.vinculoEmpregaticio)}
/>
<InfoItem
label="Renda Mensal"
value={formatMoeda(Number(cliente.renda))}
/>
<InfoItem
label="Declara IR"
value={cliente.declaraIR ? "Sim" : "Não"}
/>
<InfoItem
label="Tipo Previdência"
value={formatTextoSimples(cliente.tipoPrevidencia)}
/>
<InfoItem
label="Contribui Previdência"
value={cliente.contribuiPrevidencia ? "Sim" : "Não"}
/>
</dl>
</Tab.Panel>
{/* Informações Familiares */}
<Tab.Panel className="bg-white rounded-xl p-6">
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<InfoItem
label="Nome do Pai"
value={formatNome(cliente.nomePai || "")}
/>
<InfoItem
label="Nome da Mãe"
value={formatNome(cliente.nomeMae || "")}
/>
<InfoItem
label="Nome do Cônjuge"
value={formatNome(cliente.conjugeNome || "")}
/>
<InfoItem
label="CPF do Cônjuge"
value={
cliente.conjugeCpf
? formatCPF(cliente.conjugeCpf)
: "Não informado"
}
/>
</div>
{/* Lista de Filhos */}
<div>
<h3 className="text-lg font-medium text-gray-900 mb-4">
Filhos
</h3>
{cliente.filhos && cliente.filhos.length > 0 ? (
<ul className="divide-y divide-gray-200">
{cliente.filhos.map((filho) => (
<li key={filho.id} className="py-3">
<p className="text-sm font-medium text-gray-900">
{filho.nome}
</p>
<p className="text-sm text-gray-500">
Nascimento:{" "}
{new Date(filho.dataNascimento).toLocaleDateString()}
</p>
</li>
))}
</ul>
) : (
<p className="text-sm text-gray-500">
Nenhum filho cadastrado
</p>
)}
</div>
</div>
</Tab.Panel>
{/* Processos */}
<Tab.Panel className="bg-white rounded-xl p-6">
{cliente.processos && cliente.processos.length > 0 ? (
<ul className="divide-y divide-gray-200">
{cliente.processos.map((processo) => (
<li key={processo.id} className="py-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-900">
{processo.titulo}
</p>
<p className="text-sm text-gray-500">
Nº {processo.numero}
</p>
</div>
<span
className={classNames(
"px-2 py-1 text-xs rounded-full",
processo.status === "em_andamento"
? "bg-blue-100 text-blue-800"
: processo.status === "concluido"
? "bg-green-100 text-green-800"
: "bg-gray-100 text-gray-800",
)}
>
{processo.status}
</span>
</div>
</li>
))}
</ul>
) : (
<p className="text-sm text-gray-500">
Nenhum processo cadastrado
</p>
)}
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
{/* Modal de Edição */}
<ClienteModal
isOpen={isEditModalOpen}
cliente={cliente}
onClose={() => {
setIsEditModalOpen(false);
setEditingField(null);
}}
onSuccess={() => {
setIsEditModalOpen(false);
setEditingField(null);
}}
initialTab={getTabForField(editingField)}
/>
</div>
);
};