11 KiB
PROMPT COMPLETO — Portal do Aluno (EduManager Student Portal)
Copie este prompt inteiro e cole em uma nova conversa para criar o projeto do zero.
Objetivo
Atue como um Desenvolvedor Sênior Full-Stack. Crie um projeto NOVO e SEPARADO chamado "Portal do Aluno" — uma aplicação web onde os alunos matriculados no sistema EduManager podem fazer login e visualizar seus dados acadêmicos e financeiros.
Este portal NÃO faz parte do código do EduManager. É um projeto independente que CONSOME os mesmos dados do EduManager, lendo diretamente do mesmo banco de dados Supabase.
Stack Tecnológica Obrigatória
- Frontend: React + TypeScript + Vite
- Backend: Node.js + Express (server.js)
- Banco de Dados: Supabase (PostgreSQL) — o mesmo banco do EduManager, apenas leitura
- Estilização: TailwindCSS
- Deploy: Docker (Dockerfile multi-stage) para rodar no Portainer via Docker Swarm
- Porta: 3001 (para não conflitar com o EduManager que roda na 3000)
Arquitetura do Banco de Dados (Supabase — SOMENTE LEITURA)
O EduManager armazena TODOS os dados da escola em uma única tabela chamada school_data com uma única linha (id = 1). O campo data é um JSON gigante com a seguinte estrutura:
// Tabela: school_data (id: 1, coluna "data" tipo JSONB)
{
students: Student[], // Lista de todos os alunos
classes: Class[], // Lista de turmas
courses: Course[], // Lista de cursos
payments: Payment[], // Lista de todos os pagamentos/cobranças
contracts: Contract[], // Contratos dos alunos
certificates: Certificate[], // Certificados emitidos
attendance: Attendance[], // Registros de presença
subjects: Subject[], // Disciplinas
grades: Grade[], // Notas dos alunos
profile: SchoolProfile, // Dados da escola (nome, logo, etc.)
logo?: string, // Logo da escola em base64
}
Interface Student (campos relevantes para login):
interface Student {
id: string; // UUID
name: string; // Nome completo
email: string;
phone: string;
birthDate: string; // YYYY-MM-DD
cpf: string; // Formato: 000.000.000-00
rg?: string;
classId: string; // ID da turma vinculada
status: 'active' | 'inactive' | 'cancelled';
registrationDate: string;
photo?: string; // Base64 da foto do aluno
addressZip?: string;
addressStreet?: string;
addressNumber?: string;
addressNeighborhood?: string;
addressCity?: string;
addressState?: string;
enrollmentNumber?: string; // ← LOGIN (formato: MAT-202600001)
portalPassword?: string; // ← SENHA (padrão: 6 primeiros dígitos do CPF)
discount?: number;
}
Interface Payment (cobranças do aluno):
interface Payment {
id: string;
studentId: string; // Relaciona com Student.id
amount: number;
discount?: number;
dueDate: string; // YYYY-MM-DD
status: 'pending' | 'paid' | 'overdue';
paidDate?: string;
type: 'monthly' | 'registration' | 'other';
installmentNumber?: number;
totalInstallments?: number;
description?: string;
asaasPaymentId?: string; // ID no Asaas
asaasPaymentUrl?: string; // URL do boleto no Asaas
}
Outras Tabelas Auxiliares no Supabase:
Tabela alunos_cobrancas (cobranças sincronizadas com Asaas):
-- Colunas principais:
id (uuid), aluno_id (uuid), asaas_customer_id (text), asaas_payment_id (text),
asaas_installment_id (text), valor (numeric), vencimento (date),
link_boleto (text), link_carne (text), status (text), created_at (timestamptz)
Interface Grade (notas):
interface Grade {
id: string;
studentId: string;
subjectId: string;
value: number;
period: string; // Ex: "1º Bimestre"
}
Interface Attendance (frequência):
interface Attendance {
id: string;
studentId: string;
classId: string;
date: string; // ISO String
verified: boolean;
type?: 'presence' | 'absence';
justification?: string;
}
Interface Class e Course:
interface Class {
id: string;
name: string;
courseId: string;
teacher: string;
schedule: string;
}
interface Course {
id: string;
name: string;
duration: string;
monthlyFee: number;
}
Interface Contract:
interface Contract {
id: string;
studentId: string;
title: string;
content: string; // HTML do contrato
createdAt: string;
}
Interface Certificate:
interface Certificate {
id: string;
studentId: string;
description?: string;
issueDate: string;
}
Interface SchoolProfile:
interface SchoolProfile {
id: string;
name: string;
address: string;
city: string;
state: string;
cnpj: string;
phone: string;
email: string;
}
Sistema de Autenticação
Login:
- Usuário: Campo
enrollmentNumberdo aluno (ex:MAT-202600001) - Senha: Campo
portalPassworddo aluno (padrão: 6 primeiros dígitos do CPF)
Fluxo de Login no Backend:
- Receber
enrollmentNumberepasswordvia POST/api/portal/login - Consultar a tabela
school_data(id=1), pegar o JSONdata - Buscar no array
data.studentso aluno cujoenrollmentNumber= input do usuário - Verificar se
portalPassword=== senha digitada - Se válido: gerar um JWT (jsonwebtoken) com
{ studentId, enrollmentNumber, name }e retornar - Se inválido: retornar 401
Middleware de Autenticação:
- Criar middleware
authMiddlewareque valida o JWT em todas as rotas/api/portal/* - O JWT secret deve vir de
process.env.JWT_SECRET
Funcionalidades do Portal (Páginas)
1. Tela de Login
- Design moderno, escuro, com gradientes
- Logo da escola carregada dinamicamente do Supabase (campo
data.logo) - Campos: Nº de Matrícula + Senha
- Botão "Entrar"
- Mensagem de erro amigável se credenciais inválidas
2. Dashboard (Página Inicial pós-login)
- Saudação: "Olá, {nome do aluno}!"
- Foto do aluno (se tiver)
- Cards resumo:
- Turma: nome da turma e curso vinculado
- Financeiro: total de parcelas pendentes / valor total em aberto
- Frequência: porcentagem de presença
- Próximo vencimento: data e valor
3. Financeiro (Meus Boletos)
- Tabela listando TODOS os pagamentos do aluno (filtrados por
studentId) - Colunas: Descrição, Vencimento, Valor, Status (badges coloridos), Ação
- Botão "Ver Boleto" que abre o link do Asaas (
asaasPaymentUrlou buscar da tabelaalunos_cobrancas.link_boleto) - Filtros: Todos, Pendentes, Pagos, Atrasados
- Destaque visual para boletos atrasados (vermelho)
4. Notas / Boletim
- Buscar no array
data.gradestodas as notas ondestudentId= aluno logado - Buscar os nomes das disciplinas no array
data.subjects - Exibir em formato de tabela/boletim organizada por período (1º Bimestre, 2º Bimestre, etc.)
- Calcular média automaticamente
5. Frequência / Presença
- Buscar no array
data.attendanceondestudentId= aluno logado - Mostrar calendário ou lista com os dias de presença/falta
- Exibir porcentagem total de frequência
- Justificativas de falta quando houver
6. Contratos
- Listar contratos do aluno (array
data.contractsfiltrado porstudentId) - Botão para visualizar o contrato completo (renderizar HTML)
- Botão para download/impressão
7. Certificados
- Listar certificados emitidos para o aluno
- Botão para visualizar/download
8. Meus Dados
- Exibir dados pessoais do aluno (somente leitura):
- Nome, CPF, RG, Data de Nascimento, Telefone, Email
- Endereço completo
- Dados do responsável (se tiver)
- Botão "Alterar Senha" que permite trocar a
portalPassword- Para salvar a nova senha: fazer PUT no server.js que atualiza o campo
portalPassworddo aluno no JSONdata.studentsdentro daschool_data
- Para salvar a nova senha: fazer PUT no server.js que atualiza o campo
Rotas do Backend (server.js)
POST /api/portal/login → Autenticação (retorna JWT)
GET /api/portal/me → Dados do aluno logado (protegido)
GET /api/portal/financeiro → Pagamentos do aluno (protegido)
GET /api/portal/notas → Notas/boletim do aluno (protegido)
GET /api/portal/frequencia → Registros de presença (protegido)
GET /api/portal/contratos → Contratos do aluno (protegido)
GET /api/portal/certificados → Certificados do aluno (protegido)
GET /api/portal/boletos → Boletos do Asaas (tabela alunos_cobrancas) (protegido)
PUT /api/portal/alterar-senha → Alterar senha do portal (protegido)
GET /api/portal/escola → Dados da escola + logo (público, para o login)
Variáveis de Ambiente (Portainer)
PORT=3001
VITE_SUPABASE_URL=https://xxxx.supabase.co
VITE_SUPABASE_KEY=eyJhb...
JWT_SECRET=uma-chave-secreta-forte-aqui
Dockerfile (Multi-stage, igual ao EduManager)
# ---- Build Stage ----
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Production Stage ----
FROM node:22-alpine AS production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY server.js ./
COPY --from=builder /app/dist ./dist
EXPOSE 3001
CMD ["node", "server.js"]
Regras e Restrições CRÍTICAS
-
SOMENTE LEITURA no Supabase — o portal NÃO deve criar, editar ou excluir alunos, pagamentos, notas, etc. A ÚNICA exceção é a rota
PUT /api/portal/alterar-senhaque atualiza o campoportalPassworddentro do JSON. -
Nunca expor dados de outros alunos — todas as queries devem filtrar por
studentIddo JWT. -
Design Premium — Use dark mode por padrão, gradientes modernos, animações suaves, tipografia Google Fonts (Inter ou Outfit). O visual deve ser profissional e bonito.
-
Responsivo — Deve funcionar perfeitamente em celulares (a maioria dos alunos acessará pelo celular).
-
O projeto deve ser 100% funcional — sem mocks, sem placeholder. Lê dados reais do Supabase.
-
Separação total do EduManager — Este é um projeto em pasta separada, repositório separado, container Docker separado. Eles compartilham apenas o banco Supabase.
Estrutura de Pastas Esperada
portal-aluno/
├── server.js # Backend Express
├── Dockerfile
├── package.json
├── vite.config.ts
├── tsconfig.json
├── index.html
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── types.ts # Interfaces compartilhadas
│ ├── context/
│ │ └── AuthContext.tsx # Context de autenticação (JWT)
│ ├── pages/
│ │ ├── Login.tsx
│ │ ├── Dashboard.tsx
│ │ ├── Financeiro.tsx
│ │ ├── Notas.tsx
│ │ ├── Frequencia.tsx
│ │ ├── Contratos.tsx
│ │ ├── Certificados.tsx
│ │ └── MeusDados.tsx
│ ├── components/
│ │ ├── Sidebar.tsx
│ │ ├── Header.tsx
│ │ └── ProtectedRoute.tsx
│ └── styles/
│ └── index.css
Gere o projeto completo com TODOS os arquivos acima, funcional e pronto para deploy no Docker/Portainer.