Fernando Pirichowski Aguiar d082028c5a feat: Sistema completo de agendamento médico com correções RLS e melhorias UI
- Fix: Avatar upload usando Supabase Client com RLS policies
- Fix: Profile update usando Supabase Client
- Fix: Timezone handling em datas de consultas
- Fix: Filtros de consultas passadas/futuras
- Fix: Appointment cancellation com Supabase Client
- Fix: Navegação após booking de consulta
- Fix: Report service usando Supabase Client
- Fix: Campo created_by em relatórios
- Fix: URL pública de avatares no Storage
- Fix: Modal de criação de usuário com scroll
- Feat: Sistema completo de gestão de consultas
- Feat: Painéis para paciente, médico, secretária e admin
- Feat: Upload de avatares
- Feat: Sistema de relatórios médicos
- Feat: Gestão de disponibilidade de médicos
2025-11-05 23:38:31 -03:00

MediConnect - Sistema de Agendamento Médico

Sistema completo de gestão médica com agendamento inteligente, prontuários eletrônicos e gerenciamento de pacientes.

Stack: React + TypeScript + Vite + TailwindCSS + Supabase
Deploy: Cloudflare Pages


🚀 Acesso ao Sistema

Credenciais de Teste

Médico:

Paciente:

Secretária:


🏗️ Arquitetura

Frontend (React/Vite)
    ↓
Supabase Backend
    ├── Auth (JWT + Magic Link)
    ├── PostgreSQL (PostgREST)
    ├── Edge Functions (Slots, Criação de Usuários)
    └── Storage (Avatares, Documentos)
    ↓
Cloudflare Pages (Deploy)

<EFBFBD> Instalação e Execução

Pré-requisitos

  • Node.js 18+
  • pnpm (recomendado) ou npm

Instalação

# Instalar dependências
pnpm install

# Iniciar desenvolvimento
pnpm dev

# Acessar em http://localhost:5173

Build e Deploy

# Build de produção
pnpm build

# Deploy para Cloudflare Pages
pnpm wrangler pages deploy dist --project-name=mediconnect --branch=production

Funcionalidades Principais

🏥 Para Médicos

  • Agenda personalizada com disponibilidade configurável
  • 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
  • Teleconsulta e presencial
  • Chatbot AI para suporte

👥 Para Pacientes

  • Agendamento inteligente com slots disponíveis em tempo real
  • Histórico completo de consultas
  • 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
  • Chatbot AI para dúvidas e suporte

🏢 Para Secretárias

  • 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
  • Boas-vindas personalizadas com nome real

🔐 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 (médico/paciente/secretária)

🔧 Tecnologias

Frontend

  • React 18 - Interface moderna e reativa
  • TypeScript - Tipagem estática
  • Vite - Build ultra-rápido
  • TailwindCSS - Estilização utilitária
  • React Router - Navegação SPA
  • Axios - Cliente HTTP
  • date-fns - Manipulação de datas
  • jsPDF - Geração de PDFs
  • Lucide Icons - Ícones modernos

Backend (Supabase)

  • PostgreSQL - Banco de dados relacional
  • PostgREST - API REST automática
  • Edge Functions - Funções serverless (Deno)
  • Storage - Armazenamento de arquivos
  • Auth - Autenticação e autorização

Deploy

  • Cloudflare Pages - Hospedagem global com CDN

📁 Estrutura do Projeto

MEDICONNECT 2/
├── src/
│   ├── components/         # Componentes React
│   │   ├── auth/          # Login, cadastro, recuperação
│   │   ├── secretaria/    # Painéis da secretária
│   │   ├── agenda/        # Sistema de agendamento
│   │   ├── consultas/     # Gerenciamento de consultas
│   │   └── ui/            # Componentes reutilizáveis
│   ├── pages/             # Páginas da aplicação
│   │   ├── Home.tsx
│   │   ├── PainelMedico.tsx
│   │   ├── PainelSecretaria.tsx
│   │   └── AgendamentoPaciente.tsx
│   ├── services/          # Camada de API
│   │   ├── api/           # Cliente HTTP
│   │   ├── auth/          # Autenticação
│   │   ├── appointments/  # Agendamentos
│   │   ├── doctors/       # Médicos
│   │   ├── patients/      # Pacientes
│   │   ├── availability/  # Disponibilidade
│   │   └── avatars/       # Avatares
│   ├── context/           # Context API
│   ├── hooks/             # Custom hooks
│   ├── types/             # TypeScript types
│   └── utils/             # Funções utilitárias
├── public/                # Arquivos estáticos
├── scripts/               # Scripts de utilidade
└── dist/                  # Build de produção

