new file: .gitignore

new file:   src/App.css
new file:   src/App.jsx
new file:   src/assets/figma/login-clinic.png
new file:   src/assets/hero.png
new file:   src/assets/react.svg
new file:   src/assets/vite.svg
new file:   src/components/AppShell.jsx
new file:   src/components/Brand.jsx
new file:   src/components/ui.jsx
new file:   src/data/mockData.js
new file:   src/index.css
new file:   src/main.jsx
new file:   src/pages/AgendaPage.jsx
new file:   src/pages/AnalyticsPage.jsx
new file:   src/pages/AuthPages.jsx
new file:   src/pages/HomePage.jsx
new file:   src/pages/MedicalRecordsPage.jsx
new file:   src/pages/MessagesPage.jsx
new file:   src/pages/NotFoundPage.jsx
new file:   src/pages/PatientsPage.jsx
new file:   src/pages/ProfilePage.jsx
new file:   src/pages/ReportsPage.jsx
new file:   src/pages/SettingsPage.jsx
new file:   src/pages/TeamPage.jsx
new file:   src/pages/VisitsPage.jsx
new file:   src/repositories/analyticsRepository.js
new file:   src/repositories/appointmentRepository.js
new file:   src/repositories/communicationRepository.js
new file:   src/repositories/homeRepository.js
new file:   src/repositories/medicalRecordRepository.js
new file:   src/repositories/patientRepository.js
new file:   src/repositories/professionalRepository.js
new file:   src/repositories/profileRepository.js
new file:   src/repositories/reportRepository.js
new file:   src/repositories/settingsRepository.js
new file:   src/repositories/visitRepository.js
new file:   src/services/analyticsService.js
new file:   src/services/appointmentService.js
new file:   src/services/communicationService.js
new file:   src/services/homeService.js
new file:   src/services/medicalRecordService.js
new file:   src/services/patientService.js
new file:   src/services/professionalService.js
new file:   src/services/profileService.js
new file:   src/services/reportService.js
new file:   src/services/settingsService.js
This commit is contained in:
2026-04-27 00:47:58 -03:00
parent 27226b3df8
commit db2d1562e0
37 changed files with 7324 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { patientRepository } from './patientRepository.js'
const fallbackTopPatients = [
{ id: 'ana-souza', name: 'Carlos Eduardo Santos', visits: 12, revenue: 4200 },
{ id: 'carla-mendes', name: 'Fernanda Lima', visits: 10, revenue: 6000 },
{ id: 'bruno-lima', name: 'Mariana Costa', visits: 8, revenue: 1600 },
{ id: 'roberto-campos', name: 'Roberto Campos', visits: 7, revenue: 2450 },
{ id: 'sandra-oliveira', name: 'Sandra Oliveira', visits: 6, revenue: 1500 },
]
export const analyticsRepository = {
getDashboardData() {
return {
absenteeismData: [
{ month: 'Out', taxa: 18, meta: 15 },
{ month: 'Nov', taxa: 16, meta: 15 },
{ month: 'Dez', taxa: 22, meta: 15 },
{ month: 'Jan', taxa: 14, meta: 15 },
{ month: 'Fev', taxa: 12, meta: 15 },
{ month: 'Mar', taxa: 14.2, meta: 15 },
],
consultationsData: [
{ month: 'Out', total: 380, realizadas: 312 },
{ month: 'Nov', total: 420, realizadas: 352 },
{ month: 'Dez', total: 350, realizadas: 273 },
{ month: 'Jan', total: 450, realizadas: 387 },
{ month: 'Fev', total: 400, realizadas: 352 },
{ month: 'Mar', total: 460, realizadas: 395 },
],
doctorPerformance: [
{ name: 'Dra. Ana Silva', consultas: 185, noShow: 12, satisfacao: 4.8 },
{ name: 'Dr. Carlos Mendes', consultas: 142, noShow: 18, satisfacao: 4.6 },
{ name: 'Dr. Roberto Nunes', consultas: 128, noShow: 22, satisfacao: 4.4 },
],
insuranceData: [
{ name: 'Particular', value: 35, color: '#3b82f6' },
{ name: 'Unimed', value: 28, color: '#10b981' },
{ name: 'Bradesco', value: 18, color: '#8b5cf6' },
{ name: 'Amil', value: 12, color: '#f59e0b' },
{ name: 'SUS', value: 7, color: '#ef4444' },
],
kpis: [
{ label: 'Consultas Realizadas', value: '395', change: '+12%', up: true, icon: 'calendar' },
{ label: 'Taxa de No-Show', value: '14.2%', change: '-3.1%', up: false, icon: 'activity' },
{ label: 'Faturamento', value: 'R$ 56K', change: '+24%', up: true, icon: 'dollar' },
{ label: 'Pacientes Ativos', value: '1.247', change: '+8%', up: true, icon: 'users' },
],
revenueData: [
{ month: 'Out', valor: 42000 },
{ month: 'Nov', valor: 48000 },
{ month: 'Dez', valor: 38000 },
{ month: 'Jan', valor: 52000 },
{ month: 'Fev', valor: 45000 },
{ month: 'Mar', valor: 56000 },
],
topPatients: fallbackTopPatients.map((patient) => ({
...patient,
name: patientRepository.getById(patient.id)?.name || patient.name,
})),
}
},
}

