尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Sistema de gestão de plantões hospitalares do GAMI (Grupo Anestesia Materno Infantil): escala de plantões, trocas entre médicos, férias e congressos, fichas anestésicas, casos fora, gestão de usuários e o BM Financeiro mensal.
O projeto é dividido em duas aplicações:
Backend
@supabase/supabase-js recomenda v20+)Frontend
.xlsxDeploy: backend no Render, frontend na Vercel, banco no Supabase.
Plantão/
├── Backend/
│ ├── src/
│ │ ├── controllers/
│ │ │ ├── authController.js # Login, refresh, cadastro, /me (perfil e troca de senha) e redefinição de senha
│ │ │ ├── users.js # Busca de médicos e gestão de usuários (inclui a aprovação de cadastros)
│ │ │ ├── plantoes.js # Plantões, vagas, trocas e histórico
│ │ │ ├── afastamentos.js # Férias e congressos (pedido do médico, aprovação da gestão)
│ │ │ ├── fichas.js # Fichas anestésicas (com foto) e a exportação para o faturamento
│ │ │ ├── casosFora.js # Casos fora da escala
│ │ │ ├── hospitais.js # Hospitais dos casos fora
│ │ │ ├── dashboard.js # Painel pessoal e visão da gestão
│ │ │ └── bmFinanceiro.js # Resumo mensal: pontos dos sócios e plantões dos plantonistas
│ │ ├── services/ # Regras compartilhadas: vagas, conflitos de horário, trocas por horas, afastamentos, usuários
│ │ ├── middleware/
│ │ │ ├── auth.js # Validação do JWT e checagem de roles
│ │ │ └── rateLimit.js # Limites de login, cadastro e e-mails
│ │ ├── routes/
│ │ ├── app.js # Configuração do Express
│ │ └── server.js # Entrypoint — sobe o servidor
│ ├── scripts/
│ │ └── seed-local.js # Dados de teste do Supabase local
│ ├── Dockerfile
│ ├── .env.example
│ └── package.json
├── Frontend/
│ ├── app/
│ │ ├── page.tsx # Página inicial (pública)
│ │ ├── login/ register/ forgot-password/ reset-password/
│ │ └── (app)/ # Área logada (com sidebar)
│ │ ├── dashboard/
│ │ ├── plantoes/ # Calendário de plantões
│ │ ├── meu-plantao/ # Semana no formato da escala
│ │ ├── fichas/ # Fichas anestésicas
│ │ ├── casos-fora/ # Casos fora
│ │ ├── perfil/ # Meu perfil (dados, senha, dias disponíveis, férias e congressos)
│ │ ├── usuarios/ # Gestão de usuários
│ │ └── bm-financeiro/ # BM Financeiro
│ ├── components/ # Modais, calendário, lista de participantes, sidebar, badges
│ ├── public/ # Logos da GAMI
│ ├── .env.example
│ └── package.json
├── supabase/
│ ├── config.toml # Supabase local
│ └── migrations/ # Schema do banco
├── docker-compose.yml
└── README.md
cp Backend/.env.example Backend/.env
PORT=3000
SUPABASE_URL=https://xxxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# URL do frontend, usada no link de redefinição de senha enviado por e-mail
FRONTEND_URL=http://localhost:3001
⚠️ A
SUPABASE_SERVICE_ROLE_KEYé uma chave administrativa (ignora o RLS) — nunca exponha no frontend ou em repositórios públicos.
cp Frontend/.env.example Frontend/.env.local
NEXT_PUBLIC_API_URL=http://localhost:3000
Para testar localmente, use o Supabase local (Postgres, Auth e Storage no Docker), com dados de teste. Precisa da Supabase CLI (brew install supabase/tap/supabase).
supabase start # na raiz do repositório; sobe o Supabase local
cd Backend && npm run db:reset # zera o banco local, aplica as migrations e cria os dados de teste
Backend/.env aponta para o Supabase local (http://host.docker.internal:54321, porque o backend roda no Docker). A chave service_role local aparece no supabase status.Backend/.env.producao (fora do git). Para subir o backend contra ela: ENV_FILE=.env.producao docker-compose up.senha123: [email protected] (escritório + admin), [email protected] (técnico), [email protected] (sócia), [email protected] (plantonista) etc. A lista completa sai no fim do npm run db:reset.http://127.0.0.1:54323. E-mails (redefinição de senha): http://127.0.0.1:54324.host.docker.internal: echo "127.0.0.1 host.docker.internal" | sudo tee -a /etc/hosts.O schema fica em supabase/migrations/. A produção não aplica migrations sozinha: cada mudança de schema vira um arquivo novo em supabase/migrations/ e também é rodada manualmente no SQL Editor do Supabase de produção.
docker-compose up
A API ficará disponível em http://localhost:3000.
O Docker monta o volume
./Backend/src, então alterações no código reiniciam automaticamente via nodemon.
cd Backend
npm install
npm run dev # hot reload
npm start # produção
Sem Docker, use SUPABASE_URL=http://127.0.0.1:54321 no .env para o Supabase local.
cd Frontend
npm install
npm run dev -- -p 3001
O frontend ficará disponível em http://localhost:3001 (a porta 3000 é usada pela API).
| Role | Descrição |
|---|---|
anestesita_socio | Anestesista sócio — entra em qualquer vaga do plantão; no BM conta pontos pela vaga |
anestesita_plantonista | Anestesista plantonista — entra em qualquer vaga do plantão; no BM conta plantões por categoria (se também for sócio, conta pontos) |
tecnico | Técnico — faz parte da gestão |
escritorio | Escritório — faz parte da gestão |
is_admin, que dá acesso de gestão e só pode ser concedida ou retirada por outro administrador.escritorio, tecnico e administradores. É quem monta a escala, aprova cadastros e afastamentos e edita registros de outras pessoas.app_metadata no Supabase — só a service role key consegue alterá-las./register): qualquer cargo (sócio, plantonista, técnico ou escritório). A conta nasce bloqueada e só entra depois que a gestão aprova na tela de Usuários.Segue a escala anual do cliente: um plantão por dia, com as vagas do diurno e do noturno. A vaga (posição de 1 a 13, na ordem das linhas da escala) identifica cada lugar:
| Turno | Horário | Vagas (posição) |
|---|---|---|
| Diurno | 7h–19h | Coordenador (1), Coordenador (2), 2º a 7º (3 a 8) |
| Noturno | 19h–7h do dia seguinte | Intermediário (9), 1º (10), 1º (11), 2º (12), 3º (13) |
No sábado e no domingo (pela data do plantão: o noturno de domingo para segunda conta, o de sexta não) algumas vagas mudam:
| Turno | Vagas (posição) no fim de semana |
|---|---|
| Diurno | Coordenador (1), Coordenador (2), Manhã 7h–13h (3), 1º a 4º (4 a 7). A vaga 8 não existe. |
| Noturno | Intermediário (9), 1º a 4º (10 a 13) |
A manhã tem horário próprio: libera o médico às 13h e limita a troca por horas a 7h–13h. Os pontos de cada vaga no fim de semana estão na seção do BM Financeiro.
Regras de escala:
No modal do plantão, os participantes aparecem em lista, uma por turno, com uma linha por vaga: a vaga ("Coord.", "2º", "Interm."), a sigla, o nome e, embaixo, avisos como troca pendente, troca por horas ou férias. As ações de cada médico (trocar, remover) ficam no botão ⋯, e uma vaga livre mostra + Adicionar. Quem vira coordenador é definido mudando o médico de vaga — inclusive entre o diurno e o noturno. Mudar posições: a gestão e os médicos escalados no plantão. O modal também mostra o histórico de trocas, remoções e mudanças de vaga.
O calendário tem visões de semana (uma linha por turno) e mês, com uma cor por turno (âmbar = diurno, índigo = noturno). Cada sigla tem indicadores: anel roxo do coordenador, ponto laranja de troca pendente, "!" de afastamento aprovado, "?" de pedido pendente e a sigla de quem cobre (troca por horas ou férias). No celular, a semana mostra os dias empilhados.
No modal do plantão, o médico (sócio ou plantonista) vê Pedir esta vaga nas vagas livres, até o turno começar e se ainda não estiver naquele turno. O pedido fica pendente até a gestão aprovar ou recusar, pelo sino de notificações ou na própria vaga no modal.
A semana no formato da escala: as vagas nas linhas e os dias nas colunas, com as suas vagas destacadas. No sábado e no domingo, a célula mostra o nome da vaga quando ele muda (Manhã, 1º–4º), e a vaga 8 fica cinza. Clicar em qualquer célula de um dia abre o plantão. Para sócios, plantonistas, técnicos e escritório.
Acessado pelo menu da sigla (canto superior). Cada usuário edita nome, sigla, telefone, data de nascimento e CRM, e troca a própria senha. E-mail e funções só a gestão altera. Anestesistas também têm:
*), uma cor por médico com legenda, a lista de congressos e os dias de férias e de congresso de cada um (só períodos aprovados).Plantões que viram a noite contam pelo dia em que começam.
Registro avulso de uma anestesia, sem ligação com o plantão: data e horário (início e fim, podendo virar a noite), um ou mais anestesistas, tipo de anestesia, valor, observações e uma foto da ficha (guardada num bucket privado do Supabase Storage; o link da foto vale 1 hora).
.xlsx no modelo do faturamento (Data, Hora, Número de Atendimento … Nome Procedimento), com uma linha por anestesista. A ficha preenche só Data (dd/mm/aa), Hora (hh:mm:ss), Crm Anestesista e Anestesista; as outras colunas saem vazias, para o faturamento completar à mão antes de importar no outro sistema. A gestão exporta todas as fichas do período; o médico, só as linhas em que ele é o anestesista.Cirurgias feitas por um anestesista fora da escala (substitui o antigo Google Forms): data, anestesista (até dois por caso), se estava escalado, cirurgião, paciente (nome e CPF), cirurgia, hospital, recibo (GAMI/ADDS ou sem nota fiscal), valor e comentários. Todos os usuários logados veem; editam e excluem quem criou ou a gestão. A lista de hospitais é cadastrada pela gestão (hospital desativado some das opções, mas continua nos casos antigos). Os casos do mês também aparecem numa aba do BM Financeiro.
Tela para listar, criar e editar usuários, aprovar cadastros pendentes, ativar/desativar contas e enviar e-mail de redefinição de senha. A lista mostra o contato (e-mail e telefone) de cada usuário, e o modal do médico mostra suas férias e congressos (a gestão pode remover).
anestesita_socioPainel pessoal de cada usuário: próximo plantão (com contagem regressiva e a vaga), agenda dos próximos 7 dias, trocas aguardando o seu aceite (com Aceitar/Recusar), trocas que você pediu e o resumo do seu mês (pontos do sócio; plantões por categoria do plantonista).
Para a gestão, o dashboard também mostra a visão do hospital: quem está de plantão agora, alertas de cobertura dos próximos 7 dias por turno (turno sem médico ou diurno sem coordenador), trocas pendentes no sistema, resumo do mês comparado ao anterior, top 5 em plantões (plantonistas) e pontos (sócios) e a equipe cadastrada por role.
Resumo mensal por médico, calculado na hora a partir dos plantões (nada é gravado). O plantão não tem tipo: a separação é pelo cargo do médico.
Regras em Backend/src/services/pontuacao.js (tabela do cliente de 19/10/2026), iguais para todos os meses.
Tem seletor de mês, busca por médico, aba de casos fora e exportação para Excel. Acesso: só administradores (flag is_admin).
A API usa JWT via Supabase Auth. Após o login, inclua o token nas requisições protegidas:
Authorization: Bearer <access_token>
"Gestão" nas tabelas abaixo = escritorio, tecnico ou administrador.
| Método | Rota | Descrição |
|---|---|---|
GET | /health | Status da API e do banco (usado pelo monitor que mantém o Render acordado) |
POST | /auth/login | Login — retorna { token, refresh_token, expires_at } |
POST | /auth/refresh | Renova a sessão (refresh_token) |
POST | /auth/register | Cadastro público — a conta fica pendente até a gestão aprovar |
POST | /auth/forgot-password | Envia e-mail de redefinição de senha |
POST | /auth/reset-password | Define a nova senha (access_token + password) |
Autenticação
| Método | Rota | Descrição |
|---|---|---|
GET | /auth/me | Dados do usuário logado (id, email, nome, sigla, roles, is_admin, CRM, telefone, data de nascimento, dias disponíveis) |
PATCH | /auth/me | Edita o próprio cadastro (nome, sigla, telefone, data de nascimento, CRM, dias disponíveis) |
POST | /auth/me/senha | Troca a própria senha (senha_atual + nova_senha, mínimo 8). Encerra as sessões da conta e devolve uma nova (token, refresh_token, expires_at) |
Usuários
| Método | Rota | Descrição |
|---|---|---|
GET | /users | Busca de médicos (?search=, ?role=). Com ?data= marca quem está de férias/congresso; com ?horaInicio=&horaFim= também tira quem tem conflito de horário |
GET | /users/gestao | Lista para a gestão de usuários (gestão e sócios) |
POST | /users | Cria usuário (gestão) |
PATCH | /users/:id | Edita nome, sigla, roles, flag admin e CRM (gestão) |
PATCH | /users/:id/ativo | Ativa/desativa usuário — também aprova cadastros pendentes (gestão) |
POST | /users/:id/reset-senha | Envia e-mail de redefinição de senha (gestão) |
Plantões
| Método | Rota | Descrição |
|---|---|---|
GET | /plantoes | Lista plantões (?inicio=, ?fim=) |
GET | /plantoes/:id | Detalhe de um plantão |
POST | /plantoes | Cria o plantão do dia (titulo, data, fila: [{ usuario_id, posicao }]); 409 se o dia já tem plantão (gestão) |
PATCH | /plantoes/:id | Edita título, descrição e dia (gestão) |
POST | /plantoes/:id/usuarios | Preenche vagas vazias (fila: [{ usuario_id, posicao }]) (gestão) |
DELETE | /plantoes/:id/vagas/:posicao | Esvazia a vaga (registra no histórico e cancela a troca pendente dela) (gestão) |
PATCH | /plantoes/:id/posicao | Move o médico da vaga posicao para nova_posicao (troca com quem estiver nela); define o coordenador (gestão e médicos) |
GET | /plantoes/:id/remocoes | Histórico de remoções do plantão |
GET | /plantoes/:id/mudancas-posicao | Histórico de mudanças de vaga |
GET | /plantoes/:id/trocas | Histórico de trocas do plantão |
POST | /plantoes/:id/trocas | Solicita troca da vaga (posicao, usuario_entrada e, na troca por horas, hora_inicio/hora_fim) |
PATCH | /plantoes/:id/trocas/:trocaId/cancelar | Cancela um pedido pendente (quem pediu ou a gestão) ou desfaz uma troca por horas aceita (gestão) |
GET | /plantoes/:id/pedidos-vaga | Pedidos de vaga livre do plantão (todos os status) |
POST |
Férias e congressos
| Método | Rota | Descrição |
|---|---|---|
GET | /afastamentos | Todos os médicos (gestão). Sem período, os atuais e futuros; ?inicio=&fim= para outro intervalo; ?tipo= e ?status= (padrão: aprovados e pendentes) |
GET | /afastamentos/me | Períodos atuais e futuros do usuário logado (inclui os recusados) |
POST | /afastamentos | Pede férias ou congresso (só anestesistas) — fica pendente até a gestão aprovar |
DELETE | /afastamentos/:id | Remove um período (o próprio médico, enquanto não for aprovado; a gestão, qualquer um) |
GET | /afastamentos/pendentes | Pedidos aguardando aprovação (gestão) |
POST | /afastamentos/usuario/:usuarioId | A gestão cadastra férias ou congresso de um médico — já aprovado, e pode ser no passado |
GET | /afastamentos/usuario/:usuarioId | Períodos atuais e futuros de um médico (gestão) |
PATCH | /afastamentos/:id/aprovar | Aprova o pedido (gestão) |
PATCH | /afastamentos/:id/recusar | Recusa o pedido (gestão) |
PATCH | /afastamentos/:id/tipo-congresso | Define o tipo do congresso (palestrante/congressista): o próprio médico, se estiver sem tipo ou pendente; a gestão, sempre |
Fichas anestésicas
| Método | Rota | Descrição |
|---|---|---|
GET | /fichas | Lista as fichas (gestão: todas; médico: as dele) |
GET | /fichas/exportar?inicio=&fim= | Linhas da planilha do faturamento no período (até um ano), uma por anestesista |
GET | /fichas/:id | Detalhe, com o link temporário da foto (404 para quem não pode ver a ficha) |
POST | /fichas | Cria a ficha (foto em fotos_novas, como data URL) (médicos e gestão) |
PATCH | /fichas/:id | Edita (quem criou ou a gestão) |
DELETE | /fichas/:id | Exclui, com a foto (quem criou ou a gestão) |
Casos fora e hospitais
| Método | Rota | Descrição |
|---|---|---|
GET | /casos-fora | Lista os casos fora |
POST | /casos-fora | Registra um caso (médicos e gestão) |
PATCH | /casos-fora/:id | Edita (quem criou ou a gestão) |
DELETE | /casos-fora/:id | Exclui (quem criou ou a gestão) |
GET | /hospitais | Lista os hospitais |
POST | /hospitais | Cadastra um hospital (gestão) |
PATCH | /hospitais/:id | Renomeia ou ativa/desativa (gestão) |
Dashboard
| Método | Rota | Descrição |
|---|---|---|
GET | /dashboard | Próximo plantão, agenda de 7 dias, trocas pendentes e resumo do mês do usuário logado |
GET | /dashboard/gestao | Visão do hospital (gestão): plantões em andamento, alertas de cobertura, trocas pendentes, resumo/top 5 do mês e equipe |
BM Financeiro
| Método | Rota | Descrição |
|---|---|---|
GET | /bm-financeiro?mes=AAAA-MM | Plantões por categoria por plantonista, pontos por sócio e os casos fora do mês (administradores e técnicos) |
POST /auth/register
{
"email": "[email protected]",
"password": "senha123",
"nome": "Dr. Fulano",
"sigla": "FU",
"roles": ["anestesita_plantonista"],
"crm": "123456",
"crm_uf": "SP"
}
crmecrm_ufsão obrigatórios só para anestesistas. A conta fica pendente até a gestão aprovar.
Resposta:
{
"id": "…",
"pendente": true
}
POST /auth/login
{
"email": "[email protected]",
"password": "senha123"
}
Resposta:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "…",
"expires_at": 1760000000
}
/plantoes/:id/pedidos-vaga |
Pede uma vaga livre (posicao) (médicos) |
GET | /plantoes/pedidos-vaga/pendentes | Pedidos de vaga aguardando aprovação, de todos os plantões (gestão) |
PATCH | /plantoes/pedidos-vaga/:pedidoId/aprovar | Aprova: o médico entra na vaga e os outros pedidos dela são recusados (gestão) |
PATCH | /plantoes/pedidos-vaga/:pedidoId/recusar | Recusa o pedido (gestão) |
PATCH | /plantoes/pedidos-vaga/:pedidoId/cancelar | Cancela um pedido pendente (quem pediu ou a gestão) |
GET | /plantoes/trocas/pendentes | Trocas aguardando o aceite do usuário logado |
PATCH | /plantoes/trocas/:trocaId/aceitar | Aceita a troca |
PATCH | /plantoes/trocas/:trocaId/recusar | Recusa a troca |