forked from RiseUP/riseup-squad20
add-report-endpoints
This commit is contained in:
parent
0a7d3f3ae4
commit
6951dcec96
@ -5,7 +5,9 @@ import SignatureCanvas from "react-signature-canvas";
|
||||
import Link from "next/link";
|
||||
import ProtectedRoute from "@/components/ProtectedRoute";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import { buscarPacientes } from "@/lib/api";
|
||||
import { buscarPacientes, listarPacientes, buscarPacientePorId, type Paciente } from "@/lib/api";
|
||||
import { useReports } from "@/hooks/useReports";
|
||||
import { CreateReportData, ReportFormData } from "@/types/report";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
@ -108,11 +110,20 @@ const ProfissionalPage = () => {
|
||||
prognostico: "",
|
||||
tratamentosRealizados: "",
|
||||
recomendacoes: "",
|
||||
cid: "",
|
||||
dataRelatorio: new Date().toISOString().split('T')[0]
|
||||
});
|
||||
const [relatoriosMedicos, setRelatoriosMedicos] = useState<any[]>([]);
|
||||
const [editandoRelatorio, setEditandoRelatorio] = useState<any>(null);
|
||||
|
||||
// Estados para integração com API de Relatórios
|
||||
const [pacientesReais, setPacientesReais] = useState<Paciente[]>([]);
|
||||
const [carregandoPacientes, setCarregandoPacientes] = useState(false);
|
||||
const [pacienteSelecionadoReport, setPacienteSelecionadoReport] = useState<Paciente | null>(null);
|
||||
|
||||
// Hook personalizado para relatórios
|
||||
const reportsApi = useReports();
|
||||
|
||||
// Estados para funcionalidades do prontuário
|
||||
const [consultasRegistradas, setConsultasRegistradas] = useState<any[]>([]);
|
||||
const [historicoMedico, setHistoricoMedico] = useState<any[]>([]);
|
||||
@ -306,6 +317,7 @@ const ProfissionalPage = () => {
|
||||
prognostico: "",
|
||||
tratamentosRealizados: "",
|
||||
recomendacoes: "",
|
||||
cid: "",
|
||||
dataRelatorio: new Date().toISOString().split('T')[0]
|
||||
});
|
||||
};
|
||||
@ -339,10 +351,271 @@ const ProfissionalPage = () => {
|
||||
prognostico: "",
|
||||
tratamentosRealizados: "",
|
||||
recomendacoes: "",
|
||||
cid: "",
|
||||
dataRelatorio: new Date().toISOString().split('T')[0]
|
||||
});
|
||||
};
|
||||
|
||||
// ===== FUNÇÕES PARA INTEGRAÇÃO COM API DE RELATÓRIOS =====
|
||||
|
||||
// Carregar pacientes reais do Supabase
|
||||
const carregarPacientesReais = async () => {
|
||||
setCarregandoPacientes(true);
|
||||
try {
|
||||
console.log('📋 [REPORTS] Carregando pacientes do Supabase...');
|
||||
|
||||
// Tentar primeiro usando a função da API que já existe
|
||||
try {
|
||||
console.log('📋 [REPORTS] Tentando função listarPacientes...');
|
||||
const pacientes = await listarPacientes({ limit: 50 });
|
||||
console.log('✅ [REPORTS] Pacientes do Supabase via API:', pacientes);
|
||||
|
||||
if (pacientes && pacientes.length > 0) {
|
||||
setPacientesReais(pacientes);
|
||||
console.log('✅ [REPORTS] Usando pacientes do Supabase:', pacientes.length);
|
||||
return;
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.warn('⚠️ [REPORTS] Erro na função listarPacientes:', apiError);
|
||||
}
|
||||
|
||||
// Se a função da API falhar, tentar diretamente
|
||||
console.log('📋 [REPORTS] Tentando buscar diretamente do Supabase...');
|
||||
const supabaseUrl = 'https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients';
|
||||
console.log('📋 [REPORTS] URL do Supabase:', supabaseUrl);
|
||||
|
||||
// Verificar se há token de autenticação
|
||||
const token = localStorage.getItem("auth_token") || localStorage.getItem("token") ||
|
||||
sessionStorage.getItem("auth_token") || sessionStorage.getItem("token");
|
||||
|
||||
console.log('🔑 [REPORTS] Token encontrado:', token ? 'SIM' : 'NÃO');
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(supabaseUrl, {
|
||||
method: 'GET',
|
||||
headers
|
||||
});
|
||||
|
||||
console.log('📡 [REPORTS] Status da resposta do Supabase:', response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('❌ [REPORTS] Erro detalhado do Supabase:', errorText);
|
||||
throw new Error(`Supabase HTTP ${response.status}: ${response.statusText} - ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('✅ [REPORTS] Resposta completa do Supabase:', data);
|
||||
console.log('✅ [REPORTS] Tipo da resposta:', Array.isArray(data) ? 'Array' : typeof data);
|
||||
|
||||
let pacientes: Paciente[] = [];
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
pacientes = data;
|
||||
} else if (data.data && Array.isArray(data.data)) {
|
||||
pacientes = data.data;
|
||||
} else {
|
||||
console.warn('⚠️ [REPORTS] Formato de resposta inesperado do Supabase:', data);
|
||||
pacientes = [];
|
||||
}
|
||||
|
||||
console.log('✅ [REPORTS] Pacientes encontrados no Supabase:', pacientes.length);
|
||||
if (pacientes.length > 0) {
|
||||
console.log('✅ [REPORTS] Primeiro paciente:', pacientes[0]);
|
||||
console.log('✅ [REPORTS] Últimos 3 pacientes:', pacientes.slice(-3));
|
||||
}
|
||||
|
||||
setPacientesReais(pacientes);
|
||||
|
||||
if (pacientes.length === 0) {
|
||||
console.warn('⚠️ [REPORTS] Nenhum paciente encontrado no Supabase - verifique se há dados na tabela patients');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [REPORTS] Erro detalhado ao carregar pacientes:', {
|
||||
error,
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined
|
||||
});
|
||||
|
||||
setPacientesReais([]);
|
||||
alert('Erro ao carregar pacientes do Supabase: ' + (error instanceof Error ? error.message : String(error)));
|
||||
} finally {
|
||||
setCarregandoPacientes(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Calcular idade do paciente baseado na data de nascimento
|
||||
const calcularIdade = (birthDate: string | null | undefined): string => {
|
||||
if (!birthDate) return '';
|
||||
|
||||
const hoje = new Date();
|
||||
const nascimento = new Date(birthDate);
|
||||
let idade = hoje.getFullYear() - nascimento.getFullYear();
|
||||
const mesAtual = hoje.getMonth();
|
||||
const mesNascimento = nascimento.getMonth();
|
||||
|
||||
if (mesAtual < mesNascimento || (mesAtual === mesNascimento && hoje.getDate() < nascimento.getDate())) {
|
||||
idade--;
|
||||
}
|
||||
|
||||
return idade.toString();
|
||||
};
|
||||
|
||||
// Selecionar paciente para o relatório
|
||||
const selecionarPacienteParaRelatorio = (paciente: Paciente) => {
|
||||
setPacienteSelecionadoReport(paciente);
|
||||
|
||||
// Atualizar o formulário de relatório com dados do paciente
|
||||
setRelatorioMedico(prev => ({
|
||||
...prev,
|
||||
pacienteNome: paciente.full_name,
|
||||
pacienteCpf: paciente.cpf || '',
|
||||
pacienteIdade: calcularIdade(paciente.birth_date),
|
||||
}));
|
||||
|
||||
console.log('👤 [REPORTS] Paciente selecionado:', paciente);
|
||||
};
|
||||
|
||||
// Salvar relatório usando a API
|
||||
const salvarRelatorioAPI = async () => {
|
||||
if (!pacienteSelecionadoReport) {
|
||||
alert('Por favor, selecione um paciente.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!relatorioMedico.motivoRelatorio.trim()) {
|
||||
alert('Por favor, preencha o motivo do relatório.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('💾 [REPORTS] Salvando relatório...');
|
||||
|
||||
// Dados para enviar à API
|
||||
const reportData: CreateReportData = {
|
||||
patient_id: pacienteSelecionadoReport.id,
|
||||
doctor_id: user?.id || 'temp-doctor-id', // Usar ID do usuário logado
|
||||
report_type: 'Relatório Médico',
|
||||
chief_complaint: relatorioMedico.motivoRelatorio,
|
||||
clinical_history: relatorioMedico.historicoClinico,
|
||||
symptoms_and_signs: relatorioMedico.sinaisSintomas,
|
||||
physical_examination: '', // Pode adicionar campo no formulário se necessário
|
||||
complementary_exams: relatorioMedico.examesRealizados,
|
||||
exam_results: relatorioMedico.resultadosExames,
|
||||
diagnosis: relatorioMedico.diagnosticos,
|
||||
prognosis: relatorioMedico.prognostico,
|
||||
treatment_performed: relatorioMedico.tratamentosRealizados,
|
||||
objective_recommendations: relatorioMedico.recomendacoes || '',
|
||||
icd_code: relatorioMedico.cid,
|
||||
report_date: relatorioMedico.dataRelatorio,
|
||||
};
|
||||
|
||||
const novoRelatorio = await reportsApi.createNewReport(reportData);
|
||||
|
||||
console.log('✅ [REPORTS] Relatório salvo com sucesso:', novoRelatorio);
|
||||
|
||||
// Recarregar a lista de relatórios para garantir que está sincronizada
|
||||
await reportsApi.loadReports();
|
||||
|
||||
alert('Relatório médico salvo com sucesso!');
|
||||
|
||||
// Limpar formulário
|
||||
limparFormularioRelatorio();
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ [REPORTS] Erro ao salvar relatório:', error);
|
||||
alert('Erro ao salvar relatório: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
// Limpar formulário de relatório
|
||||
const limparFormularioRelatorio = () => {
|
||||
setRelatorioMedico({
|
||||
pacienteNome: "",
|
||||
pacienteCpf: "",
|
||||
pacienteIdade: "",
|
||||
profissionalNome: medico.nome,
|
||||
profissionalCrm: medico.identificacao,
|
||||
motivoRelatorio: "",
|
||||
historicoClinico: "",
|
||||
sinaisSintomas: "",
|
||||
examesRealizados: "",
|
||||
resultadosExames: "",
|
||||
diagnosticos: "",
|
||||
prognostico: "",
|
||||
tratamentosRealizados: "",
|
||||
recomendacoes: "",
|
||||
cid: "",
|
||||
dataRelatorio: new Date().toISOString().split('T')[0]
|
||||
});
|
||||
setPacienteSelecionadoReport(null);
|
||||
};
|
||||
|
||||
// Carregar relatórios existentes
|
||||
const carregarRelatorios = async () => {
|
||||
try {
|
||||
await reportsApi.loadReports();
|
||||
console.log('✅ [REPORTS] Relatórios carregados:', reportsApi.reports.length);
|
||||
} catch (error) {
|
||||
console.error('❌ [REPORTS] Erro ao carregar relatórios:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// useEffect para carregar dados iniciais
|
||||
useEffect(() => {
|
||||
if (activeSection === 'relatorios-medicos') {
|
||||
console.log('🔄 [REPORTS] Seção de relatórios ativada - carregando dados...');
|
||||
carregarPacientesReais();
|
||||
carregarRelatorios();
|
||||
}
|
||||
}, [activeSection]);
|
||||
|
||||
// Buscar pacientes faltantes por patient_id após carregar relatórios e pacientes
|
||||
useEffect(() => {
|
||||
if (activeSection !== 'relatorios-medicos') return;
|
||||
if (!reportsApi.reports || reportsApi.reports.length === 0) return;
|
||||
|
||||
// IDs de pacientes já carregados
|
||||
const idsPacientesReais = new Set(pacientesReais.map(p => String(p.id)));
|
||||
// IDs de pacientes presentes nos relatórios
|
||||
const idsRelatorios = Array.from(new Set(reportsApi.reports.map(r => String(r.patient_id)).filter(Boolean)));
|
||||
// IDs que faltam
|
||||
const idsFaltantes = idsRelatorios.filter(id => !idsPacientesReais.has(id));
|
||||
|
||||
if (idsFaltantes.length === 0) return;
|
||||
|
||||
// Buscar pacientes faltantes individualmente, apenas se o ID for string/UUID
|
||||
(async () => {
|
||||
const novosPacientes: Paciente[] = [];
|
||||
for (const id of idsFaltantes) {
|
||||
// Só busca se for string e não for número
|
||||
if (typeof id === 'string' && isNaN(Number(id))) {
|
||||
try {
|
||||
const paciente = await buscarPacientePorId(id);
|
||||
if (paciente) novosPacientes.push(paciente);
|
||||
} catch (e) {
|
||||
console.warn('⚠️ [REPORTS] Paciente não encontrado para o relatório:', id);
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ [REPORTS] Ignorando busca de paciente por ID não-string/UUID:', id);
|
||||
}
|
||||
}
|
||||
if (novosPacientes.length > 0) {
|
||||
setPacientesReais(prev => ([...prev, ...novosPacientes]));
|
||||
}
|
||||
})();
|
||||
}, [activeSection, reportsApi.reports, pacientesReais]);
|
||||
|
||||
|
||||
const handleDateClick = (arg: any) => {
|
||||
setSelectedDate(arg.dateStr);
|
||||
@ -2883,32 +3156,54 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Identificação do Paciente */}
|
||||
{/* Identificação do Paciente - USANDO API REAL */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-md font-medium text-primary border-b pb-2">Identificação do Paciente</h4>
|
||||
<div className="flex items-center justify-between border-b pb-2">
|
||||
<h4 className="text-md font-medium text-primary">Identificação do Paciente</h4>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={carregarPacientesReais}
|
||||
disabled={carregandoPacientes}
|
||||
className="flex items-center gap-2 text-xs"
|
||||
>
|
||||
🔄 {carregandoPacientes ? 'Carregando...' : 'Recarregar Pacientes'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pacienteNome">Nome do Paciente *</Label>
|
||||
<Select
|
||||
value={relatorioMedico.pacienteNome}
|
||||
value={pacienteSelecionadoReport?.id || ''}
|
||||
onValueChange={(value) => {
|
||||
const pacienteSelecionado = pacientes.find(p => p.nome === value);
|
||||
handleRelatorioChange('pacienteNome', value);
|
||||
if (pacienteSelecionado) {
|
||||
handleRelatorioChange('pacienteCpf', pacienteSelecionado.cpf);
|
||||
handleRelatorioChange('pacienteIdade', pacienteSelecionado.idade.toString());
|
||||
const paciente = pacientesReais.find(p => p.id === value);
|
||||
if (paciente) {
|
||||
selecionarPacienteParaRelatorio(paciente);
|
||||
}
|
||||
}}
|
||||
onOpenChange={(open) => {
|
||||
// Carregar pacientes quando o dropdown for aberto pela primeira vez
|
||||
if (open && pacientesReais.length === 0 && !carregandoPacientes) {
|
||||
console.log('🔄 [REPORTS] Dropdown aberto - carregando pacientes...');
|
||||
carregarPacientesReais();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione o paciente" />
|
||||
<SelectValue placeholder={carregandoPacientes ? "Carregando..." : "Selecione o paciente"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{pacientes.map((paciente) => (
|
||||
<SelectItem key={paciente.cpf} value={paciente.nome}>
|
||||
{paciente.nome}
|
||||
{carregandoPacientes ? (
|
||||
<SelectItem value="loading" disabled>Carregando pacientes...</SelectItem>
|
||||
) : pacientesReais.length === 0 ? (
|
||||
<SelectItem value="empty" disabled>Nenhum paciente encontrado</SelectItem>
|
||||
) : (
|
||||
pacientesReais.map((paciente) => (
|
||||
<SelectItem key={paciente.id} value={paciente.id}>
|
||||
{paciente.full_name} - {paciente.cpf || 'CPF não informado'}
|
||||
</SelectItem>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@ -2917,27 +3212,50 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
<Input
|
||||
id="pacienteCpf"
|
||||
value={relatorioMedico.pacienteCpf}
|
||||
onChange={(e) => handleRelatorioChange('pacienteCpf', e.target.value)}
|
||||
placeholder="000.000.000-00"
|
||||
disabled
|
||||
className="bg-muted"
|
||||
placeholder="CPF será preenchido automaticamente"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pacienteIdade">Idade</Label>
|
||||
<Input
|
||||
id="pacienteIdade"
|
||||
type="number"
|
||||
type="text"
|
||||
value={relatorioMedico.pacienteIdade}
|
||||
onChange={(e) => handleRelatorioChange('pacienteIdade', e.target.value)}
|
||||
placeholder="Idade do paciente"
|
||||
disabled
|
||||
className="bg-muted"
|
||||
placeholder="Idade será calculada automaticamente"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Informações adicionais do paciente selecionado */}
|
||||
{pacienteSelecionadoReport && (
|
||||
<div className="bg-muted/50 p-4 rounded-lg">
|
||||
<h5 className="font-medium text-sm text-muted-foreground mb-2">Informações do Paciente Selecionado:</h5>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="font-medium">Nome Completo:</span><br />
|
||||
<span>{pacienteSelecionadoReport.full_name}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Email:</span><br />
|
||||
<span>{pacienteSelecionadoReport.email || 'Não informado'}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Telefone:</span><br />
|
||||
<span>{pacienteSelecionadoReport.phone_mobile || 'Não informado'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Informações do Relatório */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-md font-medium text-primary border-b pb-2">Informações do Relatório</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="motivoRelatorio">Motivo do Relatório *</Label>
|
||||
<Textarea
|
||||
@ -2948,6 +3266,15 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cid">CID</Label>
|
||||
<Input
|
||||
id="cid"
|
||||
value={relatorioMedico.cid}
|
||||
onChange={(e) => handleRelatorioChange('cid', e.target.value)}
|
||||
placeholder="Ex: A00, B20, C34..."
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="dataRelatorio">Data do Relatório</Label>
|
||||
<Input
|
||||
@ -2958,7 +3285,6 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="historicoClinico">Histórico Clínico Conciso</Label>
|
||||
<Textarea
|
||||
@ -3068,19 +3394,54 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
<Button variant="outline" onClick={handleCancelarEdicaoRelatorio} className="hover:bg-blue-50 dark:hover:bg-accent dark:hover:text-accent-foreground">
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onClick={handleSalvarRelatorio} className="flex items-center gap-2">
|
||||
<Button
|
||||
onClick={salvarRelatorioAPI}
|
||||
className="flex items-center gap-2"
|
||||
disabled={reportsApi.loading || !pacienteSelecionadoReport}
|
||||
>
|
||||
<FileCheck className="h-4 w-4" />
|
||||
{editandoRelatorio ? 'Atualizar Relatório' : 'Salvar Relatório'}
|
||||
{reportsApi.loading ? 'Salvando...' : (editandoRelatorio ? 'Atualizar Relatório' : 'Salvar Relatório')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Lista de Relatórios Existentes */}
|
||||
{/* Lista de Relatórios da API */}
|
||||
<div className="bg-card shadow-md rounded-lg p-6">
|
||||
<h3 className="text-lg font-semibold mb-4 text-foreground">Relatórios Médicos Salvos</h3>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold text-foreground">Relatórios Médicos</h3>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={carregarRelatorios}
|
||||
disabled={reportsApi.loading}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<FileCheck className="h-4 w-4" />
|
||||
{reportsApi.loading ? 'Carregando...' : 'Atualizar'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{relatoriosMedicos.length === 0 ? (
|
||||
{reportsApi.error && (
|
||||
<div className="bg-destructive/10 border border-destructive/20 rounded-lg p-4 mb-4">
|
||||
<p className="text-destructive text-sm">{reportsApi.error}</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={reportsApi.clearError}
|
||||
className="mt-2"
|
||||
>
|
||||
Limpar erro
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{reportsApi.loading ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<FileCheck className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50 animate-pulse" />
|
||||
<p className="text-lg mb-2">Carregando relatórios...</p>
|
||||
</div>
|
||||
) : reportsApi.reports.length === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<FileCheck className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
||||
<p className="text-lg mb-2">Nenhum relatório médico encontrado</p>
|
||||
@ -3088,30 +3449,50 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{relatoriosMedicos.map((relatorio) => (
|
||||
<div key={relatorio.id} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
||||
{reportsApi.reports.filter(relatorio => relatorio != null).map((relatorio, idx) => {
|
||||
// Buscar dados do paciente pelos pacientes carregados
|
||||
const pacienteEncontrado = pacientesReais.find(p => p.id === relatorio?.patient_id);
|
||||
const nomeExibir = relatorio?.patient?.full_name || pacienteEncontrado?.full_name || 'Paciente não identificado';
|
||||
const cpfExibir = relatorio?.patient?.cpf || pacienteEncontrado?.cpf || 'Não informado';
|
||||
|
||||
return (
|
||||
<div key={relatorio?.id ? `report-${relatorio.id}-${idx}` : `report-idx-${idx}`} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
||||
<div className="flex justify-between items-start mb-3">
|
||||
<div>
|
||||
<h4 className="font-semibold text-lg">{relatorio.pacienteNome}</h4>
|
||||
<p className="text-sm text-muted-foreground">CPF: {relatorio.pacienteCpf} • Idade: {relatorio.pacienteIdade} anos</p>
|
||||
<p className="text-sm text-muted-foreground">Data do relatório: {new Date(relatorio.dataRelatorio).toLocaleDateString('pt-BR')}</p>
|
||||
<p className="text-xs text-muted-foreground/70">Gerado em: {relatorio.dataGeracao}</p>
|
||||
<h4 className="font-semibold text-lg">
|
||||
{nomeExibir}
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
CPF: {cpfExibir} •
|
||||
Tipo: {relatorio?.report_type || 'Relatório Médico'}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Data do relatório: {relatorio?.report_date ? new Date(relatorio.report_date).toLocaleDateString('pt-BR') : 'Data não informada'}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground/70">
|
||||
Criado em: {relatorio?.created_at ? new Date(relatorio.created_at).toLocaleDateString('pt-BR') : 'Data não informada'}
|
||||
</p>
|
||||
<p className="text-sm text-foreground/80 mt-2 line-clamp-2">
|
||||
<strong>Motivo:</strong> {relatorio?.chief_complaint || 'Não informado'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleEditarRelatorio(relatorio)}
|
||||
onClick={() => relatorio?.id && reportsApi.loadReportById(relatorio.id)}
|
||||
className="flex items-center gap-1"
|
||||
disabled={!relatorio?.id}
|
||||
>
|
||||
<Edit className="h-3 w-3" />
|
||||
Editar
|
||||
<Eye className="h-3 w-3" />
|
||||
Visualizar
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={() => handleExcluirRelatorio(relatorio.id)}
|
||||
onClick={() => relatorio?.id && reportsApi.deleteExistingReport(relatorio.id)}
|
||||
className="flex items-center gap-1"
|
||||
disabled={reportsApi.loading || !relatorio?.id}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
Excluir
|
||||
@ -3121,26 +3502,34 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="font-medium text-primary">Motivo:</span>
|
||||
<p className="text-foreground mt-1">{relatorio.motivoRelatorio}</p>
|
||||
<span className="font-medium text-primary">Queixa Principal:</span>
|
||||
<p className="text-foreground mt-1 line-clamp-3">{relatorio.chief_complaint}</p>
|
||||
</div>
|
||||
|
||||
{relatorio.diagnosticos && (
|
||||
{relatorio.diagnosis && (
|
||||
<div>
|
||||
<span className="font-medium text-primary">Diagnóstico(s):</span>
|
||||
<p className="text-foreground mt-1">{relatorio.diagnosticos}</p>
|
||||
<p className="text-foreground mt-1 line-clamp-3">{relatorio.diagnosis}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{relatorio.recomendacoes && (
|
||||
{relatorio.objective_recommendations && (
|
||||
<div className="md:col-span-2">
|
||||
<span className="font-medium text-primary">Recomendações:</span>
|
||||
<p className="text-foreground mt-1">{relatorio.recomendacoes}</p>
|
||||
<p className="text-foreground mt-1 line-clamp-3">{relatorio.objective_recommendations}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{relatorio.icd_code && (
|
||||
<div>
|
||||
<span className="font-medium text-primary">CID:</span>
|
||||
<p className="text-foreground mt-1">{relatorio.icd_code}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
219
susconecta/hooks/useReports.ts
Normal file
219
susconecta/hooks/useReports.ts
Normal file
@ -0,0 +1,219 @@
|
||||
// hooks/useReports.ts
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Report,
|
||||
CreateReportData,
|
||||
UpdateReportData,
|
||||
ApiError
|
||||
} from '@/types/report-types';
|
||||
import {
|
||||
listarRelatorios,
|
||||
buscarRelatorioPorId,
|
||||
criarRelatorio,
|
||||
atualizarRelatorio,
|
||||
deletarRelatorio,
|
||||
listarRelatoriosPorPaciente,
|
||||
listarRelatoriosPorMedico
|
||||
} from '@/lib/reports';
|
||||
|
||||
interface UseReportsReturn {
|
||||
// Estados
|
||||
reports: Report[];
|
||||
selectedReport: Report | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
|
||||
// Ações
|
||||
loadReports: () => Promise<void>;
|
||||
loadReportById: (id: string) => Promise<void>;
|
||||
createNewReport: (data: CreateReportData) => Promise<Report>;
|
||||
updateExistingReport: (id: string, data: UpdateReportData) => Promise<Report>;
|
||||
deleteExistingReport: (id: string) => Promise<void>;
|
||||
loadReportsByPatient: (patientId: string) => Promise<void>;
|
||||
loadReportsByDoctor: (doctorId: string) => Promise<void>;
|
||||
clearError: () => void;
|
||||
clearSelectedReport: () => void;
|
||||
}
|
||||
|
||||
export function useReports(): UseReportsReturn {
|
||||
// Estados
|
||||
const [reports, setReports] = useState<Report[]>([]);
|
||||
const [selectedReport, setSelectedReport] = useState<Report | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Função para tratar erros
|
||||
const handleError = useCallback((error: any) => {
|
||||
console.error('❌ [useReports] Erro:', error);
|
||||
|
||||
if (error && typeof error === 'object' && 'message' in error) {
|
||||
setError(error.message);
|
||||
} else if (typeof error === 'string') {
|
||||
setError(error);
|
||||
} else {
|
||||
setError('Ocorreu um erro inesperado');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Carregar todos os relatórios
|
||||
const loadReports = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const data = await listarRelatorios();
|
||||
setReports(data);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError]);
|
||||
|
||||
// Carregar um relatório específico
|
||||
const loadReportById = useCallback(async (id: string) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const report = await buscarRelatorioPorId(id);
|
||||
setSelectedReport(report);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError]);
|
||||
|
||||
// Criar novo relatório
|
||||
const createNewReport = useCallback(async (data: CreateReportData): Promise<Report> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const newReport = await criarRelatorio(data);
|
||||
|
||||
// Adicionar o novo relatório à lista
|
||||
setReports(prev => [newReport, ...prev]);
|
||||
|
||||
return newReport;
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError]);
|
||||
|
||||
// Atualizar relatório existente
|
||||
const updateExistingReport = useCallback(async (id: string, data: UpdateReportData): Promise<Report> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const updatedReport = await atualizarRelatorio(id, data);
|
||||
|
||||
// Atualizar na lista
|
||||
setReports(prev =>
|
||||
prev.map(report =>
|
||||
report.id === id ? updatedReport : report
|
||||
)
|
||||
);
|
||||
|
||||
// Atualizar o selecionado se for o mesmo
|
||||
if (selectedReport?.id === id) {
|
||||
setSelectedReport(updatedReport);
|
||||
}
|
||||
|
||||
return updatedReport;
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError, selectedReport]);
|
||||
|
||||
// Deletar relatório
|
||||
const deleteExistingReport = useCallback(async (id: string): Promise<void> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await deletarRelatorio(id);
|
||||
|
||||
// Remover da lista
|
||||
setReports(prev => prev.filter(report => report.id !== id));
|
||||
|
||||
// Limpar seleção se for o mesmo
|
||||
if (selectedReport?.id === id) {
|
||||
setSelectedReport(null);
|
||||
}
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError, selectedReport]);
|
||||
|
||||
// Carregar relatórios por paciente
|
||||
const loadReportsByPatient = useCallback(async (patientId: string) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const data = await listarRelatoriosPorPaciente(patientId);
|
||||
setReports(data);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError]);
|
||||
|
||||
// Carregar relatórios por médico
|
||||
const loadReportsByDoctor = useCallback(async (doctorId: string) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const data = await listarRelatoriosPorMedico(doctorId);
|
||||
setReports(data);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [handleError]);
|
||||
|
||||
// Limpar erro
|
||||
const clearError = useCallback(() => {
|
||||
setError(null);
|
||||
}, []);
|
||||
|
||||
// Limpar relatório selecionado
|
||||
const clearSelectedReport = useCallback(() => {
|
||||
setSelectedReport(null);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// Estados
|
||||
reports,
|
||||
selectedReport,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// Ações
|
||||
loadReports,
|
||||
loadReportById,
|
||||
createNewReport,
|
||||
updateExistingReport,
|
||||
deleteExistingReport,
|
||||
loadReportsByPatient,
|
||||
loadReportsByDoctor,
|
||||
clearError,
|
||||
clearSelectedReport,
|
||||
};
|
||||
}
|
||||
@ -320,7 +320,12 @@ export async function buscarPacientes(termo: string): Promise<Paciente[]> {
|
||||
}
|
||||
|
||||
export async function buscarPacientePorId(id: string | number): Promise<Paciente> {
|
||||
const url = `${REST}/patients?id=eq.${id}`;
|
||||
// Se for string e não for só número, coloca aspas duplas (para UUID/texto)
|
||||
let idParam: string | number = id;
|
||||
if (typeof id === 'string' && isNaN(Number(id))) {
|
||||
idParam = `\"${id}\"`;
|
||||
}
|
||||
const url = `${REST}/patients?id=eq.${idParam}`;
|
||||
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
|
||||
const arr = await parse<Paciente[]>(res);
|
||||
if (!arr?.length) throw new Error("404: Paciente não encontrado");
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
289
susconecta/lib/reports.ts
Normal file
289
susconecta/lib/reports.ts
Normal file
@ -0,0 +1,289 @@
|
||||
/**
|
||||
* Atualiza um relatório existente (edição)
|
||||
* @param id ID do relatório a ser atualizado
|
||||
* @param dados Dados a serem atualizados no relatório
|
||||
*/
|
||||
export async function editarRelatorio(id: string, dados: Partial<{
|
||||
patient_id: string;
|
||||
order_number: string;
|
||||
exam: string;
|
||||
diagnosis: string;
|
||||
conclusion: string;
|
||||
cid_code: string;
|
||||
content_html: string;
|
||||
content_json: any;
|
||||
status: string;
|
||||
requested_by: string;
|
||||
due_at: string;
|
||||
hide_date: boolean;
|
||||
hide_signature: boolean;
|
||||
}>): Promise<any> {
|
||||
const url = `${BASE_API_RELATORIOS}/${id}`;
|
||||
const cabecalhos: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
cabecalhos['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
const resposta = await fetch(url, {
|
||||
method: 'PATCH',
|
||||
headers: cabecalhos,
|
||||
body: JSON.stringify(dados),
|
||||
});
|
||||
if (!resposta.ok) throw new Error('Erro ao atualizar relatório');
|
||||
return resposta.json();
|
||||
}
|
||||
// services/reports.ts
|
||||
|
||||
import {
|
||||
Report,
|
||||
CreateReportData,
|
||||
UpdateReportData,
|
||||
ReportsResponse,
|
||||
ReportResponse,
|
||||
ApiError
|
||||
} from '@/types/report-types';
|
||||
|
||||
// URL base da API Mock
|
||||
const BASE_API_RELATORIOS = 'https://mock.apidog.com/m1/1053378-0-default/rest/v1/reports';
|
||||
|
||||
// Cabeçalhos base para as requisições
|
||||
function obterCabecalhos(): HeadersInit {
|
||||
const cabecalhos: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
|
||||
};
|
||||
|
||||
// Adiciona token de autenticação do localStorage se disponível
|
||||
if (typeof window !== 'undefined') {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
cabecalhos['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
return cabecalhos;
|
||||
}
|
||||
|
||||
// Função para tratar erros da API
|
||||
async function tratarRespostaApi<T>(resposta: Response): Promise<T> {
|
||||
if (!resposta.ok) {
|
||||
let mensagemErro = `HTTP ${resposta.status}: ${resposta.statusText}`;
|
||||
try {
|
||||
const dadosErro = await resposta.json();
|
||||
mensagemErro = dadosErro.message || dadosErro.error || mensagemErro;
|
||||
} catch (e) {
|
||||
// Se não conseguir parsear como JSON, usa a mensagem de status HTTP
|
||||
}
|
||||
const erro: ApiError = {
|
||||
message: mensagemErro,
|
||||
code: resposta.status.toString(),
|
||||
};
|
||||
throw erro;
|
||||
}
|
||||
const dados = await resposta.json();
|
||||
return dados;
|
||||
}
|
||||
|
||||
// ===== SERVIÇOS DE RELATÓRIOS MÉDICOS =====
|
||||
|
||||
/**
|
||||
* Lista relatórios médicos com filtros opcionais (patient_id, status)
|
||||
*/
|
||||
export async function listarRelatorios(filtros?: { patient_id?: string; status?: string }): Promise<Report[]> {
|
||||
// Monta query string se houver filtros
|
||||
let url = BASE_API_RELATORIOS;
|
||||
if (filtros && (filtros.patient_id || filtros.status)) {
|
||||
const params = new URLSearchParams();
|
||||
if (filtros.patient_id) params.append('patient_id', filtros.patient_id);
|
||||
if (filtros.status) params.append('status', filtros.status);
|
||||
url += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
// Monta cabeçalhos conforme cURL
|
||||
const cabecalhos: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
cabecalhos['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
const resposta = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: cabecalhos,
|
||||
});
|
||||
if (!resposta.ok) throw new Error('Erro ao buscar relatórios');
|
||||
const dados = await resposta.json();
|
||||
if (Array.isArray(dados)) return dados;
|
||||
if (dados && Array.isArray(dados.data)) return dados.data;
|
||||
for (const chave in dados) {
|
||||
if (Array.isArray(dados[chave])) return dados[chave];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca um relatório específico por ID
|
||||
*/
|
||||
export async function buscarRelatorioPorId(id: string): Promise<Report> {
|
||||
try {
|
||||
console.log('🔍 [API RELATÓRIOS] Buscando relatório ID:', id);
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, {
|
||||
method: 'GET',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<ReportResponse>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatório encontrado:', resultado.data);
|
||||
return resultado.data;
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatório:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cria um novo relatório médico
|
||||
*/
|
||||
export async function criarRelatorio(dadosRelatorio: CreateReportData): Promise<Report> {
|
||||
try {
|
||||
console.log('📝 [API RELATÓRIOS] Criando novo relatório...');
|
||||
console.log('📤 [API RELATÓRIOS] Dados enviados:', dadosRelatorio);
|
||||
const resposta = await fetch(BASE_API_RELATORIOS, {
|
||||
method: 'POST',
|
||||
headers: obterCabecalhos(),
|
||||
body: JSON.stringify(dadosRelatorio),
|
||||
});
|
||||
console.log('📝 [API RELATÓRIOS] Status da criação:', resposta.status);
|
||||
console.log('📝 [API RELATÓRIOS] Response OK:', resposta.ok);
|
||||
console.log('📝 [API RELATÓRIOS] Response URL:', resposta.url);
|
||||
if (!resposta.ok) {
|
||||
let mensagemErro = `HTTP ${resposta.status}: ${resposta.statusText}`;
|
||||
try {
|
||||
const dadosErro = await resposta.json();
|
||||
mensagemErro = dadosErro.message || dadosErro.error || mensagemErro;
|
||||
console.log('📝 [API RELATÓRIOS] Erro da API:', dadosErro);
|
||||
} catch (e) {
|
||||
console.log('📝 [API RELATÓRIOS] Não foi possível parsear erro como JSON');
|
||||
}
|
||||
const erro: ApiError = {
|
||||
message: mensagemErro,
|
||||
code: resposta.status.toString(),
|
||||
};
|
||||
throw erro;
|
||||
}
|
||||
const resultadoBruto = await resposta.json();
|
||||
console.log('📝 [API RELATÓRIOS] Resposta bruta da criação:', resultadoBruto);
|
||||
console.log('📝 [API RELATÓRIOS] Tipo da resposta:', typeof resultadoBruto);
|
||||
console.log('📝 [API RELATÓRIOS] Chaves da resposta:', Object.keys(resultadoBruto || {}));
|
||||
let relatorioCriado: Report;
|
||||
// Verifica formato da resposta similar ao listarRelatorios
|
||||
if (resultadoBruto && resultadoBruto.data) {
|
||||
relatorioCriado = resultadoBruto.data;
|
||||
} else if (resultadoBruto && resultadoBruto.id) {
|
||||
relatorioCriado = resultadoBruto;
|
||||
} else if (Array.isArray(resultadoBruto) && resultadoBruto.length > 0) {
|
||||
relatorioCriado = resultadoBruto[0];
|
||||
} else {
|
||||
console.warn('📝 [API RELATÓRIOS] Formato de resposta inesperado, criando relatório local');
|
||||
relatorioCriado = {
|
||||
id: 'local-' + Date.now() + '-' + Math.random().toString(36).substr(2, 5),
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
...dadosRelatorio
|
||||
};
|
||||
}
|
||||
console.log('✅ [API RELATÓRIOS] Relatório processado:', relatorioCriado);
|
||||
return relatorioCriado;
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao criar relatório:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza um relatório existente
|
||||
*/
|
||||
export async function atualizarRelatorio(id: string, dadosRelatorio: UpdateReportData): Promise<Report> {
|
||||
try {
|
||||
console.log('📝 [API RELATÓRIOS] Atualizando relatório ID:', id);
|
||||
console.log('📤 [API RELATÓRIOS] Dados:', dadosRelatorio);
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: obterCabecalhos(),
|
||||
body: JSON.stringify(dadosRelatorio),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<ReportResponse>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatório atualizado:', resultado.data);
|
||||
return resultado.data;
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao atualizar relatório:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleta um relatório
|
||||
*/
|
||||
export async function deletarRelatorio(id: string): Promise<void> {
|
||||
try {
|
||||
console.log('🗑️ [API RELATÓRIOS] Deletando relatório ID:', id);
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
await tratarRespostaApi<void>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatório deletado com sucesso');
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao deletar relatório:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista relatórios de um paciente específico
|
||||
*/
|
||||
export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<Report[]> {
|
||||
try {
|
||||
console.log('👤 [API RELATÓRIOS] Buscando relatórios do paciente:', idPaciente);
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}?patient_id=${idPaciente}`, {
|
||||
method: 'GET',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<ReportsResponse>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios do paciente encontrados:', resultado.data?.length || 0);
|
||||
return resultado.data || [];
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do paciente:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista relatórios de um médico específico
|
||||
*/
|
||||
export async function listarRelatoriosPorMedico(idMedico: string): Promise<Report[]> {
|
||||
try {
|
||||
console.log('👨⚕️ [API RELATÓRIOS] Buscando relatórios do médico:', idMedico);
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}?doctor_id=${idMedico}`, {
|
||||
method: 'GET',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<ReportsResponse>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios do médico encontrados:', resultado.data?.length || 0);
|
||||
return resultado.data || [];
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do médico:', erro);
|
||||
throw erro;
|
||||
}
|
||||
}
|
||||
107
susconecta/types/report-types.ts
Normal file
107
susconecta/types/report-types.ts
Normal file
@ -0,0 +1,107 @@
|
||||
// Tipo de erro padrão para respostas de API
|
||||
export interface ApiError {
|
||||
message: string;
|
||||
code?: string;
|
||||
}
|
||||
// Este arquivo foi renomeado de report.ts para report-types.ts para evitar confusão com outros arquivos de lógica.
|
||||
// Tipos para o endpoint de Relatórios Médicos
|
||||
export interface Report {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
doctor_id: string;
|
||||
report_type: string;
|
||||
chief_complaint: string;
|
||||
clinical_history: string;
|
||||
symptoms_and_signs: string;
|
||||
physical_examination: string;
|
||||
complementary_exams: string;
|
||||
exam_results: string;
|
||||
diagnosis: string;
|
||||
prognosis?: string;
|
||||
treatment_performed: string;
|
||||
objective_recommendations: string;
|
||||
icd_code?: string;
|
||||
report_date: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
|
||||
// Dados expandidos (quando incluir dados relacionados)
|
||||
patient?: {
|
||||
id: string;
|
||||
full_name: string;
|
||||
cpf?: string;
|
||||
birth_date?: string;
|
||||
};
|
||||
|
||||
doctor?: {
|
||||
id: string;
|
||||
full_name: string;
|
||||
crm?: string;
|
||||
specialty?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Dados para criar um novo relatório
|
||||
export interface CreateReportData {
|
||||
patient_id: string;
|
||||
doctor_id: string;
|
||||
report_type: string;
|
||||
chief_complaint: string;
|
||||
clinical_history: string;
|
||||
symptoms_and_signs: string;
|
||||
physical_examination: string;
|
||||
complementary_exams: string;
|
||||
exam_results: string;
|
||||
diagnosis: string;
|
||||
prognosis?: string;
|
||||
treatment_performed: string;
|
||||
objective_recommendations: string;
|
||||
icd_code?: string;
|
||||
report_date: string;
|
||||
}
|
||||
|
||||
// Dados para atualizar um relatório existente
|
||||
export interface UpdateReportData extends Partial<CreateReportData> {
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
// Resposta da API ao listar relatórios
|
||||
export interface ReportsResponse {
|
||||
data: Report[];
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
// Resposta da API ao criar/atualizar um relatório
|
||||
export interface ReportResponse {
|
||||
data: Report;
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
// Dados do formulário (adaptado para a estrutura do front-end existente)
|
||||
export interface ReportFormData {
|
||||
// Identificação do Profissional
|
||||
profissionalNome: string;
|
||||
profissionalCrm: string;
|
||||
|
||||
// Identificação do Paciente
|
||||
pacienteId: string;
|
||||
pacienteNome: string;
|
||||
pacienteCpf: string;
|
||||
pacienteIdade: string;
|
||||
|
||||
// Informações do Relatório
|
||||
motivoRelatorio: string;
|
||||
cid?: string;
|
||||
dataRelatorio: string;
|
||||
|
||||
// Histórico Clínico
|
||||
historicoClinico: string;
|
||||
|
||||
// Sinais, Sintomas e Exames
|
||||
sinaisSintomas: string;
|
||||
examesRealizados: string;
|
||||
resultadosExames: string;
|
||||
// ...restante do código...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user