View File

@@ -0,0 +1,35 @@
import { appointments as mockAppointments } from '../data/mockData.js'
export const appointmentRepository = {
getAll() {
return mockAppointments
},
getTodayTimeline() {
return [
{ hour: '08:00', patient: 'Carla Mendes', type: 'Consulta inicial', status: 'Confirmada', patientId: 'carla-mendes' },
{ hour: '09:30', patient: 'Ana Souza', type: 'Retorno clinico', status: 'Em triagem', patientId: 'ana-souza' },
{ hour: '11:00', patient: 'Diego Alves', type: 'Acompanhamento', status: 'Aguardando', patientId: 'diego-alves' },
{ hour: '14:30', patient: 'Bruno Lima', type: 'Teleconsulta', status: 'Confirmada', patientId: 'bruno-lima' },
{ hour: '16:00', patient: 'Horario protegido', type: 'Revisao de laudos', status: 'Bloqueado', patientId: null },
]
},
getPredictiveQueueSummary() {
return [
{ label: 'Alta prioridade', value: 3, tone: 'red' },
{ label: 'A confirmar', value: 5, tone: 'amber' },
{ label: 'Teleconsultas', value: 6, tone: 'blue' },
]
},
getWeekDays() {
return [
{ label: 'Seg', day: '06', active: false, count: 6 },
{ label: 'Ter', day: '07', active: true, count: 18 },
{ label: 'Qua', day: '08', active: false, count: 12 },
{ label: 'Qui', day: '09', active: false, count: 9 },
{ label: 'Sex', day: '10', active: false, count: 15 },
]
},
}

View File

