modified: docs/repository-api-audit.md

modified:   src/repositories/authRepository.js
modified:   src/repositories/patientRepository.js
modified:   src/repositories/professionalRepository.js
modified:   src/repositories/profileRepository.js
modified:   src/repositories/userRepository.js
This commit is contained in:
2026-05-11 13:24:29 -03:00
parent fba021e048
commit 04a13c24d3
6 changed files with 152 additions and 38 deletions

View File

@@ -1,49 +1,73 @@
# Auditoria de Implementacao e Mapeamento da API # Auditoria de Implementacao e Mapeamento da API
Este documento resume o estado atual da integracao entre o front-end e os endpoints da API. Este documento resume as APIs do Apidog conectadas no projeto `riseup_squad_03`.
## Integrado no front ## Autenticacao
- **Autenticacao** - `POST /auth/v1/token?grant_type=password` em `authRepository.login`
- Login com email e senha via Supabase Auth (`/auth/v1/token`). - `POST /auth/v1/otp` em `authRepository.sendMagicLink`
- Solicitar reset de senha: tenta `/solicitar-reset-de-senha` e usa `/auth/v1/recover` como fallback. - `POST /functions/v1/request-password-reset` em `authRepository.requestPasswordReset`
- Dados do usuario autenticado: tenta `/informacoes-do-usuario-autenticado` e usa `/auth/v1/user` como fallback. - `GET /auth/v1/user` em `authRepository.getUser` como fallback
- Logout: tenta `/logout`, usa `/auth/v1/logout` como fallback e sempre limpa a sessao local. - `POST /functions/v1/user-info` em `authRepository.getUser`
- `POST /auth/v1/logout` em `authRepository.logout`
- **Pacientes** ## Usuarios
- Listar, criar, atualizar e deletar pacientes via Supabase REST.
- Criar paciente com validacao via Edge Function quando disponivel.
- **Agendamentos** - `POST /functions/v1/create-user` em `userRepository.create`
- Listar agendamentos: tenta `GET /agendamentos` e usa Supabase REST `appointments` como fallback. - `POST /functions/v1/create-user-with-password` em `userRepository.createWithPassword`
- Criar agendamento: tenta `POST /agendamentos` e usa Supabase REST `appointments` como fallback. - `POST /functions/v1/user-info-by-id/:id` em `userRepository.getById`
- `POST /functions/v1/delete-user` em `userRepository.remove`
- Listagem de perfis via REST em `profiles` / `user_profiles` em `userRepository.getAll`
- **Laudos Medicos** ## Pacientes
- Listar relatorios: tenta `GET /reports` e usa Supabase REST `reports` como fallback.
- Criar relatorio: tenta `POST /reports` e usa Supabase REST `reports` como fallback.
- Atualizar relatorio: tenta `PATCH /reports/{id}`, depois `PATCH /reports`, e usa Supabase REST `reports` como fallback.
- **Medicos / Profissionais** - `GET /rest/v1/patients` em `patientRepository.getAll`
- Listar medicos: tenta `GET /listar-medicos` e usa Supabase REST `doctors` como fallback. - `POST /rest/v1/patients` em `patientRepository.create`
- `PATCH /rest/v1/patients?id=eq.ID` em `patientRepository.update`
- `DELETE /rest/v1/patients?id=eq.ID` em `patientRepository.remove`
- `POST /functions/v1/create-patient` em `patientRepository.createWithValidation`
- `POST /functions/v1/register-patient` em `patientRepository.registerPublic`
- **Mensageria** ## Medicos
- Enviar SMS: tenta `POST /enviar-sms-via-twilio` e usa Edge Function `send-sms` como fallback.
- O formulario agora coleta telefone quando o canal selecionado e SMS.
- **Storage** - `GET /rest/v1/doctors` em `professionalRepository.getAll`
- Upload de avatar: tenta `/upload-avatar` e usa Supabase Storage no bucket `avatars` como fallback. - `POST /functions/v1/create-doctor` em `professionalRepository.create`
- A tela de perfil atualiza a imagem exibida apos upload bem-sucedido.
## Ainda sem endpoint consolidado documentado ## Agendamentos
- Dashboard / Inicio (`HomePage` / `homeRepository.js`). - `GET /rest/v1/appointments` em `appointmentRepository.getAll`
- Estatisticas e BI (`AnalyticsPage` / `analyticsRepository.js`). - `POST /rest/v1/appointments` em `appointmentRepository.create`
- Prontuarios especificos separados de laudos (`MedicalRecordsPage` / `medicalRecordRepository.js`). - `PATCH /rest/v1/appointments?id=eq.ID` em `appointmentRepository.update`
- Consultas isoladas fora de agendamento (`VisitsPage` / `visitRepository.js`). - Cancelamento via `PATCH /rest/v1/appointments?id=eq.ID` em `appointmentRepository.cancel`
- Configuracoes gerais do tenant (`SettingsPage` / `settingsRepository.js`).
## Disponibilidade e Slots
- `GET /rest/v1/doctor_availability` em `availabilityRepository.getAll`
- `POST /rest/v1/doctor_availability` em `availabilityRepository.create`
- `PATCH /rest/v1/doctor_availability?id=eq.ID` em `availabilityRepository.update`
- `DELETE /rest/v1/doctor_availability?id=eq.ID` em `availabilityRepository.remove`
- `GET /rest/v1/doctor_exceptions` em `availabilityRepository.getExceptions`
- `POST /rest/v1/doctor_exceptions` em `availabilityRepository.createException`
- `POST /functions/v1/get-available-slots` em `availabilityRepository.getAvailableSlots`
## Reports / Laudos Medicos
- `GET /rest/v1/reports` em `reportRepository.getInitialReports`
- `POST /rest/v1/reports` em `reportRepository.create`
- `PATCH /rest/v1/reports?id=eq.ID` em `reportRepository.update`
## SMS / Comunicacao
- `POST /functions/v1/send-sms` em `communicationRepository.sendSms`
## Storage
- `POST /storage/v1/object/avatars/{path}` em `profileRepository.updateAvatar`
- `GET /storage/v1/object/avatars/{path}` em `profileRepository.downloadAvatar`
## Observacoes ## Observacoes
- `VITE_API_BASE_URL` define a base dos endpoints nomeados da API. Quando nao informado, o front usa `VITE_SUPABASE_FUNCTIONS_URL`. - O Supabase real responde as rotas REST em `/rest/v1/...`.
- Os reposititorios aceitam formatos de resposta comuns como arrays diretos ou objetos com chaves `data`, `reports`, `agendamentos`, `medicos` etc. - As Edge Functions reais respondem em `/functions/v1/...`.
- Os fallbacks existem para manter o front funcional em ambientes onde parte das Edge Functions ainda nao foi publicada. - Algumas rotas curtas do Apidog retornam `404` no ambiente real; o codigo usa o caminho que respondeu em producao.
- `Schemas` no Apidog nao sao endpoints executaveis, apenas contratos de dados.

