1165 lines
32 KiB
Markdown
1165 lines
32 KiB
Markdown
# MediConnect - Sistema de Gestão Médica Completo
|
||
|
||
Sistema completo de gestão médica com agendamento inteligente, teleconsultas, prontuários eletrônicos e gerenciamento multi-nível.
|
||
|
||
**Stack:** React + TypeScript + Vite + TailwindCSS + Supabase Edge Functions
|
||
**Deploy:** Cloudflare Pages
|
||
**Última Atualização:** 29/11/2025
|
||
|
||
---
|
||
|
||
## 🚀 Acesso ao Sistema
|
||
|
||
- **URL Principal:** https://mediconnectbrasil.app/
|
||
- **URL Cloudflare:** https://mediconnect-5oz.pages.dev/
|
||
|
||
### Credenciais de Teste
|
||
|
||
**Admin/Gestor:**
|
||
|
||
- Email: admin@mediconnect.app
|
||
- Senha: admin123
|
||
- Acesso: Todos os painéis
|
||
|
||
**Médico:**
|
||
|
||
- Email: medico@teste.com
|
||
- Senha: senha123
|
||
- Acesso: Painel Médico, Sala de Espera, Teleconsultas
|
||
|
||
**Paciente:**
|
||
|
||
- Email: paciente@teste.com
|
||
- Senha: senha123
|
||
- Acesso: Agendamento, Acompanhamento, Teleconsultas
|
||
|
||
**Secretária:**
|
||
|
||
- Email: secretaria@teste.com
|
||
- Senha: senha123
|
||
- Acesso: Gerenciamento de Consultas, Pacientes e Médicos
|
||
|
||
---
|
||
|
||
## 🏗️ Arquitetura
|
||
|
||
```
|
||
Frontend (React/Vite + TypeScript)
|
||
↓
|
||
API Gateway (Edge Functions - Deno)
|
||
↓
|
||
Dual Supabase Architecture
|
||
├── EXTERNAL Supabase (Auth + RLS)
|
||
│ ├── Auth (JWT + Magic Link)
|
||
│ ├── PostgreSQL (Source of Truth)
|
||
│ └── Storage (Avatares, Documentos)
|
||
└── NEW Supabase (Functions + No RLS)
|
||
├── 65 Edge Functions (CORS Fixed)
|
||
├── 34 Tabelas (Gamification, Analytics)
|
||
└── Real-time Subscriptions
|
||
↓
|
||
Integrations
|
||
├── Jitsi (Teleconsultas)
|
||
├── Twilio (SMS)
|
||
└── Resend (Email)
|
||
↓
|
||
Cloudflare Pages (Deploy Global)
|
||
```
|
||
|
||
---
|
||
|
||
## ✨ Funcionalidades Principais
|
||
|
||
### 🎯 **NOVO: Painel do Gestor**
|
||
|
||
- ✅ Dashboard executivo com KPIs
|
||
- ✅ Visão geral do sistema (pacientes, médicos, consultas)
|
||
- ✅ Métricas de atividade (hoje, semana, mês)
|
||
- ✅ Lista de consultas recentes com status
|
||
- ✅ Filtros de período (7d, 30d, 90d, 1 ano)
|
||
- ✅ Tabs: Dashboard, Análises, Relatórios, Usuários, Configurações
|
||
- ✅ Design moderno com gradientes e animações
|
||
|
||
### 🏥 Para Médicos
|
||
|
||
- ✅ **Painel modernizado** com design da Landing Page
|
||
- ✅ Agenda personalizada com disponibilidade configurável
|
||
- ✅ **Sala de Espera Virtual** - Pacientes com check-in em tempo real
|
||
- ✅ **Teleconsulta integrada** com Jitsi (vídeo HD)
|
||
- ✅ Gerenciamento completo de disponibilidade semanal
|
||
- ✅ Sistema de exceções (bloqueios e horários extras)
|
||
- ✅ Prontuário eletrônico completo
|
||
- ✅ Histórico de consultas do paciente
|
||
- ✅ Dashboard com métricas e estatísticas
|
||
- ✅ Consultas presenciais e remotas
|
||
- ✅ Confirmação/Cancelamento/Remarcar consultas
|
||
- ✅ Status dinâmicos (scheduled, confirmed, in_progress, completed, cancelled, no_show)
|
||
|
||
### 👥 Para Pacientes
|
||
|
||
- ✅ **Dashboard modernizado** com gradientes e animações hover
|
||
- ✅ **Teleconsulta** - Entrar em videochamada com médico
|
||
- ✅ Verificação automática de dispositivos (câmera/microfone)
|
||
- ✅ Agendamento inteligente com slots disponíveis em tempo real
|
||
- ✅ Histórico completo de consultas com filtros
|
||
- ✅ Visualização detalhada de laudos médicos com modal
|
||
- ✅ Visualização e download de relatórios médicos (PDF)
|
||
- ✅ Perfil com avatar e dados pessoais
|
||
- ✅ Filtros por médico, especialidade e data
|
||
- ✅ Gamificação (pontos, streaks, badges)
|
||
- ✅ Cards de estatísticas com cores dinâmicas
|
||
|
||
### 🏢 Para Secretárias
|
||
|
||
- ✅ **Painel modernizado** com tabs em gradiente
|
||
- ✅ Gerenciamento completo de médicos, pacientes e consultas
|
||
- ✅ Cadastro com validação de CPF e CRM
|
||
- ✅ Configuração de agenda médica (horários e exceções)
|
||
- ✅ Relatórios com nomes de médicos (não apenas IDs)
|
||
- ✅ Busca e filtros avançados
|
||
- ✅ Confirmação profissional para exclusões
|
||
- ✅ Gestão de sala de espera
|
||
- ✅ Envio de notificações (SMS, Email, WhatsApp)
|
||
|
||
### 🔐 Para Administradores
|
||
|
||
- ✅ **Painel modernizado** com design consistente
|
||
- ✅ Gerenciamento de usuários e permissões (roles)
|
||
- ✅ CRUD completo de Pacientes, Médicos e Secretárias
|
||
- ✅ Controle de acesso granular
|
||
- ✅ Logs de auditoria
|
||
- ✅ Configurações globais do sistema
|
||
- ✅ Tabs organizadas (Pacientes, Usuários, Médicos)
|
||
|
||
### 🔐 Sistema de Autenticação
|
||
|
||
- ✅ Login com email/senha
|
||
- ✅ Magic Link (login sem senha)
|
||
- ✅ Recuperação de senha
|
||
- ✅ Tokens JWT com refresh automático
|
||
- ✅ Controle de acesso por role (admin, gestor, médico, paciente, secretária)
|
||
- ✅ Dual Supabase (EXTERNAL para auth, NEW para functions)
|
||
|
||
---
|
||
|
||
## 🎨 Design System (Atualizado 29/11/2025)
|
||
|
||
### Padrões Visuais da Landing Page
|
||
|
||
Todos os painéis foram modernizados seguindo o design da Landing Page:
|
||
|
||
**Cores e Gradientes:**
|
||
|
||
```css
|
||
/* Gradientes principais */
|
||
from-blue-600 to-purple-600 /* Títulos e botões ativos */
|
||
from-blue-500 to-cyan-600 /* Ícones e destaques */
|
||
from-purple-500 to-pink-600 /* Variações */
|
||
from-green-500 to-teal-600 /* Status positivos */
|
||
|
||
/* Backgrounds */
|
||
bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 /* Fundo dos painéis */
|
||
bg-white/80 backdrop-blur-md /* Cards glassmorphism */
|
||
```
|
||
|
||
**Componentes:**
|
||
|
||
```css
|
||
/* Cards */
|
||
rounded-2xl shadow-lg hover:shadow-2xl hover:-translate-y-1 transition-all
|
||
|
||
/* Botões */
|
||
px-6 py-3 bg-gradient-to-r text-white rounded-xl hover:shadow-lg hover:scale-105
|
||
|
||
/* Ícones em containers */
|
||
w-14 h-14 bg-gradient-to-br rounded-xl group-hover:scale-110 transition-transform
|
||
|
||
/* Tabs ativos */
|
||
bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg scale-105
|
||
|
||
/* Bordas sutis */
|
||
border border-gray-100 /* Ao invés de gray-200 */
|
||
```
|
||
|
||
**Tipografia:**
|
||
|
||
```css
|
||
/* Títulos */
|
||
text-3xl sm:text-4xl font-bold
|
||
text-transparent bg-clip-text bg-gradient-to-r /* Títulos com gradiente */
|
||
|
||
/* Valores de métricas */
|
||
text-3xl font-bold text-blue-600 /* Números destacados */
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Tecnologias
|
||
|
||
### Frontend
|
||
|
||
- **React 18** - Interface moderna e reativa
|
||
- **TypeScript** - Tipagem estática
|
||
- **Vite** - Build ultra-rápido (HMR em <200ms)
|
||
- **TailwindCSS** - Estilização utilitária + Design System customizado
|
||
- **React Router** - Navegação SPA com rotas protegidas
|
||
- **Axios** - Cliente HTTP com interceptors
|
||
- **date-fns** - Manipulação de datas (i18n pt-BR)
|
||
- **jsPDF** - Geração de PDFs
|
||
- **Lucide Icons** - 1000+ ícones SVG modernos
|
||
- **React Hot Toast** - Notificações elegantes
|
||
|
||
### Backend (Supabase)
|
||
|
||
- **PostgreSQL 15** - Banco de dados relacional
|
||
- **PostgREST** - API REST automática
|
||
- **Edge Functions (Deno)** - 65 funções serverless
|
||
- ✅ CORS corrigido (status: 200 explícito)
|
||
- ✅ Auth bypass com SERVICE_ROLE_KEY
|
||
- ✅ URL normalization (/ → -)
|
||
- **Storage** - Armazenamento de arquivos (avatares, PDFs)
|
||
- **Auth** - JWT + RLS (EXTERNAL Supabase)
|
||
- **Real-time** - Subscriptions via WebSocket
|
||
|
||
### Integrações
|
||
|
||
- **Jitsi Meet** - Teleconsultas HD (vpaas-magic-cookie)
|
||
- **Twilio** - SMS notifications (+1 978-981-4520)
|
||
- **Resend** - Email transacional (noreply@mediconnectbrasil.app)
|
||
|
||
### Deploy
|
||
|
||
- **Cloudflare Pages** - Hospedagem global com CDN
|
||
- **Wrangler CLI** - Deploy automatizado
|
||
- **GitHub Actions** - CI/CD pipeline
|
||
|
||
---
|
||
|
||
## 📦 Instalação e Execução
|
||
|
||
### Pré-requisitos
|
||
|
||
- Node.js 18+
|
||
- pnpm (recomendado) ou npm
|
||
- Supabase CLI (para deploy de functions)
|
||
|
||
### Instalação
|
||
|
||
```bash
|
||
# Clone o repositório
|
||
git clone https://github.com/seu-usuario/mediconnect.git
|
||
cd riseup-squad18
|
||
|
||
# Instalar dependências
|
||
pnpm install
|
||
|
||
# Configurar variáveis de ambiente
|
||
cp .env.example .env
|
||
# Edite .env com suas credenciais Supabase, Jitsi, Twilio, Resend
|
||
|
||
# Iniciar desenvolvimento
|
||
pnpm dev
|
||
|
||
# Acessar em http://localhost:5173
|
||
```
|
||
|
||
### Build e Deploy
|
||
|
||
```bash
|
||
# Build de produção
|
||
pnpm build
|
||
|
||
# Preview do build
|
||
pnpm preview
|
||
|
||
# Deploy para Cloudflare Pages
|
||
pnpm wrangler pages deploy dist --project-name=mediconnect --branch=production
|
||
```
|
||
|
||
### Deploy de Edge Functions
|
||
|
||
```powershell
|
||
# Deploy individual
|
||
supabase functions deploy <nome-funcao> --project-ref etblfypcxxtvvuqjkrgd --no-verify-jwt
|
||
|
||
# Deploy em massa (17 funções corrigidas)
|
||
.\deploy-fixed-functions.ps1
|
||
|
||
# Deploy de todas as funções
|
||
supabase functions deploy --project-ref etblfypcxxtvvuqjkrgd
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 Melhorias Recentes (Novembro 2025)
|
||
|
||
### 🎨 Modernização de UI (29/11/2025)
|
||
|
||
**4 Painéis Modernizados:**
|
||
|
||
- ✅ **PainelMedico.tsx** - Design da Landing Page aplicado
|
||
- ✅ **PainelSecretaria.tsx** - Tabs com gradiente, glassmorphism
|
||
- ✅ **PainelAdmin.tsx** - Header e tabs modernizados
|
||
- ✅ **AcompanhamentoPaciente.tsx** - Cards com hover effects, gradientes dinâmicos
|
||
|
||
**1 Painel Novo:**
|
||
|
||
- 🆕 **PainelGestor.tsx** - Dashboard executivo com KPIs e analytics
|
||
- Métricas do sistema (pacientes, médicos, consultas, relatórios)
|
||
- Atividade temporal (hoje, semana, mês)
|
||
- Filtros de período (7d, 30d, 90d, 1 ano)
|
||
- Lista de consultas recentes
|
||
- Tabs: Dashboard, Análises, Relatórios, Usuários, Configurações
|
||
|
||
**Padrões Aplicados:**
|
||
|
||
- Gradientes: `from-blue-600 to-purple-600`, `from-blue-500 to-cyan-600`
|
||
- Cards: `rounded-2xl shadow-lg hover:shadow-2xl hover:-translate-y-1`
|
||
- Glassmorphism: `bg-white/80 backdrop-blur-md`
|
||
- Ícones com gradiente: `bg-gradient-to-br rounded-xl group-hover:scale-110`
|
||
- Bordas sutis: `border-gray-100` (ao invés de gray-200)
|
||
|
||
### 🔧 Correção Massiva de CORS (29/11/2025)
|
||
|
||
**17 Edge Functions Corrigidas:**
|
||
|
||
Todas agora têm `status: 200` explícito no OPTIONS handler:
|
||
|
||
```typescript
|
||
if (req.method === "OPTIONS") {
|
||
return new Response("ok", {
|
||
status: 200, // ✅ EXPLÍCITO!
|
||
headers: corsHeaders(),
|
||
});
|
||
}
|
||
```
|
||
|
||
**Funções corrigidas:**
|
||
|
||
- appointments: checkin, confirm, no-show, reschedule, suggest-slot
|
||
- gamification: add-points, doctor-badges, patient-streak
|
||
- notifications: send, confirm
|
||
- teleconsult: start, end, status
|
||
- offline: agenda-today, patient-basic
|
||
- reports: export
|
||
- virtual-queue: advance
|
||
|
||
**Script de Deploy:**
|
||
|
||
- Criado `deploy-fixed-functions.ps1` para deploy automatizado
|
||
- Flag `--no-verify-jwt` para bypass de autenticação
|
||
- Deploy em massa de todas as 17 funções
|
||
|
||
### 🎥 Sistema de Teleconsulta (Implementado)
|
||
|
||
**Painel do Paciente:**
|
||
|
||
- ✅ Botão "Entrar na Consulta" visível em consultas agendadas
|
||
- ✅ Verificação automática de dispositivos (câmera/microfone)
|
||
- ✅ Integração com Jitsi Meet (vpaas-magic-cookie)
|
||
- ✅ Abertura de sala em nova janela
|
||
- ✅ Toast notifications para feedback
|
||
|
||
**Painel do Médico:**
|
||
|
||
- ✅ Botão de videochamada nas consultas
|
||
- ✅ Tab "Sala de Espera" dedicada
|
||
- ✅ Componente WaitingRoom integrado
|
||
- ✅ Status de consultas em tempo real
|
||
|
||
**Services:**
|
||
|
||
- ✅ `teleconsultService.ts` completo
|
||
- ✅ Funções: start(), end(), getStatus(), openRoom(), checkMediaDevices()
|
||
- ✅ Edge Functions: teleconsult-start, teleconsult-end, teleconsult-status
|
||
|
||
### 🔔 Sistema de Notificações (Implementado)
|
||
|
||
**Services:**
|
||
|
||
- ✅ `notificationService.ts` com CRUD completo
|
||
- ✅ Tipos: SMS, Email, WhatsApp
|
||
- ✅ Status: pending, sent, failed, scheduled
|
||
- ✅ Integração com Twilio (SMS) e Resend (Email)
|
||
|
||
**Edge Functions:**
|
||
|
||
- ✅ notifications-send (envio multi-canal)
|
||
- ✅ notifications-confirm (confirmação de leitura)
|
||
- ✅ notifications-subscription (push notifications)
|
||
|
||
### 📅 Sistema de Agendamento Completo
|
||
|
||
**Status de Consultas:**
|
||
|
||
- scheduled → confirmed → checked_in → in_progress → completed
|
||
- Também: cancelled, no_show
|
||
|
||
**Edge Functions:**
|
||
|
||
- ✅ appointments-create (criar consulta)
|
||
- ✅ appointments-update (atualizar)
|
||
- ✅ appointments-cancel (cancelar)
|
||
- ✅ appointments-confirm (confirmar)
|
||
- ✅ appointments-checkin (check-in para sala de espera)
|
||
- ✅ appointments-no-show (marcar falta)
|
||
- ✅ appointments-reschedule (reagendar)
|
||
- ✅ appointments-suggest-slot (sugerir novo horário)
|
||
|
||
**Componentes:**
|
||
|
||
- ✅ WaitingRoom.tsx - Sala de espera virtual
|
||
- ✅ ConsultaModal.tsx - Modal de gerenciamento
|
||
- ✅ Integração completa nos painéis
|
||
|
||
### 🎮 Sistema de Gamificação
|
||
|
||
**Features:**
|
||
|
||
- ✅ Pontos por ações (consultas, check-ins, etc)
|
||
- ✅ Streaks de pacientes (dias consecutivos)
|
||
- ✅ Badges de médicos (conquistas)
|
||
- ✅ Edge Functions: add-points, patient-streak, doctor-badges
|
||
|
||
### 🚀 Melhorias de Navegação
|
||
|
||
**Command Palette (Ctrl+K):**
|
||
|
||
- ✅ Adicionado "Painel Gestor" aos comandos
|
||
- ✅ Adicionado "Painel Admin" aos comandos
|
||
- ✅ Keywords: gestor, gerencial, analytics, métricas
|
||
|
||
**Header:**
|
||
|
||
- ✅ Link para "Gestor" (roxo/indigo) entre Admin e Médico
|
||
- ✅ Versão mobile com ícone 📈
|
||
- ✅ Navegação consistente em todos os painéis
|
||
|
||
**Rotas:**
|
||
|
||
- ✅ `/painel-gestor` protegida (roles: admin, gestor)
|
||
- ✅ Importação e integração no App.tsx
|
||
- ✅ ProtectedRoute com verificação de roles
|
||
|
||
### Chatbot AI 🤖
|
||
|
||
- ✅ Assistente virtual inteligente com IA
|
||
- ✅ Interface de chat moderna e responsiva
|
||
- ✅ Posicionamento otimizado (canto inferior esquerdo)
|
||
- ✅ Respostas personalizadas sobre o sistema
|
||
- ✅ Suporte a dúvidas sobre agendamento e funcionalidades
|
||
|
||
### Gerenciamento de Disponibilidade Médica 📅
|
||
|
||
- ✅ Painel completo de disponibilidade no painel do médico
|
||
- ✅ Criação e edição de horários semanais
|
||
- ✅ Sistema de exceções (bloqueios e horários extras)
|
||
- ✅ Visualização em abas (Horário Semanal e Exceções)
|
||
- ✅ Interface intuitiva com validações completas
|
||
|
||
### Visualização de Laudos 🔍
|
||
|
||
- ✅ Botão de visualização (ícone de olho) no painel do paciente
|
||
- ✅ Modal detalhado com informações completas do laudo
|
||
- ✅ Exibição de: número do pedido, status, exame, diagnóstico, CID, conclusão
|
||
- ✅ Suporte a modo escuro
|
||
- ✅ Formatação de datas em português
|
||
|
||
### Melhorias no Painel da Secretária 👩💼
|
||
|
||
- ✅ Relatórios mostram nome do médico ao invés de ID
|
||
- ✅ Mensagem de boas-vindas personalizada com nome real
|
||
- ✅ Busca e resolução automática de nomes de médicos
|
||
- ✅ Fallback para email caso nome não esteja disponível
|
||
|
||
### Sistema de Agendamento
|
||
|
||
- ✅ API de slots disponíveis (Edge Function)
|
||
- ✅ Cálculo automático de horários
|
||
- ✅ Validação de antecedência mínima
|
||
- ✅ Verificação de conflitos
|
||
- ✅ Interface otimizada
|
||
|
||
### UX/UI
|
||
|
||
- ✅ Toast único de boas-vindas após login (removidas mensagens duplicadas)
|
||
- ✅ Chatbot responsivo adaptado ao tamanho da tela
|
||
- ✅ Diálogos de confirmação profissionais
|
||
- ✅ Filtros de busca em todas as listas
|
||
- ✅ Feedback visual melhorado
|
||
- ✅ Loading states consistentes
|
||
- ✅ Mensagens de erro claras
|
||
|
||
### Performance
|
||
|
||
- ✅ Build otimizado (~467KB)
|
||
- ✅ Code splitting
|
||
- ✅ Lazy loading de rotas
|
||
- ✅ Cache de assets
|
||
- ✅ Remoção de dependências não utilizadas
|
||
|
||
---
|
||
|
||
## 📝 Convenções de Código
|
||
|
||
### TypeScript
|
||
|
||
- Interfaces para todas as entidades
|
||
- Tipos explícitos em funções
|
||
- Evitar `any` (usar `unknown` quando necessário)
|
||
|
||
### Componentes React
|
||
|
||
- Functional components com hooks
|
||
- Props tipadas com interfaces
|
||
- Estado local com useState/useContext
|
||
- Effects para side effects
|
||
|
||
### Serviços
|
||
|
||
- Um serviço por entidade (doctorService, patientService)
|
||
- Métodos assíncronos com try/catch
|
||
- Logs de debug no console
|
||
- Tratamento de erros consistente
|
||
|
||
### Nomenclatura
|
||
|
||
- **Componentes:** PascalCase (ex: `AgendamentoConsulta`)
|
||
- **Arquivos:** kebab-case ou PascalCase conforme tipo
|
||
- **Variáveis:** camelCase (ex: `selectedDate`)
|
||
- **Constantes:** UPPER_SNAKE_CASE (ex: `API_CONFIG`)
|
||
|
||
---
|
||
|
||
## 🐛 Troubleshooting
|
||
|
||
### Erro 401 (Unauthorized)
|
||
|
||
- Verificar se token está no localStorage
|
||
- Tentar logout e login novamente
|
||
- Verificar permissões RLS no Supabase
|
||
|
||
### Slots não aparecem
|
||
|
||
- Verificar se médico tem disponibilidade configurada
|
||
- Verificar se data é futura
|
||
- Verificar logs da Edge Function
|
||
|
||
### Upload de avatar falha
|
||
|
||
- Verificar tamanho do arquivo (max 2MB)
|
||
- Verificar formato (jpg, png)
|
||
- Verificar permissões do Storage no Supabase
|
||
|
||
### Build falha
|
||
|
||
- Limpar cache: `rm -rf node_modules dist`
|
||
- Reinstalar: `pnpm install`
|
||
- Verificar versão do Node (18+)
|
||
|
||
---
|
||
|
||
## 👥 Equipe
|
||
|
||
**RiseUp Squad 18**
|
||
|
||
- Desenvolvimento: GitHub Copilot + Equipe
|
||
- Data: Outubro 2025
|
||
|
||
---
|
||
|
||
## 📄 Licença
|
||
|
||
Este projeto é privado e desenvolvido para fins educacionais.
|
||
|
||
---
|
||
|
||
## <20> Links Úteis
|
||
|
||
- [Supabase Docs](https://supabase.com/docs)
|
||
- [React Docs](https://react.dev/)
|
||
- [Vite Docs](https://vitejs.dev/)
|
||
- [TailwindCSS Docs](https://tailwindcss.com/)
|
||
- [Cloudflare Pages](https://pages.cloudflare.com/)
|
||
|
||
---
|
||
|
||
**Última atualização:** 30 de Outubro de 2025
|
||
|
||
- ✅ Veem apenas **seus próprios dados**
|
||
- ✅ Veem apenas **seus próprios laudos** (filtro: `patient_id = paciente`)
|
||
- ✅ Veem apenas **seus próprios agendamentos**
|
||
- ✅ Podem agendar consultas
|
||
|
||
#### 👩💼 Secretárias:
|
||
|
||
- ✅ Veem **todos os pacientes**
|
||
- ✅ Veem **todos os agendamentos**
|
||
- ✅ Veem **todos os laudos**
|
||
- ✅ Podem criar/editar agendamentos
|
||
|
||
---
|
||
|
||
## 📡 API e Serviços
|
||
|
||
### Estrutura de Services
|
||
|
||
O projeto usa uma arquitetura de **services** que encapsulam toda comunicação com o Supabase:
|
||
|
||
```
|
||
src/services/
|
||
├── api/
|
||
│ ├── client.ts # Cliente HTTP (Axios configurado)
|
||
│ └── config.ts # Configurações da API
|
||
├── auth/
|
||
│ ├── authService.ts # Login, signup, recuperação de senha
|
||
│ └── types.ts
|
||
├── users/
|
||
│ └── userService.ts # getUserInfo, createUser, deleteUser
|
||
├── patients/
|
||
│ └── patientService.ts # CRUD de pacientes
|
||
├── doctors/
|
||
│ └── doctorService.ts # CRUD de médicos
|
||
├── appointments/
|
||
│ └── appointmentService.ts # Agendamentos
|
||
└── availability/
|
||
└── availabilityService.ts # Disponibilidade médica
|
||
```
|
||
|
||
### Principais Endpoints
|
||
|
||
#### 🔐 Autenticação (authService)
|
||
|
||
```typescript
|
||
// Login com Email e Senha
|
||
await authService.login({ email, password });
|
||
// Retorna: { access_token, refresh_token, user }
|
||
|
||
// Magic Link (Login sem senha)
|
||
await authService.sendMagicLink("email@example.com");
|
||
// Envia email com link de autenticação
|
||
// Usuário clica no link e é automaticamente autenticado
|
||
|
||
// Recuperação de senha
|
||
await authService.requestPasswordReset("email@example.com");
|
||
// Envia email com link de reset
|
||
|
||
// Atualizar senha
|
||
await authService.updatePassword(accessToken, newPassword);
|
||
// Usado na página /reset-password
|
||
|
||
// Refresh token
|
||
await authService.refreshToken(refreshToken);
|
||
```
|
||
|
||
**Fluxo Magic Link:**
|
||
|
||
1. Usuário solicita magic link na tela de login
|
||
2. `localStorage.setItem("magic_link_redirect", "/painel-medico")` salva contexto
|
||
3. Supabase envia email com link único
|
||
4. Usuário clica no link
|
||
5. `Home.tsx` detecta hash params e redireciona para `/auth/callback`
|
||
6. `AuthCallback.tsx` processa tokens, salva no localStorage
|
||
7. `window.location.href` redireciona para painel salvo
|
||
8. Página recarrega com `AuthContext` atualizado
|
||
9. Usuário autenticado no painel correto ✅
|
||
|
||
#### 👤 Usuários (userService)
|
||
|
||
```typescript
|
||
// Buscar informações do usuário autenticado (com roles)
|
||
const userInfo = await userService.getUserInfo();
|
||
// Retorna: { id, email, full_name, roles: ['medico', 'admin'] }
|
||
|
||
// Criar usuário com role
|
||
await userService.createUser({
|
||
email: "user@example.com",
|
||
full_name: "Nome Completo",
|
||
role: "medico", // ou 'admin', 'paciente', 'secretaria'
|
||
});
|
||
|
||
// Deletar usuário
|
||
await userService.deleteUser(userId);
|
||
```
|
||
|
||
#### 🏥 Pacientes (patientService)
|
||
|
||
```typescript
|
||
// Listar pacientes
|
||
const patients = await patientService.list();
|
||
|
||
// Buscar por ID
|
||
const patient = await patientService.getById(id);
|
||
|
||
// Criar paciente
|
||
await patientService.create({
|
||
email: "paciente@example.com",
|
||
full_name: "Nome Paciente",
|
||
cpf: "12345678900",
|
||
phone_mobile: "11999999999",
|
||
});
|
||
|
||
// Atualizar paciente
|
||
await patientService.update(id, { phone_mobile: "11888888888" });
|
||
|
||
// Deletar paciente
|
||
await patientService.delete(id);
|
||
```
|
||
|
||
#### 👨⚕️ Médicos (doctorService)
|
||
|
||
```typescript
|
||
// Listar médicos
|
||
const doctors = await doctorService.list();
|
||
|
||
// Buscar por ID
|
||
const doctor = await doctorService.getById(id);
|
||
|
||
// Buscar disponibilidade
|
||
const slots = await doctorService.getAvailableSlots(doctorId, date);
|
||
```
|
||
|
||
#### 📅 Agendamentos (appointmentService)
|
||
|
||
```typescript
|
||
// Listar agendamentos (filtrado por role automaticamente)
|
||
const appointments = await appointmentService.list();
|
||
|
||
// Criar agendamento
|
||
await appointmentService.create({
|
||
patient_id: "uuid-paciente",
|
||
doctor_id: "uuid-medico",
|
||
scheduled_at: "2025-10-25T10:00:00",
|
||
reason: "Consulta de rotina",
|
||
});
|
||
|
||
// Atualizar status
|
||
await appointmentService.updateStatus(id, "confirmed");
|
||
// Status: requested, confirmed, completed, cancelled
|
||
|
||
// Cancelar
|
||
await appointmentService.cancel(id, "Motivo do cancelamento");
|
||
```
|
||
|
||
#### 📸 Avatares (avatarService)
|
||
|
||
```typescript
|
||
// Upload de avatar (usa FormData com x-upsert: true)
|
||
const file = event.target.files[0]; // File do input
|
||
const result = await avatarService.upload({
|
||
userId: user.id,
|
||
file: file,
|
||
});
|
||
// Retorna: { Key: "url-publica-do-avatar" }
|
||
|
||
// Obter URL pública do avatar
|
||
const url = avatarService.getPublicUrl({
|
||
userId: user.id,
|
||
ext: "png", // ou 'jpg', 'webp'
|
||
});
|
||
// Retorna: https://yuanqfswhberkoevtmfr.supabase.co/storage/v1/object/avatars/{userId}/avatar.png
|
||
|
||
// Auto-load de avatar (testa múltiplas extensões)
|
||
const extensions = ["png", "jpg", "webp"];
|
||
for (const ext of extensions) {
|
||
const url = avatarService.getPublicUrl({ userId: user.id, ext });
|
||
const response = await fetch(url, { method: "HEAD" });
|
||
if (response.ok) {
|
||
setAvatarUrl(url);
|
||
break;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Detalhes importantes:**
|
||
|
||
- Upload usa **multipart/form-data** via FormData
|
||
- Header `x-upsert: true` permite sobrescrever avatares existentes
|
||
- Suporta formatos: PNG, JPG, WEBP (máx 2MB)
|
||
- URLs públicas não requerem autenticação
|
||
- Avatar é carregado automaticamente nos painéis
|
||
|
||
---
|
||
|
||
## 🔧 Configuração da API
|
||
|
||
```typescript
|
||
// src/services/api/config.ts
|
||
export const API_CONFIG = {
|
||
SUPABASE_URL: "https://yuanqfswhberkoevtmfr.supabase.co",
|
||
SUPABASE_ANON_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
AUTH_URL: `${SUPABASE_URL}/auth/v1`,
|
||
REST_URL: `${SUPABASE_URL}/rest/v1`,
|
||
APP_URL: "https://mediconnectbrasil.app",
|
||
TIMEOUT: 30000,
|
||
STORAGE_KEYS: {
|
||
ACCESS_TOKEN: "mediconnect_access_token",
|
||
REFRESH_TOKEN: "mediconnect_refresh_token",
|
||
USER: "mediconnect_user",
|
||
},
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 Deploy no Cloudflare Pages
|
||
|
||
### Via Wrangler CLI
|
||
|
||
```powershell
|
||
# Build
|
||
pnpm build
|
||
|
||
# Deploy para production
|
||
npx wrangler pages deploy dist --project-name=mediconnect --branch=production
|
||
|
||
# Deploy com mudanças não commitadas
|
||
npx wrangler pages deploy dist --project-name=mediconnect --commit-dirty=true
|
||
```
|
||
|
||
### Configuração do Projeto
|
||
|
||
- **Production Branch:** `production`
|
||
- **Build Command:** `pnpm build`
|
||
- **Build Output:** `dist`
|
||
- **Custom Domain:** `mediconnectbrasil.app`
|
||
|
||
### URLs
|
||
|
||
- **Production:** https://mediconnectbrasil.app/
|
||
- **Preview:** https://mediconnect-5oz.pages.dev/
|
||
- **Branch Preview:** https://[branch].mediconnect-5oz.pages.dev/
|
||
|
||
---
|
||
|
||
## 1. Variáveis de Ambiente (`.env` / `.env.local`)
|
||
|
||
| Variável | Obrigatória | Descrição |
|
||
| ------------------------ | ----------- | --------------------------------------------------------------- |
|
||
| `VITE_SUPABASE_URL` | Sim | URL base do projeto Supabase (`https://<ref>.supabase.co`) |
|
||
| `VITE_SUPABASE_ANON_KEY` | Sim | Chave pública (anon) usada para Auth password grant e PostgREST |
|
||
| `VITE_APP_ENV` | Não | Identifica ambiente (ex: `dev`, `staging`, `prod`) |
|
||
|
||
**Nota:** As variáveis já estão configuradas no código em `src/services/api/config.ts`. Não é necessário criar arquivo `.env` para desenvolvimento local.
|
||
|
||
Boas práticas:
|
||
|
||
- Nunca exponha Service Role Key no frontend.
|
||
- Não comitar `.env` – usar `.env.example` como referência (se houver).
|
||
|
||
---
|
||
|
||
## 2. Fluxo de Login e Validação de Roles
|
||
|
||
### Exemplo: Login de Médico
|
||
|
||
```typescript
|
||
// pages/LoginMedico.tsx
|
||
const handleLogin = async (e: FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
try {
|
||
// 1. Autenticar com Supabase
|
||
const loginResponse = await authService.login({ email, password });
|
||
|
||
// 2. Salvar tokens
|
||
localStorage.setItem("access_token", loginResponse.access_token);
|
||
localStorage.setItem("refresh_token", loginResponse.refresh_token);
|
||
|
||
// 3. Buscar informações do usuário com roles
|
||
const userInfo = await userService.getUserInfo();
|
||
const roles = userInfo.roles || [];
|
||
|
||
// 4. Validar permissões
|
||
const isAdmin = roles.includes("admin");
|
||
const isGestor = roles.includes("gestor");
|
||
const isMedico = roles.includes("medico");
|
||
|
||
if (!isAdmin && !isGestor && !isMedico) {
|
||
toast.error("Você não tem permissão para acessar esta área");
|
||
await authService.logout();
|
||
return;
|
||
}
|
||
|
||
// 5. Redirecionar para o painel
|
||
navigate("/painel-medico");
|
||
} catch (error) {
|
||
toast.error("Email ou senha incorretos");
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Recuperação de Senha
|
||
|
||
### Fluxo Completo
|
||
|
||
1. **Usuário clica em "Esqueceu a senha?"**
|
||
2. **Sistema envia email com link de recuperação**
|
||
3. **Usuário clica no link → redireciona para `/reset-password`**
|
||
4. **Sistema extrai token do URL (#access_token=...)**
|
||
5. **Usuário define nova senha**
|
||
6. **Sistema atualiza senha e redireciona para login**
|
||
|
||
### Implementação
|
||
|
||
```typescript
|
||
// Solicitar recuperação (páginas de login)
|
||
const handlePasswordReset = async () => {
|
||
try {
|
||
await authService.requestPasswordReset(email);
|
||
toast.success("Link de recuperação enviado para seu email");
|
||
} catch (error) {
|
||
toast.error("Erro ao enviar email de recuperação");
|
||
}
|
||
};
|
||
|
||
// Página de reset (ResetPassword.tsx)
|
||
useEffect(() => {
|
||
// Extrair token do URL
|
||
const hash = window.location.hash;
|
||
const params = new URLSearchParams(hash.substring(1));
|
||
const token = params.get("access_token");
|
||
|
||
if (token) {
|
||
setAccessToken(token);
|
||
setIsLoading(false);
|
||
}
|
||
}, []);
|
||
|
||
const handleSubmit = async (e: FormEvent) => {
|
||
try {
|
||
await authService.updatePassword(accessToken, newPassword);
|
||
toast.success("Senha atualizada com sucesso!");
|
||
navigate("/login-paciente");
|
||
} catch (error) {
|
||
toast.error("Erro ao atualizar senha");
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Estrutura do Banco de Dados
|
||
|
||
### Tabelas Principais
|
||
|
||
#### `profiles`
|
||
|
||
```sql
|
||
- id (uuid, PK)
|
||
- email (text, unique)
|
||
- full_name (text)
|
||
- phone (text)
|
||
- created_at (timestamp)
|
||
- updated_at (timestamp)
|
||
```
|
||
|
||
#### `patients`
|
||
|
||
```sql
|
||
- id (uuid, PK, FK -> profiles)
|
||
- email (text, unique)
|
||
- full_name (text)
|
||
- cpf (text, unique)
|
||
- phone_mobile (text)
|
||
- birth_date (date)
|
||
- address (text)
|
||
- created_at (timestamp)
|
||
```
|
||
|
||
#### `doctors`
|
||
|
||
```sql
|
||
- id (uuid, PK, FK -> profiles)
|
||
- email (text, unique)
|
||
- full_name (text)
|
||
- specialty (text)
|
||
- crm (text, unique)
|
||
- phone (text)
|
||
- created_at (timestamp)
|
||
```
|
||
|
||
#### `appointments`
|
||
|
||
```sql
|
||
- id (uuid, PK)
|
||
- patient_id (uuid, FK -> patients)
|
||
- doctor_id (uuid, FK -> doctors)
|
||
- scheduled_at (timestamp)
|
||
- status (enum: requested, confirmed, completed, cancelled)
|
||
- reason (text)
|
||
- notes (text)
|
||
- created_at (timestamp)
|
||
```
|
||
|
||
#### `user_roles`
|
||
|
||
```sql
|
||
- user_id (uuid, FK -> profiles)
|
||
- role (text: admin, gestor, medico, secretaria, paciente)
|
||
- created_at (timestamp)
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Armazenamento de Tokens
|
||
|
||
O sistema usa uma estratégia híbrida para máxima segurança:
|
||
|
||
| Tipo | Local | Expiração Natural |
|
||
| ------------- | ------------ | --------------------------------- |
|
||
| Access Token | localStorage | 1 hora (renovado automaticamente) |
|
||
| Refresh Token | localStorage | 30 dias (ou revogação backend) |
|
||
| User Snapshot | localStorage | Limpo em logout |
|
||
|
||
**Segurança:**
|
||
|
||
- Tokens são limpos automaticamente no logout
|
||
- Refresh automático quando access_token expira (401)
|
||
- Interceptors garantem tokens válidos em todas as requisições
|
||
|
||
Riscos remanescentes:
|
||
|
||
- XSS ainda pode ler refresh token dentro da mesma aba.
|
||
- Ataques supply-chain podem capturar tokens em runtime.
|
||
|
||
Mitigações planejadas:
|
||
|
||
1. CSP + bloqueio de inline script não autorizado.
|
||
2. Auditoria de dependências e lockfile imutável.
|
||
3. (Opcional) Migrar refresh para cookie httpOnly + rotacionamento curto (exige backend/proxy).
|
||
|
||
Fallback / Migração:
|
||
|
||
- Em primeira utilização o `tokenStore` migra chaves legacy (`authToken`, `refreshToken`, `authUser`) e remove do `localStorage`.
|
||
|
||
Operações:
|
||
|
||
- `tokenStore.setTokens(access, refresh?)` atualiza memória e session.
|
||
- `tokenStore.clear()` remove tudo (usado em logout e erro crítico de refresh).
|
||
|
||
Fluxo de Refresh:
|
||
|
||
1. Requisição falha com 401.
|
||
2. Wrapper (`http.ts`) obtém refresh do `tokenStore`.
|
||
|
||
---
|
||
|
||
## 7. Scripts Utilitários
|
||
|
||
### Gerenciamento de Usuários
|
||
|
||
```bash
|
||
# Listar todos os usuários
|
||
node scripts/manage-users.js list
|
||
|
||
# Criar usuário
|
||
node scripts/manage-users.js create email@example.com "Nome Completo"
|
||
|
||
# Deletar usuário
|
||
node scripts/manage-users.js delete user-id
|
||
|
||
# Limpar usuários de teste
|
||
node scripts/cleanup-users.js
|
||
```
|
||
|
||
### Testes de API
|
||
|
||
```bash
|
||
# Testar recuperação de senha
|
||
node test-password-recovery.js
|
||
|
||
# Criar usuário Fernando (exemplo)
|
||
node create-fernando.cjs
|
||
|
||
# Buscar usuário Fernando
|
||
node search-fernando.cjs
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Padrões de Código
|
||
|
||
### Nomenclatura
|
||
|
||
- **Componentes:** PascalCase (`LoginPaciente.tsx`)
|
||
- **Serviços:** camelCase (`authService.ts`)
|
||
- **Hooks:** camelCase com prefixo `use` (`useAuth.ts`)
|
||
- **Tipos:** PascalCase (`LoginInput`, `AuthUser`)
|
||
|
||
### Estrutura de Componentes
|
||
|
||
```typescript
|
||
// Imports
|
||
import React, { useState, useEffect } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { serviceImport } from "../services";
|
||
|
||
// Types
|
||
interface Props {
|
||
// ...
|
||
}
|
||
|
||
// Component
|
||
const ComponentName: React.FC<Props> = ({ ...props }) => {
|
||
// Hooks
|
||
const navigate = useNavigate();
|
||
const [state, setState] = useState();
|
||
|
||
// Effects
|
||
useEffect(() => {
|
||
// ...
|
||
}, []);
|
||
|
||
// Handlers
|
||
const handleAction = async () => {
|
||
// ...
|
||
};
|
||
|
||
// Render
|
||
return <div>{/* JSX */}</div>;
|
||
};
|
||
|
||
export default ComponentName;
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Tecnologias Utilizadas
|
||
|
||
### Frontend
|
||
|
||
- **React** 18.3.1 - Biblioteca UI
|
||
- **TypeScript** 5.9.3 - Tipagem estática
|
||
- **Vite** 7.1.10 - Build tool
|
||
- **React Router** 6.30.1 - Roteamento
|
||
- **Tailwind CSS** 3.4.17 - Estilização
|
||
- **Axios** 1.12.2 - Cliente HTTP
|
||
- **React Hot Toast** 2.4.1 - Notificações
|
||
- **date-fns** 4.1.0 - Manipulação de datas
|
||
|
||
### Backend
|
||
|
||
- **Supabase** - Backend as a Service
|
||
- **PostgreSQL** - Banco de dados relacional
|
||
- **Supabase Auth** - Autenticação JWT
|
||
|
||
### Deploy
|
||
|
||
- **Cloudflare Pages** - Hospedagem frontend
|
||
- **Wrangler** 4.44.0 - CLI Cloudflare
|
||
|
||
---
|
||
|
||
## 10. Suporte e Contato
|
||
|
||
- **Equipe:** Squad 18 - Rise Up
|
||
- **Repositório:** https://git.popcode.com.br/RiseUP/riseup-squad18.git
|
||
- **Trello:** [Squad 18 - Idealização/Planejamento](https://trello.com/b/CCl3Azxk/squad-18-idealizacao-planejamento)
|
||
|
||
---
|
||
|
||
**Desenvolvido com ❤️ pela Squad 18**
|