feature/patiente-medical-assignment #45

Merged
JoaoGustavo-dev merged 21 commits from feature/patiente-medical-assignment into develop 2025-10-12 03:57:36 +00:00
6 changed files with 366 additions and 2155 deletions
Showing only changes of commit 2bb0b06375 - Show all commits

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,7 @@ import { useState, useEffect, useCallback } from 'react';
import { import {
Report, Report,
CreateReportData, CreateReportData,
UpdateReportData, UpdateReportData
ApiError
} from '@/types/report-types'; } from '@/types/report-types';
import { import {
listarRelatorios, listarRelatorios,

View File

@ -14,11 +14,8 @@ export type ApiOk<T = any> = {
total?: number; total?: number;
}; };
}; };
// ===== TIPOS COMUNS =====
export type Endereco = { export type Endereco = {
cep?: string; cep?: string;
logradouro?: string;
numero?: string; numero?: string;
complemento?: string; complemento?: string;
bairro?: string; bairro?: string;
@ -245,14 +242,19 @@ export async function listarPacientes(params?: {
}): Promise<Paciente[]> { }): Promise<Paciente[]> {
const qs = new URLSearchParams(); const qs = new URLSearchParams();
if (params?.q) qs.set("q", params.q); if (params?.q) qs.set("q", params.q);
let url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients`;
const qsString = qs.toString();
if (qsString) url += `?${qsString}`;
// Use baseHeaders() so the current user token (from local/session storage) and apikey are sent
const headers: HeadersInit = {
...baseHeaders(),
...rangeHeaders(params?.page, params?.limit),
};
const url = `${REST}/patients${qs.toString() ? `?${qs.toString()}` : ""}`;
const res = await fetch(url, { const res = await fetch(url, {
method: "GET", method: "GET",
headers: { headers,
...baseHeaders(),
...rangeHeaders(params?.page, params?.limit),
},
}); });
return await parse<Paciente[]>(res); return await parse<Paciente[]>(res);
} }
@ -299,7 +301,7 @@ export async function buscarPacientes(termo: string): Promise<Paciente[]> {
// Executa as buscas e combina resultados únicos // Executa as buscas e combina resultados únicos
for (const query of queries) { for (const query of queries) {
try { try {
const url = `${REST}/patients?${query}&limit=10`; const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients?${query}&limit=10`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() }); const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const arr = await parse<Paciente[]>(res); const arr = await parse<Paciente[]>(res);
@ -325,7 +327,7 @@ export async function buscarPacientePorId(id: string | number): Promise<Paciente
if (typeof id === 'string' && isNaN(Number(id))) { if (typeof id === 'string' && isNaN(Number(id))) {
idParam = `\"${id}\"`; idParam = `\"${id}\"`;
} }
const url = `${REST}/patients?id=eq.${idParam}`; const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients?id=eq.${idParam}`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() }); const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const arr = await parse<Paciente[]>(res); const arr = await parse<Paciente[]>(res);
if (!arr?.length) throw new Error("404: Paciente não encontrado"); if (!arr?.length) throw new Error("404: Paciente não encontrado");
@ -333,7 +335,7 @@ export async function buscarPacientePorId(id: string | number): Promise<Paciente
} }
export async function criarPaciente(input: PacienteInput): Promise<Paciente> { export async function criarPaciente(input: PacienteInput): Promise<Paciente> {
const url = `${REST}/patients`; const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients`;
const res = await fetch(url, { const res = await fetch(url, {
method: "POST", method: "POST",
headers: withPrefer({ ...baseHeaders(), "Content-Type": "application/json" }, "return=representation"), headers: withPrefer({ ...baseHeaders(), "Content-Type": "application/json" }, "return=representation"),
@ -344,7 +346,7 @@ export async function criarPaciente(input: PacienteInput): Promise<Paciente> {
} }
export async function atualizarPaciente(id: string | number, input: PacienteInput): Promise<Paciente> { export async function atualizarPaciente(id: string | number, input: PacienteInput): Promise<Paciente> {
const url = `${REST}/patients?id=eq.${id}`; const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients?id=eq.${id}`;
const res = await fetch(url, { const res = await fetch(url, {
method: "PATCH", method: "PATCH",
headers: withPrefer({ ...baseHeaders(), "Content-Type": "application/json" }, "return=representation"), headers: withPrefer({ ...baseHeaders(), "Content-Type": "application/json" }, "return=representation"),
@ -355,7 +357,7 @@ export async function atualizarPaciente(id: string | number, input: PacienteInpu
} }
export async function excluirPaciente(id: string | number): Promise<void> { export async function excluirPaciente(id: string | number): Promise<void> {
const url = `${REST}/patients?id=eq.${id}`; const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients?id=eq.${id}`;
const res = await fetch(url, { method: "DELETE", headers: baseHeaders() }); const res = await fetch(url, { method: "DELETE", headers: baseHeaders() });
await parse<any>(res); await parse<any>(res);
} }

View File

@ -45,29 +45,29 @@ import {
CreateReportData, CreateReportData,
UpdateReportData, UpdateReportData,
ReportsResponse, ReportsResponse,
ReportResponse, ReportResponse
ApiError
} from '@/types/report-types'; } from '@/types/report-types';
// URL base da API Mock // Definição local para ApiError
const BASE_API_RELATORIOS = 'https://mock.apidog.com/m1/1053378-0-default/rest/v1/reports'; type ApiError = {
message: string;
code: string;
};
// Cabeçalhos base para as requisições // URL base da API Supabase
function obterCabecalhos(): HeadersInit { const BASE_API_RELATORIOS = 'https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/reports';
// Cabeçalhos base para as requisições Supabase
function obterCabecalhos(token?: string): HeadersInit {
const cabecalhos: HeadersInit = { const cabecalhos: HeadersInit = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json', 'Accept': 'application/json',
'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ', 'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
'Prefer': 'return=representation',
}; };
if (token) {
// Adiciona token de autenticação do localStorage se disponível cabecalhos['Authorization'] = `Bearer ${token}`;
if (typeof window !== 'undefined') {
const token = localStorage.getItem('token');
if (token) {
cabecalhos['Authorization'] = `Bearer ${token}`;
}
} }
return cabecalhos; return cabecalhos;
} }
@ -139,13 +139,15 @@ export async function listarRelatorios(filtros?: { patient_id?: string; status?:
export async function buscarRelatorioPorId(id: string): Promise<Report> { export async function buscarRelatorioPorId(id: string): Promise<Report> {
try { try {
console.log('🔍 [API RELATÓRIOS] Buscando relatório ID:', id); console.log('🔍 [API RELATÓRIOS] Buscando relatório ID:', id);
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, { const resposta = await fetch(`${BASE_API_RELATORIOS}?id=eq.${id}`, {
method: 'GET', method: 'GET',
headers: obterCabecalhos(), headers: obterCabecalhos(),
}); });
const resultado = await tratarRespostaApi<ReportResponse>(resposta); const resultado = await tratarRespostaApi<Report[]>(resposta);
console.log('✅ [API RELATÓRIOS] Relatório encontrado:', resultado.data); const relatorio = Array.isArray(resultado) && resultado.length > 0 ? resultado[0] : null;
return resultado.data; console.log('✅ [API RELATÓRIOS] Relatório encontrado:', relatorio);
if (!relatorio) throw new Error('Relatório não encontrado');
return relatorio;
} catch (erro) { } catch (erro) {
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatório:', erro); console.error('❌ [API RELATÓRIOS] Erro ao buscar relatório:', erro);
throw erro; throw erro;
@ -155,60 +157,30 @@ export async function buscarRelatorioPorId(id: string): Promise<Report> {
/** /**
* Cria um novo relatório médico * Cria um novo relatório médico
*/ */
export async function criarRelatorio(dadosRelatorio: CreateReportData): Promise<Report> { export async function criarRelatorio(dadosRelatorio: CreateReportData, token?: string): Promise<Report> {
try { const resposta = await fetch(BASE_API_RELATORIOS, {
console.log('📝 [API RELATÓRIOS] Criando novo relatório...'); method: 'POST',
console.log('📤 [API RELATÓRIOS] Dados enviados:', dadosRelatorio); headers: obterCabecalhos(token),
const resposta = await fetch(BASE_API_RELATORIOS, { body: JSON.stringify(dadosRelatorio),
method: 'POST', });
headers: obterCabecalhos(), if (!resposta.ok) {
body: JSON.stringify(dadosRelatorio), let mensagemErro = `HTTP ${resposta.status}: ${resposta.statusText}`;
}); try {
console.log('📝 [API RELATÓRIOS] Status da criação:', resposta.status); const dadosErro = await resposta.json();
console.log('📝 [API RELATÓRIOS] Response OK:', resposta.ok); mensagemErro = dadosErro.message || dadosErro.error || mensagemErro;
console.log('📝 [API RELATÓRIOS] Response URL:', resposta.url); } catch (e) {}
if (!resposta.ok) { const erro: any = {
let mensagemErro = `HTTP ${resposta.status}: ${resposta.statusText}`; message: mensagemErro,
try { code: resposta.status.toString(),
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; throw erro;
} }
const resultado = await resposta.json();
// Supabase retorna array
if (Array.isArray(resultado) && resultado.length > 0) {
return resultado[0];
}
throw new Error('Resposta inesperada da API Supabase');
} }
/** /**
@ -218,14 +190,16 @@ export async function atualizarRelatorio(id: string, dadosRelatorio: UpdateRepor
try { try {
console.log('📝 [API RELATÓRIOS] Atualizando relatório ID:', id); console.log('📝 [API RELATÓRIOS] Atualizando relatório ID:', id);
console.log('📤 [API RELATÓRIOS] Dados:', dadosRelatorio); console.log('📤 [API RELATÓRIOS] Dados:', dadosRelatorio);
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, { const resposta = await fetch(`${BASE_API_RELATORIOS}?id=eq.${id}`, {
method: 'PATCH', method: 'PATCH',
headers: obterCabecalhos(), headers: obterCabecalhos(),
body: JSON.stringify(dadosRelatorio), body: JSON.stringify(dadosRelatorio),
}); });
const resultado = await tratarRespostaApi<ReportResponse>(resposta); const resultado = await tratarRespostaApi<Report[]>(resposta);
console.log('✅ [API RELATÓRIOS] Relatório atualizado:', resultado.data); const relatorio = Array.isArray(resultado) && resultado.length > 0 ? resultado[0] : null;
return resultado.data; console.log('✅ [API RELATÓRIOS] Relatório atualizado:', relatorio);
if (!relatorio) throw new Error('Relatório não encontrado');
return relatorio;
} catch (erro) { } catch (erro) {
console.error('❌ [API RELATÓRIOS] Erro ao atualizar relatório:', erro); console.error('❌ [API RELATÓRIOS] Erro ao atualizar relatório:', erro);
throw erro; throw erro;
@ -256,13 +230,13 @@ export async function deletarRelatorio(id: string): Promise<void> {
export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<Report[]> { export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<Report[]> {
try { try {
console.log('👤 [API RELATÓRIOS] Buscando relatórios do paciente:', idPaciente); console.log('👤 [API RELATÓRIOS] Buscando relatórios do paciente:', idPaciente);
const resposta = await fetch(`${BASE_API_RELATORIOS}?patient_id=${idPaciente}`, { const resposta = await fetch(`${BASE_API_RELATORIOS}?patient_id=eq.${idPaciente}`, {
method: 'GET', method: 'GET',
headers: obterCabecalhos(), headers: obterCabecalhos(),
}); });
const resultado = await tratarRespostaApi<ReportsResponse>(resposta); const resultado = await tratarRespostaApi<Report[]>(resposta);
console.log('✅ [API RELATÓRIOS] Relatórios do paciente encontrados:', resultado.data?.length || 0); console.log('✅ [API RELATÓRIOS] Relatórios do paciente encontrados:', resultado.length);
return resultado.data || []; return resultado;
} catch (erro) { } catch (erro) {
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do paciente:', erro); console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do paciente:', erro);
throw erro; throw erro;
@ -275,13 +249,13 @@ export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<R
export async function listarRelatoriosPorMedico(idMedico: string): Promise<Report[]> { export async function listarRelatoriosPorMedico(idMedico: string): Promise<Report[]> {
try { try {
console.log('👨‍⚕️ [API RELATÓRIOS] Buscando relatórios do médico:', idMedico); console.log('👨‍⚕️ [API RELATÓRIOS] Buscando relatórios do médico:', idMedico);
const resposta = await fetch(`${BASE_API_RELATORIOS}?doctor_id=${idMedico}`, { const resposta = await fetch(`${BASE_API_RELATORIOS}?requested_by=eq.${idMedico}`, {
method: 'GET', method: 'GET',
headers: obterCabecalhos(), headers: obterCabecalhos(),
}); });
const resultado = await tratarRespostaApi<ReportsResponse>(resposta); const resultado = await tratarRespostaApi<Report[]>(resposta);
console.log('✅ [API RELATÓRIOS] Relatórios do médico encontrados:', resultado.data?.length || 0); console.log('✅ [API RELATÓRIOS] Relatórios do médico encontrados:', resultado.length);
return resultado.data || []; return resultado;
} catch (erro) { } catch (erro) {
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do médico:', erro); console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do médico:', erro);
throw erro; throw erro;

View File

@ -1,5 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -4,60 +4,66 @@ export interface ApiError {
code?: string; code?: string;
} }
// Este arquivo foi renomeado de report.ts para report-types.ts para evitar confusão com outros arquivos de lógica. // 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 // Tipos para o endpoint Supabase de Relatórios Médicos
export interface Report { export interface Report {
id: string; id: string;
patient_id: string; patient_id: string;
doctor_id: string; order_number: string;
report_type: string; exam: string;
chief_complaint: string;
clinical_history: string;
symptoms_and_signs: string;
physical_examination: string;
complementary_exams: string;
exam_results: string;
diagnosis: string; diagnosis: string;
prognosis?: string; conclusion: string;
treatment_performed: string; cid_code: string;
objective_recommendations: string; content_html: string;
icd_code?: string; content_json: any;
report_date: string; status: string;
requested_by: string;
due_at: string;
hide_date: boolean;
hide_signature: boolean;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
created_by: 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;
};
} }
export interface CreateReportData {
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;
created_by: string;
}
export interface UpdateReportData extends Partial<CreateReportData> {
updated_at?: string;
}
export type ReportsResponse = Report[];
export type ReportResponse = Report;
// Dados para criar um novo relatório // Dados para criar um novo relatório
export interface CreateReportData { export interface CreateReportData {
patient_id: string; patient_id: string;
doctor_id: string; order_number: string;
report_type: string; exam: string;
chief_complaint: string; diagnosis: string;
clinical_history: string; conclusion: string;
symptoms_and_signs: string; cid_code: string;
physical_examination: string; content_html: string;
complementary_exams: string; content_json: any;
exam_results: string; status: string;
diagnosis: string; requested_by: string;
prognosis?: string; due_at: string;
treatment_performed: string; hide_date: boolean;
objective_recommendations: string; hide_signature: boolean;
icd_code?: string;
report_date: string;
} }
// Dados para atualizar um relatório existente // Dados para atualizar um relatório existente
@ -66,18 +72,6 @@ export interface UpdateReportData extends Partial<CreateReportData> {
} }
// Resposta da API ao listar relatórios // 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) // Dados do formulário (adaptado para a estrutura do front-end existente)
export interface ReportFormData { export interface ReportFormData {
@ -86,15 +80,19 @@ export interface ReportFormData {
profissionalCrm: string; profissionalCrm: string;
// Identificação do Paciente // Identificação do Paciente
pacienteId: string; patient_id: string;
pacienteNome: string; report_type: string;
pacienteCpf: string; symptoms_and_signs: string;
pacienteIdade: string; diagnosis: string;
prognosis?: string;
// Informações do Relatório treatment_performed: string;
motivoRelatorio: string; icd_code?: string;
cid?: string; report_date: string;
dataRelatorio: string; hipotesesDiagnosticas: string;
condutaMedica: string;
prescricoes: string;
retornoAgendado: string;
// cid10: string; // Removed, not present in schema
// Histórico Clínico // Histórico Clínico
historicoClinico: string; historicoClinico: string;
@ -104,4 +102,4 @@ export interface ReportFormData {
examesRealizados: string; examesRealizados: string;
resultadosExames: string; resultadosExames: string;
// ...restante do código... // ...restante do código...
}