@@ -0,0 +1,33 @@
export const communicationRepository = {
getCampaigns() {
return [
{ title: 'Lembretes Anti-Falta', desc: 'Envio automatico 48h e 4h antes', count: '324 pacientes elegiveis' },
{ title: 'Vacinacao 2026', desc: 'Campanha de vacinacao anual', count: '156 pacientes elegiveis' },
{ title: 'Retorno Pendente', desc: 'Pacientes com retorno atrasado', count: '42 pacientes elegiveis' },
]
},
getInitialMessages() {
return [
{ id: '1', patient: 'Carlos Eduardo Santos', channel: 'whatsapp', template: 'Lembrete 48h', sentAt: '25/03/2026 09:00', status: 'lida', response: 'Confirmado!' },
{ id: '2', patient: 'Mariana Costa', channel: 'whatsapp', template: 'Lembrete 48h', sentAt: '25/03/2026 09:05', status: 'entregue' },
{ id: '3', patient: 'Joao Pedro Alves', channel: 'whatsapp', template: 'Lembrete 4h', sentAt: '27/03/2026 05:00', status: 'pendente' },
{ id: '4', patient: 'Fernanda Lima', channel: 'email', template: 'Confirmacao de Agendamento', sentAt: '24/03/2026 15:30', status: 'lida' },
{ id: '5', patient: 'Roberto Campos', channel: 'whatsapp', template: 'Lembrete Extra (Risco Alto)', sentAt: '26/03/2026 10:00', status: 'entregue' },
{ id: '6', patient: 'Sandra Oliveira', channel: 'sms', template: 'Lembrete 48h', sentAt: '24/03/2026 08:00', status: 'falha' },
{ id: '7', patient: 'Lucia Ferreira', channel: 'email', template: 'Resultado de Exames', sentAt: '26/03/2026 14:00', status: 'lida' },
{ id: '8', patient: 'Paulo Ricardo', channel: 'whatsapp', template: 'Reagendamento Sugerido (IA)', sentAt: '27/03/2026 07:00', status: 'pendente' },
]
},
getInitialTemplates() {
return [
{ id: 't1', name: 'Lembrete 48h', channel: 'whatsapp', content: 'Ola {nome}! Lembramos que sua consulta esta agendada para {data} as {hora}. Confirme respondendo SIM.', category: 'Lembrete' },
{ id: 't2', name: 'Lembrete 4h', channel: 'whatsapp', content: 'Ola {nome}! Sua consulta e hoje as {hora}. Estamos te esperando!', category: 'Lembrete' },
{ id: 't3', name: 'Lembrete Extra (Risco Alto)', channel: 'whatsapp', content: 'Ola {nome}! Notamos que sua presenca e muito importante. Podemos confirmar sua consulta de {data}?', category: 'IA' },
{ id: 't4', name: 'Confirmacao de Agendamento', channel: 'email', content: 'Prezado(a) {nome}, confirmamos seu agendamento para {data} as {hora} com {medico}.', category: 'Agendamento' },
{ id: 't5', name: 'Resultado de Exames', channel: 'email', content: 'Prezado(a) {nome}, seus resultados de exames estao disponiveis. Acesse o portal do paciente.', category: 'Exames' },
{ id: 't6', name: 'Reagendamento Sugerido (IA)', channel: 'whatsapp', content: 'Ola {nome}! Que tal reagendar sua consulta para um horario mais conveniente? Temos vagas em {sugestoes}.', category: 'IA' },
]
},
}

View File

@@ -0,0 +1,22 @@
export const homeRepository = {
getDashboardOverview() {
return {
appointmentsToday: [
{ time: '09:00', name: 'Ana Souza', patientId: 'ana-souza', status: 'Risco moderado' },
{ time: '10:30', name: 'Bruno Lima', patientId: 'bruno-lima', status: 'Alta prioridade' },
{ time: '14:00', name: 'Carla Mendes', patientId: 'carla-mendes', status: 'Confirmada' },
],
metrics: [
{ label: 'Consultas Hoje', value: '42', change: '+12%', tone: 'blue' },
{ label: 'Taxa de Ocupacao', value: '14.2%', change: '+8%', tone: 'violet' },
{ label: 'No-show', value: '35', change: '-3%', tone: 'green' },
],
reportCards: [
{ title: 'Proximos Pacientes', description: 'Agenda de hoje e status preditivo', icon: 'calendar' },
{ title: 'Pacientes Frequentes', description: 'Mais atendidos neste mes', icon: 'users' },
{ title: 'Produtividade Medica', description: 'Consultas realizadas e avaliacoes', icon: 'brand' },
{ title: 'Analise de Convenios', description: 'Distribuicao de atendimentos', icon: 'building' },
],
}
},
}

View File