View File

@@ -60,6 +60,20 @@ export const authRepository = {
return true return true
}, },
async sendMagicLink(email) {
const response = await fetch(`${apiConfig.supabaseUrl}/auth/v1/otp`, {
method: 'POST',
headers: getAnonHeaders(),
body: JSON.stringify({ email: email?.trim() }),
})
if (!response.ok) {
throw new Error(await getResponseError(response, 'Erro ao enviar Magic Link.'))
}
return true
},
async getUser() { async getUser() {
const apiResponse = await fetch(`${apiConfig.functionsUrl.replace(/\/+$/, '')}/user-info`, { const apiResponse = await fetch(`${apiConfig.functionsUrl.replace(/\/+$/, '')}/user-info`, {
method: 'POST', method: 'POST',

View File

@@ -1,4 +1,4 @@
import { apiConfig, getAuthenticatedHeaders } from '../config/api.js' import { apiConfig, getAnonHeaders, getAuthenticatedHeaders } from '../config/api.js'
import { getResponseError } from './repositoryUtils.js' import { getResponseError } from './repositoryUtils.js'
export const patientRepository = { export const patientRepository = {
@@ -83,6 +83,29 @@ export const patientRepository = {
return response.json() return response.json()
}, },
async registerPublic(data) {
const body = cleanPayload({
full_name: data.name || data.full_name,
cpf: data.cpf,
email: data.email,
phone_mobile: data.phone || data.phone_mobile,
birth_date: data.birthDate || data.birth_date || null,
redirect_url: data.redirectUrl || data.redirect_url,
})
const response = await fetch(`${apiConfig.functionsUrl}/register-patient`, {
method: 'POST',
headers: getAnonHeaders(),
body: JSON.stringify(body),
})
if (!response.ok) {
throw new Error(await getResponseError(response, 'Erro ao realizar auto-cadastro de paciente.'))
}
return response.json()
},
// 4. Atualizar paciente // 4. Atualizar paciente
async update(patientId, data) { async update(patientId, data) {
const body = { const body = {
@@ -325,3 +348,9 @@ function calculateAge(birthDate) {
return age return age
} }
function cleanPayload(payload) {
return Object.fromEntries(
Object.entries(payload).filter(([, value]) => value !== undefined && value !== null && value !== ''),
)
}

View File

@@ -1,4 +1,5 @@
import { apiConfig, getAuthenticatedHeaders } from '../config/api.js' import { apiConfig, getAuthenticatedHeaders } from '../config/api.js'
import { getResponseError, normalizeItem } from './repositoryUtils.js'
export const professionalRepository = { export const professionalRepository = {
async getAll() { async getAll() {
@@ -12,6 +13,29 @@ export const professionalRepository = {
return (Array.isArray(data) ? data : []).map(mapProfessional) return (Array.isArray(data) ? data : []).map(mapProfessional)
}, },
async create(data) {
const response = await fetch(`${apiConfig.functionsUrl}/create-doctor`, {
method: 'POST',
headers: getAuthenticatedHeaders(),
body: JSON.stringify(cleanPayload({
full_name: data.fullName || data.full_name || data.name,
email: data.email,
cpf: data.cpf,
crm: data.crm,
crm_uf: data.crmUf || data.crm_uf,
phone_mobile: data.phoneMobile || data.phone_mobile || data.phone,
specialty: data.specialty || data.specialidade,
birth_date: data.birthDate || data.birth_date,
})),
})
if (!response.ok) {
throw new Error(await getResponseError(response, 'Erro ao criar m?dico.'))
}
return mapProfessional(normalizeItem(await response.json(), ['doctor']))
},
getCoverageMap() { getCoverageMap() {
return { return {
slots: ['08-12', '09-13', '10-15', '13-18', '08-14'], slots: ['08-12', '09-13', '10-15', '13-18', '08-14'],
@@ -52,3 +76,9 @@ function mapProfessional(doctor) {
function normalizeValue(value) { function normalizeValue(value) {
return String(value || '').trim().toLowerCase() return String(value || '').trim().toLowerCase()
} }
function cleanPayload(payload) {
return Object.fromEntries(
Object.entries(payload).filter(([, value]) => value !== undefined && value !== null && value !== ''),
)
}

View File

@@ -84,6 +84,24 @@ export const profileRepository = {
path: objectPath, path: objectPath,
} }
}, },
async downloadAvatar(path) {
const objectPath = String(path || '').replace(/^\/+/, '')
const response = await fetch(`${apiConfig.storageUrl}/object/avatars/${objectPath}`, {
method: 'GET',
headers: getAuthenticatedHeaders({ 'Content-Type': undefined }),
})
if (!response.ok) {
throw new Error(await getResponseError(response, 'Falha ao baixar avatar.'))
}
return {
blob: await response.blob(),
contentType: response.headers.get('content-type') || 'application/octet-stream',
path: objectPath,
}
},
} }
function normalizeAvatarResponse(data) { function normalizeAvatarResponse(data) {

View File

@@ -33,10 +33,9 @@ export const userRepository = {
}, },
async getById(userId) { async getById(userId) {
const response = await fetch(`${apiConfig.functionsUrl}/user-info-by-id`, { const response = await fetch(`${apiConfig.functionsUrl}/user-info-by-id/${encodeURIComponent(userId)}`, {
method: 'POST', method: 'POST',
headers: getAuthenticatedHeaders(), headers: getAuthenticatedHeaders(),
body: JSON.stringify({ user_id: userId }),
}) })
if (!response.ok) { if (!response.ok) {
@@ -83,7 +82,7 @@ export const userRepository = {
const response = await fetch(`${apiConfig.functionsUrl}/delete-user`, { const response = await fetch(`${apiConfig.functionsUrl}/delete-user`, {
method: 'POST', method: 'POST',
headers: getAuthenticatedHeaders(), headers: getAuthenticatedHeaders(),
body: JSON.stringify({ user_id: userId }), body: JSON.stringify({ userId, user_id: userId }),
}) })
if (!response.ok) { if (!response.ok) {