Inscrição Rápida — Processo Seletivo Fora do Calendário #59
FernandoAlmeidaPinto
started this conversation in
Features
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
1. Contexto e motivação
Hoje o fluxo de inscrição em um Processo Seletivo (
InscriptionCourse) pressupõe um calendário tradicional de cursinho popular: o cursinho abre o PS, divulga, alunos se inscrevem via formulário público, passam por Em Análise → Convocado para matrícula → Declarou Interesse (com upload de docs/foto/survey) → Matriculado. O modelo cobre bem o caso "alto volume, seleção competitiva, recursos limitados".O problema: alguns cursinhos fecham parceria com a Você na Facul fora desse calendário, já com a turma definida (ex.: 30 alunos que já frequentam o cursinho presencialmente). Para esses cursinhos, todo o funil de seleção é desnecessário — eles só precisam dar acesso à plataforma para que os alunos comecem a usar simulados, redações, chat, presença e demais funcionalidades.
Pedir que cada um desses alunos faça o cadastro público completo gera atrito alto (criação de conta, confirmação de email, formulário longo, espera por análise, declaração de interesse) para um cenário em que a "seleção" já aconteceu fora do sistema. O resultado prático é que esses cursinhos ou não usam a plataforma, ou o coordenador precisa fazer suporte 1-a-1 com cada aluno.
Hipótese de valor: se permitirmos que o cursinho insira em lote os alunos já selecionados e gere acesso direto, reduzimos o tempo de "parceria fechada → turma usando a plataforma" de semanas para o tempo de uma planilha + clique. Isso aumenta o engajamento desses parceiros excepcionais sem comprometer o rigor do funil regular.
2. Objetivos e não-objetivos
Objetivos (v1):
User+StudentCourseem um único passo.InscriptionCoursee o enumStatusApplicationexistentes — sem criar um caminho paralelo de dados.Não-objetivos (v1):
3. Personas e jornadas
Admin da plataforma. Recebe contato de um cursinho parceiro que entrou fora do calendário. Avalia o caso, marca o cursinho como "autorizado a usar inscrição rápida". Pode revogar a qualquer momento.
Coordenador do cursinho (com permissão
gerenciarProcessoSeletivo). Cria um PS marcado como "inscrição rápida", sobe a planilha de alunos, acompanha o resultado do import. Ao final do upload, tem na tela do PS todos os alunos do lote já comoDeclarou Interesse, restando apenas matriculá-los manualmente em turma (fluxo atual: criarCoursePeriod, criarClass, atribuir o aluno).Aluno convidado. Recebe um email com link de primeiro acesso, define senha, preenche um onboarding mínimo e envia a foto da carteirinha. O onboarding não muda seu status — ele já entrou no sistema como
Declarou Interesseno momento do import. Se o coordenador ainda não tiver feito a matrícula em turma, cai no dashboard comoDeclarou Interesse; se o coordenador já tiver matriculado antes do onboarding, cai já comoMatriculado.4. Fluxo proposto (alto nível)
4.1. Admin habilita o cursinho
Tela de admin já existente para gerenciar
PartnerPrepCourseganha um toggle "Permitir inscrição rápida". Sem isso, a opção não aparece na criação do PS do cursinho.4.2. Cursinho cria PS com inscrição rápida
No modal de criação de PS (
InscriptionInfoCreateEditModal.tsx), aparece o checkbox "Habilitar inscrição rápida" — apenas se o cursinho estiver autorizado. Quando marcado:requestDocumentsfica desabilitado (não faz sentido pedir documentos em fluxo simplificado — ver §6).hasActiveFormcontinua valendo apenas para PS regulares; para PS rápido, não criamosFormFullnovcnafacul-form(ou criamos um stub vazio — decisão a tomar, ver §10).4.3. Cursinho sobe planilha
Após criar o PS rápido, abre-se a tela "Cadastrar estudantes em lote" com:
.xlsxcom colunas obrigatórias e exemplo.Colunas mínimas sugeridas:
Campos como CPF, telefone, endereço, gênero, dados socioeconômicos, dados do responsável legal migram para o onboarding (§4.5) — pedir tudo na planilha torna o fluxo tão pesado quanto o tradicional.
4.4. Sistema processa o lote
Para cada linha válida:
Usercom senha temporária aleatória (gerada server-side, hash com bcrypt) eemailConfirmSended = null(já considerado validado — o convite explícito do cursinho substitui o duplo opt-in).StudentCourseligado aoInscriptionCoursee aoPartnerPrepCourse, comapplicationStatus = StatusApplication.DeclaredInterest(estado terminal pré-matrícula — ver §6.a) e flag internaquickInscription = true.CreateFlow.QUICK_INSCRIPTION_ONBOARDING, novo) com expiração mais longa (sugestão: 14 dias) e envia email transacional novosendQuickInscriptionInvite.LogStudentcom descrição"Inscrição rápida criada por <coordenador> via planilha"eLogPartnerresumindo o lote.O processamento deve ser assíncrono em background (job/queue) acima de N linhas (sugestão: 25), com status visível no frontend ("Processando 87 de 120..."). Abaixo desse limite, processa síncrono e devolve o resultado direto.
Resultado esperado do lote: o coordenador volta para a tela do PS e vê todos os alunos do lote já listados com status
Declarou Interesse, restando apenas a matrícula manual de cada um (via fluxo de turma existente, §4.6).Tela final do upload: tabela
email | status (criado / erro) | mensagem+ botão "Baixar relatório (.xlsx)".4.5. Aluno faz primeiro acesso
Aluno recebe email com link
/{FRONT_URL}/inscricao-rapida/onboarding?token=…. Ao abrir:StudentCourse)User)LegalGuardiancompleto (regra atual doStudentCourseService.create)PATCH /student-course/declaration-photoe oBUCKET_PROFILEjá existentes; gravastudent.photoephotoDone = true).Importante: o onboarding NÃO altera
applicationStatus. O estudante já entrou no sistema comoDeclaredInterestno momento do import (§4.4). O onboarding só completa os dados pessoais e a foto da carteirinha; a transição paraEnrolledé exclusivamente decisão do coordenador via §4.6.Consequência prática:
Declarou Interessee mensagem "Aguarde o cursinho confirmar sua turma".Matriculado, comcod_enrolledválido.Antes de concluir o onboarding (incluindo a foto), o aluno tem acesso restrito (sugestão: apenas tela de onboarding + logout). Sem isso, abrimos um buraco onde alunos meio-cadastrados navegam pela plataforma sem CPF/foto.
4.6. Coordenador matrícula em turma (fluxo atual, sem mudanças)
Matrícula segue exatamente o caminho que já existe:
CoursePeriod(período letivo).Class(turmas) sob aquele período.DeclaredInterest, atribui a umaClasse chamaconfirmEnrolled→ status viraEnrolled, geracod_enrolled.Essa parte não é automatizada pela inscrição rápida — é decisão consciente, porque criar período letivo e turmas é trabalho que o coordenador precisa fazer com calma e que não tem como ser inferido a partir da planilha.
5. Mudanças de modelo
PartnerPrepCourse(api-vcnafacul):quick_inscription_enabled: boolean(defaultfalse). Setada apenas pelo admin.InscriptionCourse:quick_inscription: boolean(defaultfalse). Imutável após criação.StudentCourse:quick_inscription: boolean(defaultfalse). Útil para relatórios, filtros e auditoria.StatusApplication: sem novos valores. ReaproveitaDeclaredInterest→Enrolled. Pular paraEnrolleddireto também é viável (ver §6), mas adicionar um valorQuickInscriptionaumenta a complexidade do funil sem benefício claro.CreateFlow: adicionarQUICK_INSCRIPTION_ONBOARDING.Permissões (
Role): nenhuma nova permission. A permission para criar PS rápido continua sendogerenciarProcessoSeletivo; a permission para habilitar o cursinho como autorizado évalidarCursinhoougerenciarPermissoesCursinho(decidir qual encaixa melhor — recomendação:validarCursinho, pois é uma decisão de curadoria sobre o parceiro).6. Decisões de design com trade-offs
Pontos onde o card original omite ou ambiguamente decide. Cada um precisa ser fechado antes do dev começar.
(a) Status inicial do aluno:
DeclaredInterest. Decidido. O aluno entra direto comoDeclaredInterestno momento do import. Ir paraEnrolledexige criação de período letivo + turma + atribuição, que é decisão do coordenador e não é automatizada (§4.6). O onboarding feito pelo aluno (§4.5) não altera o status — apenas completa dados pessoais e foto da carteirinha.(b) Senha temporária vs link mágico. Card original menciona "email com email e senha para login". Hoje o projeto usa link de confirmação com JWT 2h, não senha temporária. Trade-off:
sendCreateUseratual; mais seguro; usuário define senha na primeira sessão.Recomendação: link mágico com JWT longo (14 dias) que cai em tela "definir senha". Mantém o padrão de segurança do projeto.
(c) Documentos. PS regular pode exigir documentos (
requestDocumentsboolean). Para PS rápido, isso é incoerente com o objetivo. Recomendação: forçarrequestDocuments = falsequandoquickInscription = true.(d) Formulário vcnafacul-form. PS regular exige
hasActiveForm. PS rápido não tem formulário público. Opções:formService.createFormFullpara PS rápido. Coerente, mas pode quebrar consumers que assumemFormFullsempre presente.FormFull"vazio" (sem perguntas) só para satisfazer o invariante.Recomendação: (1) pular, e adicionar guards explícitos nos consumers (
submissionService, relatórios) que checaminscriptionCourse.quickInscriptionantes de procurar form.(e) Vagas e
expectedOpening. PS temexpectedOpening. Em PS rápido, isso é o tamanho da turma. O sistema deve impedir um lote que estoure as vagas? Recomendação: avisar mas não bloquear (cursinho ajustaexpectedOpeningou prossegue assumindo o estouro), com warning visual e log.(f) Idempotência por email. Se a planilha tem
aluno@x.come esse email já existe noUser:StudentCourseadicional).StudentCourseligado aoUserexistente, sem sobrescrever senha; enviar email "você foi inscrito em <cursinho>, clique para confirmar".Recomendação: comportamentos diferenciados acima, todos explicitados na pré-visualização.
(g) Validação síncrona vs assíncrona. Excel parsing + criação de N usuários pode estourar timeout em lotes grandes. Recomendação: fila/job acima de 25 linhas, com status polling. Reaproveita padrão de cron já presente no
InscriptionCourseService.(h) Limites. Tamanho máximo de arquivo (sugestão: 1 MB), número máximo de linhas por upload (sugestão: 500), rate limit no endpoint (
Throttlejá em uso no projeto).7. Regras de negócio explícitas
PartnerPrepCourse.quickInscriptionEnabled = trueveem a opção de criar PS rápido.Permissions.gerenciarProcessoSeletivopodem subir planilhas./inscription-course/to-inscription/:idretorna 404 ou redireciona — não há formulário público.UserService.createUser).getSubscribers,getEnrolled, dashboards, e relatórios — com badge "Inscrição rápida" para distinção visual.8. Segurança e LGPD
LogStudentcom IP e user-agent (já existe esse padrão emdeclaredInterest).expiresInlonga mas pode ser revogado no botão "Reenviar convite".9. Telemetria e observabilidade
discordWebhook.sendMessage, já em uso noInscriptionCourseService).LogStudenteLogPartner.10. Critérios de aceite
Como admin:
PartnerPrepCourse.Como coordenador de cursinho autorizado:
requestDocumentsfica desabilitado..xlsxdo lote.Declarou Interesse— restando apenas a matrícula manual em turma.CoursePeriod, criarClass, atribuir aluno viaconfirmEnrolled).Como aluno convidado:
Declarou Interesse, com aviso "Aguarde o cursinho confirmar sua turma".Matriculado, comcod_enrolledválido.Matriculado.Como sistema:
FormFullno vcnafacul-form (ou cria stub conforme decisão final).expectedOpening< linhas válidas exibe warning mas permite prosseguir.11. Riscos e mitigações
quickInscriptionEnabledé opt-in manual), revogação imediata, monitoramento de taxa de descadastro e reclamações.StudentCourse.12. Fora de escopo (v1) — possível v2
Classespecífica.13. Sugestão de quebra em sub-tarefas
PartnerPrepCourse.quickInscriptionEnabled,InscriptionCourse.quickInscription,StudentCourse.quickInscription. Endpoint admin para alternar o toggle.CreateInscriptionCourseInputeInscriptionCourseService.createpara aceitarquickInscription; validar autorização; pular criação deFormFull.POST /inscription-course/:id/quick-inscription/import(multipart), com parser viaexceljs, validação linha-a-linha, job assíncrono, geração de JWT longo, email transacionalsendQuickInscriptionInvite, criação deUser+StudentCourse+ logs.POST /quick-inscription/complete-onboarding(recebe CPF, contatos, endereço, legal guardian se menor, foto da carteirinha multipart) + endpointPOST /quick-inscription/:studentId/resend-invite. Não alteraapplicationStatus— apenas atualiza dados pessoais e marcaphotoDone = true. ReaproveitaBUCKET_PROFILEe a mecânica desubmitPhotoexistentes..xlsx, drag & drop, pré-visualização, polling de status, relatório final./inscricao-rapida/onboarding, formulário, guard de rota até completar.quickInscriptionEnabledno detalhe dePartnerPrepCourse.CLAUDE.md, registrar webhook de erro, dashboard de uso.14. Perguntas em aberto
Lista para discussão antes do refinamento técnico (não decididas aqui de propósito):
gerenciarPermissoesCursinhoouvalidarCursinhopara autorizarquickInscriptionEnabled?StudentCourse?All reactions