first commit

This commit is contained in:
rodrigo committed 2025-04-10 23:07:21 -03:00
commit c9fb67e751
423 files changed
+53287

No files matched your search

@@ -0,0 +1,309 @@
import { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { useQuery, useQueryClient } from "react-query";
import { Tab } from "@headlessui/react";
import { classNames } from "../../utils/classNames";
import {
ArrowLeftIcon,
DocumentTextIcon,
ClockIcon,
PencilIcon,
TrashIcon,
CurrencyDollarIcon,
} from "@heroicons/react/24/outline";
import { formatCurrency } from "../../utils/format";
import { useProcessosAdministrativos } from "../../hooks/useProcessosAdministrativos";
import { ProcessoAdminModal } from "./ProcessoAdminModal";
import { MovimentacaoAdministrativaDialog } from "./MovimentacaoAdministrativaDialog";
import { MovimentacaoAdminActions } from "./MovimentacaoAdminActions"; // Adicionar esta importação
import api from "../../services/api";
import { CustaAdministrativaPanel } from "./CustaAdministrativaPanel";
const tabs = [
{ id: "geral", name: "Informações Gerais", icon: DocumentTextIcon },
{ id: "movimentacoes", name: "Movimentações", icon: ClockIcon },
{ id: "financeiro", name: "Financeiro", icon: CurrencyDollarIcon }, // Adicionar nova tab
];
export function ProcessoAdminDetalhes() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const queryClient = useQueryClient();
const [selectedTab, setSelectedTab] = useState(0);
const [isMovimentacaoModalOpen, setIsMovimentacaoModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [movimentacaoParaEditar, setMovimentacaoParaEditar] = useState<MovimentacaoAdministrativa | null>(null);
// Se o id for "novo", renderizar diretamente o modal de criação
if (id === "novo") {
return (
<ProcessoAdminModal
isOpen={true}
onClose={() => navigate("/processos-administrativos")}
onSuccess={() => navigate("/processos-administrativos")}
/>
);
}
const { data: processo, isLoading, error } = useQuery(
["processo-administrativo", id],
async () => {
const response = await api.get(`/processos-administrativos/${id}`);
return response.data;
},
{
enabled: !!id && id !== "novo", // Só executa se tiver um id válido
retry: 2, // Tenta no máximo 2 vezes em caso de erro
onError: (error) => {
console.error("Erro ao carregar processo:", error);
}
}
);
const { deleteProcesso } = useProcessosAdministrativos();
const handleDelete = async () => {
try {
await deleteProcesso.mutateAsync(id!);
navigate("/processos-administrativos");
} catch (error) {
console.error("Erro ao excluir processo:", error);
}
};
const handleSaveMovimentacao = async (data: any) => {
try {
await api.post(`/processos-administrativos/${id}/movimentacoes`, data);
// Recarregar dados
queryClient.invalidateQueries(["processo-administrativo", id]);
} catch (error) {
console.error("Erro ao salvar movimentação:", error);
}
};
const handleEditMovimentacao = async (data: any) => {
try {
// Atualizar URL para usar a nova rota
await api.put(`/processos-administrativos/movimentacoes/${data.id}`, data);
queryClient.invalidateQueries(["processo-administrativo", id]);
setMovimentacaoParaEditar(null);
} catch (error) {
console.error("Erro ao editar movimentação:", error);
}
};
const handleDeleteMovimentacao = async (movimentacaoId: string) => {
if (!window.confirm("Deseja realmente excluir esta movimentação?")) return;
try {
// Atualizar URL para usar a nova rota
await api.delete(`/processos-administrativos/movimentacoes/${movimentacaoId}`);
queryClient.invalidateQueries(["processo-administrativo", id]);
} catch (error) {
console.error("Erro ao excluir movimentação:", error);
}
};
if (!processo) return <div>Processo não encontrado</div>;
if (isLoading) return <div>Carregando...</div>;
if (error) return <div>Erro ao carregar processo</div>;
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("/processos-administrativos")}
className="p-2 hover:bg-gray-100 rounded-full"
>
<ArrowLeftIcon className="h-5 w-5 text-gray-500" />
</button>
<div>
<h1 className="text-xl font-semibold text-gray-900">
{processo.numero}
</h1>
<p className="text-sm text-gray-500">{processo.orgao}</p>
</div>
</div>
<div className="flex items-center space-x-2">
<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>
<button
onClick={() => setIsDeleteModalOpen(true)}
className="flex items-center px-4 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg"
>
<TrashIcon className="h-4 w-4 mr-2" />
Excluir
</button>
</div>
</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-4">
{/* Informações Gerais */}
<Tab.Panel className="bg-white rounded-xl p-6">
<dl className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<dt className="text-sm font-medium text-gray-500">
Número/Protocolo
</dt>
<dd className="mt-1 text-sm text-gray-900">{processo.numero}</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">Órgão</dt>
<dd className="mt-1 text-sm text-gray-900">{processo.orgao}</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">Status</dt>
<dd className="mt-1 text-sm text-gray-900">{processo.status}</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">
Valor da Pretensão
</dt>
<dd className="mt-1 text-sm text-gray-900">
{processo.valorPretensao
? formatCurrency(processo.valorPretensao)
: "-"}
</dd>
</div>
<div className="col-span-2">
<dt className="text-sm font-medium text-gray-500">Descrição</dt>
<dd className="mt-1 text-sm text-gray-900">
{processo.descricao}
</dd>
</div>
<div className="col-span-2">
<dt className="text-sm font-medium text-gray-500">Observações</dt>
<dd className="mt-1 text-sm text-gray-900">
{processo.observacoes || "-"}
</dd>
</div>
</dl>
</Tab.Panel>
{/* Movimentações */}
<Tab.Panel className="bg-white rounded-xl p-6">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium text-gray-900">Movimentações</h3>
<button
onClick={() => setIsMovimentacaoModalOpen(true)}
className="flex items-center px-4 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-lg"
>
Nova Movimentação
</button>
</div>
{processo.movimentacoes?.length > 0 ? (
<div className="flow-root">
<ul role="list" className="-mb-8">
{processo.movimentacoes.map((mov, idx) => (
<li key={mov.id}>
<div className="relative pb-8">
{idx !== processo.movimentacoes.length - 1 ? (
<span
className="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200"
aria-hidden="true"
/>
) : null}
<div className="relative flex space-x-3">
<div>
<span className="h-8 w-8 rounded-full bg-gray-100 flex items-center justify-center">
<ClockIcon className="h-5 w-5 text-gray-500" />
</span>
</div>
<div className="min-w-0 flex-1">
<div className="flex justify-between items-start">
<div className="text-sm text-gray-500">
<time>{new Date(mov.data).toLocaleDateString()}</time>
</div>
<MovimentacaoAdminActions
movimentacao={mov}
onEdit={setMovimentacaoParaEditar}
onDelete={handleDeleteMovimentacao}
/>
</div>
<div className="mt-1">
<p className="text-sm text-gray-900">
{mov.descricao}
</p>
{mov.observacoes && (
<p className="mt-1 text-sm text-gray-500">
{mov.observacoes}
</p>
)}
</div>
</div>
</div>
</div>
</li>
))}
</ul>
</div>
) : (
<p className="text-sm text-gray-500 text-center py-4">
Nenhuma movimentação registrada
</p>
)}
</Tab.Panel>
{/* Painel Financeiro */}
<Tab.Panel className="bg-white rounded-xl p-6">
<CustaAdministrativaPanel processoId={id} />
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
{/* Modais */}
<ProcessoAdminModal
isOpen={isEditModalOpen}
onClose={() => setIsEditModalOpen(false)}
processo={processo}
onSuccess={() => {
queryClient.invalidateQueries(["processo-administrativo", id]);
setIsEditModalOpen(false);
}}
/>
<MovimentacaoAdministrativaDialog
isOpen={isMovimentacaoModalOpen}
onClose={() => setIsMovimentacaoModalOpen(false)}
onSave={handleSaveMovimentacao}
/>
<MovimentacaoAdministrativaDialog
isOpen={!!movimentacaoParaEditar}
onClose={() => setMovimentacaoParaEditar(null)}
onSave={handleEditMovimentacao}
movimentacao={movimentacaoParaEditar}
/>
</div>
);
}