Files
advx/src/components/atendimentos/AtendimentoDetalhes.tsx
T
rodrigo 5cb8919941 Lint errors
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
2025-04-12 21:22:06 -03:00

255 lines
9.0 KiB
TypeScript

import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { useState } from "react";
import { useQuery } from "react-query";
import { useParams, Link } from "react-router-dom";
import api from "../../services/api";
// Função auxiliar para formatar data com validação
const formatDate = (
date: string | Date | null | undefined,
formatStr: string,
): string => {
if (!date) return "Data não disponível";
try {
const dateObj = typeof date === "string" ? parseISO(date) : date;
return format(dateObj, formatStr, { locale: ptBR });
} catch (error) {
console.error("Erro ao formatar data:", error);
return "Data inválida";
}
};
export function AtendimentoDetalhes() {
const { id } = useParams();
const [loading, setLoading] = useState(false);
const [cancelModalOpen, setCancelModalOpen] = useState(false);
const [justificativa, setJustificativa] = useState("");
const [isCancelling, setIsCancelling] = useState(false);
const { data: atendimento, refetch } = useQuery(
["atendimento", id],
async () => {
const response = await api.get(`/atendimentos/${id}`);
return response.data;
},
);
const handleCancelar = async () => {
if (!justificativa.trim()) {
alert("A justificativa é obrigatória");
return;
}
try {
setIsCancelling(true);
await api.patch(`/atendimentos/${id}/status`, {
status: "CANCELADO",
justificativa,
});
await refetch();
setCancelModalOpen(false);
} catch (error) {
console.error("Erro ao cancelar atendimento:", error);
alert("Erro ao cancelar atendimento");
} finally {
setIsCancelling(false);
}
};
if (!atendimento) {
return (
<div className="flex justify-center items-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
</div>
);
}
return (
<div className="max-w-5xl mx-auto p-6 space-y-6">
{/* Cabeçalho */}
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="bg-gradient-to-r from-blue-600 to-blue-800 px-6 py-4">
<div className="flex justify-between items-center">
<div>
<h1 className="text-2xl font-bold text-white">
{atendimento.titulo}
</h1>
<p className="text-blue-100">
Protocolo: {atendimento.protocolo}
</p>
</div>
<span
className={`px-3 py-1 rounded-full text-sm font-medium ${
atendimento.tipo === "INICIAL"
? "bg-green-100 text-green-800"
: "bg-blue-100 text-blue-800"
}`}
>
{atendimento.tipo}
</span>
</div>
</div>
{/* Informações do Atendimento */}
<div className="p-6">
<div className="grid grid-cols-2 gap-6 mb-6">
<div>
<h3 className="text-sm font-medium text-gray-500">Cliente</h3>
<p className="mt-1 text-sm text-gray-900">
{atendimento.cliente.nome}
</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500">
Data do Atendimento
</h3>
<p className="mt-1 text-sm text-gray-900">
{formatDate(atendimento.dataAtendimento, "PPpp")}
</p>
</div>
{atendimento.processo && (
<div>
<h3 className="text-sm font-medium text-gray-500">
Processo Vinculado
</h3>
<Link
to={`/processos/${atendimento.processo.id}`}
className="mt-1 text-sm text-blue-600 hover:text-blue-800"
>
{atendimento.processo.numero}
</Link>
</div>
)}
<div>
<h3 className="text-sm font-medium text-gray-500">Responsável</h3>
<p className="mt-1 text-sm text-gray-900">
{atendimento.usuario.nome}
</p>
</div>
</div>
{/* Conteúdo do Atendimento */}
<div className="prose max-w-none">
<h3 className="text-lg font-medium text-gray-900 mb-4">Conteúdo</h3>
<div
className="bg-gray-50 rounded-lg p-6"
dangerouslySetInnerHTML={{ __html: atendimento.conteudo }}
/>
</div>
{/* Anexos */}
{atendimento.anexos?.length > 0 && (
<div className="mt-8">
<h3 className="text-lg font-medium text-gray-900 mb-4">Anexos</h3>
<div className="grid grid-cols-2 gap-4">
{atendimento.anexos.map((anexo: any) => (
<div
key={anexo.id}
className="flex items-center p-4 bg-gray-50 rounded-lg"
>
<svg
className="w-6 h-6 text-gray-400 mr-3"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"
/>
</svg>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">
{anexo.nome}
</p>
<p className="text-sm text-gray-500">
{formatDate(anexo.dataCriacao, "PP")}
</p>
</div>
<button
onClick={(e) => {
e.preventDefault();
window.open(anexo.url, "_blank", "noopener,noreferrer");
}}
className="ml-4 text-blue-600 hover:text-blue-800"
>
Visualizar
</button>
</div>
))}
</div>
</div>
)}
{/* Adicionar botão de cancelar próximo ao cabeçalho */}
{atendimento.status !== "CANCELADO" && (
<button
onClick={() => setCancelModalOpen(true)}
className="bg-red-100 text-red-600 px-4 py-2 rounded-lg hover:bg-red-200"
>
Cancelar Atendimento
</button>
)}
{/* Modal de Cancelamento */}
{cancelModalOpen && (
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center p-4">
<div className="bg-white rounded-lg max-w-md w-full p-6">
<h3 className="text-lg font-medium mb-4">
Cancelar Atendimento
</h3>
<p className="text-sm text-gray-500 mb-4">
Esta ação não pode ser desfeita. O atendimento será marcado
como cancelado, mas manterá seu protocolo e histórico.
</p>
<textarea
value={justificativa}
onChange={(e) => setJustificativa(e.target.value)}
placeholder="Insira a justificativa do cancelamento"
className="w-full p-2 border rounded-lg mb-4"
rows={4}
required
/>
<div className="flex justify-end space-x-4">
<button
onClick={() => setCancelModalOpen(false)}
className="px-4 py-2 text-gray-600 hover:text-gray-800"
>
Cancelar
</button>
<button
onClick={handleCancelar}
disabled={isCancelling || !justificativa.trim()}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50"
>
{isCancelling ? "Cancelando..." : "Confirmar Cancelamento"}
</button>
</div>
</div>
</div>
)}
{/* Exibir informação de cancelamento se aplicável */}
{atendimento.status === "CANCELADO" && (
<div className="mt-4 p-4 bg-red-50 border-l-4 border-red-500 rounded-r-lg">
<h4 className="text-red-800 font-medium">
Atendimento Cancelado
</h4>
<p className="text-red-600 mt-1">
{atendimento.justificativaCancelamento}
</p>
<p className="text-sm text-red-500 mt-1">
Cancelado em: {formatDate(atendimento.dataAtualizacao, "PPpp")}
</p>
</div>
)}
</div>
</div>
</div>
);
}