@@ -0,0 +1,15 @@
export const medicalRecordRepository = {
getRecordTypes() {
return ['Consulta Retorno', 'Primeira Consulta', 'Exame', 'Avaliacao Pre-Op']
},
getInitialRecords() {
return [
{ id: 'record-1', patient: 'Carlos Eduardo Santos', date: '27/03/2026', doctor: 'Dra. Ana Silva', type: 'Consulta Retorno', cid: 'I10 - Hipertensao', status: 'completo', summary: 'Paciente relata melhora com medicacao. PA: 130/85. Mantida conduta.' },
{ id: 'record-2', patient: 'Mariana Costa', date: '26/03/2026', doctor: 'Dra. Ana Silva', type: 'Exame', cid: 'Z01.7 - Exame laboratorial', status: 'completo', summary: 'Resultados de hemograma dentro da normalidade. Solicitar retorno em 6 meses.' },
{ id: 'record-3', patient: 'Joao Pedro Alves', date: '25/03/2026', doctor: 'Dr. Carlos Mendes', type: 'Primeira Consulta', cid: 'R10 - Dor abdominal', status: 'rascunho', summary: 'Queixa de dor abdominal ha 2 semanas. Solicitados exames complementares.' },
{ id: 'record-4', patient: 'Fernanda Lima', date: '24/03/2026', doctor: 'Dra. Ana Silva', type: 'Avaliacao Pre-Op', cid: 'K80 - Colelitiase', status: 'completo', summary: 'Apta para procedimento cirurgico. Exames pre-operatorios normais.' },
{ id: 'record-5', patient: 'Roberto Campos', date: '22/03/2026', doctor: 'Dr. Roberto Nunes', type: 'Consulta Retorno', cid: 'E11 - DM Tipo 2', status: 'completo', summary: 'HbA1c: 7.2%. Ajuste de metformina. Retorno em 3 meses.' },
]
},
}

View File

@@ -0,0 +1,122 @@
const BASE_URL = 'https://yuanqfswhberkoevtmfr.supabase.co/rest/v1'
const FUNCTIONS_URL = 'https://yuanqfswhberkoevtmfr.supabase.co/functions/v1'
const API_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ'
const headers = {
'apikey': API_KEY,
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
}
export const patientRepository = {
// 1. Listar pacientes
async getAll() {
const response = await fetch(`${BASE_URL}/patients?select=*`, { headers })
if (!response.ok) throw new Error('Erro ao buscar pacientes')
return response.json()
},
async getById(patientId) {
const patients = await this.getAll()
return patients.find((p) => String(p.id) === String(patientId)) || null
},
async getDirectoryRows() {
const patients = await this.getAll()
return patients.map((patient) => ({
...patient,
name: patient.full_name,
phone: patient.phone_mobile,
detailId: patient.id,
insurance: 'Particular',
city: 'Recife',
state: 'PE',
vip: false,
lastVisitIso: null,
lastVisit: 'Ainda nao houve atendimento',
nextVisit: 'Nenhum atendimento agendado',
}))
},
// 2. Criar paciente (direto)
async create(data) {
const body = {
full_name: data.name,
cpf: data.cpf,
email: data.email,
phone_mobile: data.phone,
birth_date: data.birthDate || null,
created_by: data.createdBy || '00000000-0000-0000-0000-000000000000',
}
const response = await fetch(`${BASE_URL}/patients`, {
method: 'POST',
headers: { ...headers, 'Prefer': 'return=representation' },
body: JSON.stringify(body),
})
if (!response.ok) {
const error = await response.json().catch(() => ({}))
console.error('Erro da API ao criar paciente:', error)
throw new Error(error.message || error.hint || JSON.stringify(error))
}
return response.json()
},
// 3. Criar paciente com validação de CPF (Edge Function)
async createWithValidation(data) {
const body = {
full_name: data.name,
cpf: data.cpf,
email: data.email,
phone_mobile: data.phone,
birth_date: data.birthDate || null,
created_by: data.createdBy || '00000000-0000-0000-0000-000000000000',
}
const response = await fetch(`${FUNCTIONS_URL}/create-patient`, {
method: 'POST',
headers,
body: JSON.stringify(body),
})
if (!response.ok) {
const error = await response.json().catch(() => ({}))
throw new Error(error.message || 'Erro ao criar paciente com validacao')
}
return response.json()
},
// 4. Atualizar paciente
async update(patientId, data) {
const body = {
full_name: data.name,
cpf: data.cpf,
email: data.email,
phone_mobile: data.phone,
birth_date: data.birthDate || null,
}
const response = await fetch(`${BASE_URL}/patients?id=eq.${patientId}`, {
method: 'PATCH',
headers: { ...headers, 'Prefer': 'return=representation' },
body: JSON.stringify(body),
})
if (!response.ok) throw new Error('Erro ao atualizar paciente')
return response.json()
},
// 5. Deletar paciente
async remove(patientId) {
const response = await fetch(`${BASE_URL}/patients?id=eq.${patientId}`, {
method: 'DELETE',
headers,
})
if (!response.ok) throw new Error('Erro ao deletar paciente')
return true
},
}