🔑 APIs e Serviços

Principais Endpoints

Agendamentos

// Buscar slots disponíveis (Edge Function)
POST /functions/v1/get-available-slots
{
  "doctor_id": "uuid",
  "date": "2025-10-30"
}

// Criar agendamento
POST /rest/v1/appointments
{
  "doctor_id": "uuid",
  "patient_id": "uuid",
  "scheduled_at": "2025-10-30T09:00:00Z",
  "duration_minutes": 30,
  "appointment_type": "presencial"
}

Disponibilidade

// Listar disponibilidade do médico
GET /rest/v1/doctor_availability?doctor_id=eq.{uuid}

// Criar horário de atendimento
POST /rest/v1/doctor_availability

// Atualizar disponibilidade
PATCH /rest/v1/doctor_availability?id=eq.{uuid}

Usuários

// Criar médico (Edge Function com validações)
POST /functions/v1/create-doctor

// Criar paciente
POST /rest/v1/patients

// Listar médicos
GET /rest/v1/doctors?select=*

// Atualizar perfil
PATCH /rest/v1/doctors?id=eq.{uuid}

Documentação completa: Ver AGENDAMENTO-SLOTS-API.md


🔒 Autenticação e Permissões

Sistema de Autenticação

  • JWT Tokens com refresh automático
  • Magic Link - Login sem senha via email
  • Recuperação de senha com email
  • Interceptors adicionam token automaticamente
  • Renovação automática quando token expira

Roles e Permissões (RLS)

Admin/Gestor:

  • Acesso completo a todos os recursos
  • Criar/editar/deletar usuários
  • Visualizar todos os dados

Médicos:

  • Gerenciar agenda e disponibilidade
  • Visualizar todos os pacientes
  • Criar e editar prontuários
  • Ver apenas próprios agendamentos

Pacientes:

  • Agendar consultas
  • Visualizar histórico próprio
  • Editar perfil pessoal
  • Download de relatórios médicos

Secretárias:

  • Cadastrar médicos e pacientes
  • Gerenciar agendamentos
  • Configurar agendas médicas
  • Busca e filtros avançados

🎨 Recursos de Acessibilidade

  • Modo de alto contraste
  • Ajuste de tamanho de fonte
  • Navegação por teclado
  • Leitores de tela compatíveis
  • Menu de acessibilidade flutuante

📊 Dashboards e Relatórios

Médico

  • Total de pacientes atendidos
  • Consultas do dia/semana/mês
  • Próximas consultas
  • Histórico de atendimentos

Paciente

  • Histórico de consultas
  • Relatórios médicos com download PDF
  • Próximos agendamentos
  • Acompanhamento médico

Secretária

  • Visão geral de agendamentos
  • Filtros por médico, data e status
  • Busca de pacientes e médicos
  • Estatísticas gerais

🚀 Melhorias Recentes (Novembro 2025)

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.



Ú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)

// 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)

// 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)

// 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)

// 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)

// 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)

// 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

// 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

# 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


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

// 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

// 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

- id (uuid, PK)
- email (text, unique)
- full_name (text)
- phone (text)
- created_at (timestamp)
- updated_at (timestamp)

patients

- 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

- id (uuid, PK, FK -> profiles)
- email (text, unique)
- full_name (text)
- specialty (text)
- crm (text, unique)
- phone (text)
- created_at (timestamp)

appointments

- 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

- 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

# 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

# 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

// 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


Desenvolvido com ❤️ pela Squad 18

Description
No description provided
Readme 5.8 GiB
Languages
TypeScript 95.9%
PowerShell 1.8%
CSS 1.2%
JavaScript 0.8%
Python 0.2%