View File

@@ -0,0 +1,14 @@
import { professionals as mockProfessionals } from '../data/mockData.js'
export const professionalRepository = {
getAll() {
return mockProfessionals
},
getCoverageMap() {
return {
slots: ['08-12', '09-13', '10-15', '13-18', '08-14'],
weekdays: ['Seg', 'Ter', 'Qua', 'Qui', 'Sex'],
}
},
}

View File

@@ -0,0 +1,11 @@
export const profileRepository = {
getCurrentUserProfile() {
return {
email: 'henrique.cardoso@mediconnect.com.br',
name: 'Dr. Henrique Cardoso',
phone: '(81) 98888-0101',
role: 'Medico Clinico Geral',
unit: 'Clinica Boa Vista',
}
},
}

View File

@@ -0,0 +1,121 @@
const reportTypes = [
'Atestado Medico',
'Laudo de Exame',
'Laudo de Imagem',
'Relatorio Cirurgico',
'Declaracao de Acompanhante',
'Encaminhamento',
]
const doctors = ['Dra. Ana Silva', 'Dr. Carlos Mendes', 'Dr. Roberto Nunes']
const currentUser = 'Dra. Ana Silva'
const adminUsers = ['Dr. Roberto Nunes']
export const reportRepository = {
getAdminUsers() {
return adminUsers
},
getCurrentUser() {
return currentUser
},
getDoctors() {
return doctors
},
getInitialReports() {
return [
{
id: 'report-1',
type: 'Atestado Medico',
patient: 'Carlos Eduardo Santos',
doctor: 'Dra. Ana Silva',
date: '27/03/2026',
status: 'finalizado',
content: 'Atesto que o paciente esteve em consulta medica nesta data, necessitando de repouso por 2 dias.',
showDate: true,
signDigital: true,
versions: [
{ version: 1, action: 'Criado', user: 'Dra. Ana Silva', summary: 'Laudo criado' },
{ version: 2, action: 'Editado', user: 'Dra. Ana Silva', summary: 'Ajuste no periodo de repouso' },
{ version: 3, action: 'Liberado', user: 'Dra. Ana Silva', summary: 'Laudo liberado e finalizado' },
],
},
{
id: 'report-2',
type: 'Laudo de Exame',
patient: 'Mariana Costa',
doctor: 'Dra. Ana Silva',
date: '26/03/2026',
status: 'enviado',
content: 'Laudo referente ao exame de ecocardiograma. Resultado dentro dos parametros normais.',
showDate: true,
signDigital: true,
versions: [
{ version: 1, action: 'Criado', user: 'Dr. Carlos Mendes', summary: 'Laudo criado' },
{ version: 2, action: 'Editado', user: 'Dra. Ana Silva', summary: 'Adicao da data do exame' },
{ version: 3, action: 'Liberado', user: 'Dra. Ana Silva', summary: 'Conclusao incluida' },
{ version: 4, action: 'Enviado', user: 'Dr. Roberto Nunes', summary: 'Laudo enviado ao paciente' },
],
},
{
id: 'report-3',
type: 'Relatorio Cirurgico',
patient: 'Fernanda Lima',
doctor: 'Dr. Carlos Mendes',
date: '25/03/2026',
status: 'rascunho',
content: 'Relatorio do procedimento de colecistectomia laparoscopica realizado sob anestesia geral.',
showDate: false,
signDigital: true,
versions: [
{ version: 1, action: 'Criado', user: 'Dr. Carlos Mendes', summary: 'Relatorio criado' },
{ version: 2, action: 'Rascunho', user: 'Dr. Carlos Mendes', summary: 'Detalhamento do procedimento' },
],
},
{
id: 'report-4',
type: 'Declaracao de Acompanhante',
patient: 'Joao Pedro Alves',
doctor: 'Dr. Roberto Nunes',
date: '24/03/2026',
status: 'finalizado',
content: 'Declaro que o acompanhante esteve presente durante todo o periodo de internacao.',
showDate: true,
signDigital: false,
versions: [
{ version: 1, action: 'Criado', user: 'Dr. Roberto Nunes', summary: 'Declaracao criada e liberada' },
],
},
{
id: 'report-5',
type: 'Laudo de Imagem',
patient: 'Roberto Campos',
doctor: 'Dra. Ana Silva',
date: '22/03/2026',
status: 'enviado',
content: 'Ultrassonografia de abdomen total sem achados patologicos relevantes.',
showDate: true,
signDigital: true,
versions: [
{ version: 1, action: 'Criado', user: 'Dra. Ana Silva', summary: 'Laudo criado' },
{ version: 2, action: 'Liberado', user: 'Dra. Ana Silva', summary: 'Conclusao adicionada' },
{ version: 3, action: 'Enviado', user: 'Dr. Roberto Nunes', summary: 'Laudo enviado ao paciente' },
],
},
]
},
getReportTypes() {
return reportTypes
},
getTemplates() {
return [
{ id: 'template-1', name: 'Atestado de Repouso Simples', type: 'Atestado Medico', description: 'Atestado padrao concedendo dias de repouso ao paciente.', content: 'Atesto, para os devidos fins, que o(a) paciente necessita de repouso pelo periodo indicado.' },
{ id: 'template-2', name: 'Laudo de Hemograma', type: 'Laudo de Exame', description: 'Resultado de hemograma completo com interpretacao clinica.', content: 'Laudo de hemograma completo com parametros avaliados e interpretacao clinica.' },
{ id: 'template-3', name: 'Relatorio Cirurgico', type: 'Relatorio Cirurgico', description: 'Relatorio padronizado para procedimento cirurgico.', content: 'Relatorio do procedimento cirurgico, achados, conduta e evolucao imediata.' },
]
},
}

View File

@@ -0,0 +1,23 @@
export const settingsRepository = {
getIntegrations() {
return [
['WhatsApp Business', 'Envio automatico de lembretes e confirmacoes', true, 'bg-emerald-500'],
['Google Calendar', 'Sincronizacao bidirecional de agenda', false, 'bg-blue-500'],
['Stripe / PagSeguro', 'Pagamentos online e links de cobranca', true, 'bg-violet-500'],
['CFM - Conselho Federal de Medicina', 'Validacao automatica de CRM', false, 'bg-amber-500'],
['ANS - Planos de Saude', 'Integracao com tabela TUSS e convenios', false, 'bg-rose-500'],
['API de IA Preditiva', 'Score de absenteismo e predicao de faltas', true, 'bg-[#3b82f6]'],
]
},
getSections() {
return [
{ id: 'aparencia', label: 'Aparencia', description: 'Tema, cores e exibicao', icon: 'palette' },
{ id: 'notificacoes', label: 'Notificacoes', description: 'Alertas e lembretes', icon: 'bell' },
{ id: 'privacidade', label: 'Privacidade & LGPD', description: 'Dados e conformidade', icon: 'shield' },
{ id: 'conta', label: 'Conta & Perfil', description: 'Informacoes pessoais', icon: 'user' },
{ id: 'integracoes', label: 'Integracoes', description: 'APIs e sistemas externos', icon: 'globe' },
{ id: 'dados', label: 'Dados & Backup', description: 'Exportacao e backup', icon: 'database' },
]
},
}

View File

@@ -0,0 +1,15 @@
import { careQueue as mockCareQueue } from '../data/mockData.js'
export const visitRepository = {
getCareQueue() {
return mockCareQueue
},
getStages() {
return [
{ title: 'Triagem', description: 'Sinais vitais, queixa principal e alerta de risco antes da chamada medica.' },
{ title: 'Atendimento medico', description: 'Consulta em andamento, conduta, prescricao e solicitacao de exames.' },
{ title: 'Pos-consulta', description: 'Orientacoes finais, documentos emitidos e retorno sugerido pela equipe.' },
]
},
}