Estratégia — Professor vinculado à arena, horários de turma, convite e pedido de vaga
Documentação KOLOS

Estratégia — Professor vinculado à arena, horários de turma, convite e pedido de vaga

Versão: 1.1
Contexto: módulo Academy existente (AcademyClass com venue_id, coach_user_id, schedule_label; roster; presenças; arquivamento).

Implementação (fases 1–5 do plano): ver código e 2026-04-10_academy-venue-coach-schedule-enrollment-invites; fluxo de uso: academy-enrollment-user-flow.md.

1) Objetivo de produto

  1. Professor obrigatoriamente ligado a uma arena — só opera turmas em arenas onde está autorizado.
  2. Turmas com dias e horários estruturados — não só texto livre; suporta listagens, vagas e lógica futura (calendário, choques).
  3. Professor “chama” aluno — convite explícito do professor para entrar na turma (fluxo iniciado pelo coach).
  4. Aluno vê vagas e pede entrada — descoberta de turmas elegíveis + pedido pendente + aceitação (professor e/ou arena).

2) Estado atual (baseline)

Já existe Lacuna para a estratégia
Turma com venue_id + coach_user_id Vínculo coach ↔ venue não é entidade própria; coach só aparece por turma
schedule_label (string) Não há recorrência (dias/horas) queryável
Dono cria turma; coach gere roster; add por email/user Não há capacidade máxima, vagas, nem pedido de entrada do aluno
E-mail em fila em nova inscrição Extender para convites e respostas a pedidos, se desejado

3) Modelo de dados proposto

3.1 Vínculo professor ↔ arena

Tabela sugerida: venue_coach (ou venue_staff se no futuro incluir outros papéis).

  • venue_id, user_id (coach), timestamps
  • Opcional: status (active, suspended), invited_by_user_id, notes
  • Regra: qualquer turma criada/gestionada por esse coach deve ter venue_id presente neste vínculo (ou o coach é super_admin com exceção documentada).

Quem atribui: dono da arena (e/ou super_admin). O professor não auto-associa sem aprovação (evita abuso), salvo política de negócio explícita.

3.2 Horários da turma (dias + horas)

Duas abordagens; recomendação híbrida:

A — Recorrência normalizada (recomendada)
Tabela academy_class_schedule_slots:

  • academy_class_id
  • weekday (0–6 ou ISO)
  • starts_at / ends_at como time (ou minutos desde meia-noite no TZ da venue)
  • Opcional: effective_from / effective_until para férias

B — Label humano
Manter schedule_label como denormalizado gerado para UI (“Seg e Qua, 19h–20h30”) ou preenchido pelo coach.

Sessões pontuais (AcademyClassSession) continuam alinhadas a datas concretas; os slots definem a “oferta” semanal.

3.3 Capacidade e vagas

Em academy_classes (ou tabela de configuração 1:1):

  • max_members (nullable = ilimitado, se produto permitir)
  • Vagas disponíveis = max_members - count(membros ativos) (computed; não persistir salvo cache).

3.4 Pedido de entrada (aluno → turma)

Tabela: academy_class_enrollment_requests

  • academy_class_id, user_id (player), status (pending, approved, rejected, cancelled)
  • message opcional (aluno), resolved_by_user_id, resolved_at
  • Unique: (academy_class_id, user_id) onde status = pending (ou histórico com estados finais)

Transição:

  • Aluno cria pending se houver vaga ou política “lista de espera” (definir produto).
  • Professor (ou dono, conforme matriz) approve → executa a mesma lógica que AddAcademyClassMemberAction (transação + e-mail se novo membro).
  • reject / aluno cancel sem efeito no roster.

3.5 “Chamar aluno” (professor → aluno)

Interpretação recomendada no MVP: convite direto (equivalente a pré-aprovacao).

Opção 1 — Reutilizar pedido invertido
Tabela academy_class_invitations:

  • academy_class_id, invitee_user_id, invited_by_user_id, status (pending, accepted, declined, expired)
  • Token opcional para deep link em e-mail.

Opção 2 — Só notificação + CTA
Notificação/in-app “Foste convidado” que chama acceptAddAcademyClassMemberAction.

“Chamar” em tempo real (ex.: aula já a começar): pode ser evento Reverb + notificação, fora do MVP de dados acima, ou fase posterior.

4) Regras de autorização (ACL)

Ação Quem
Gerir vínculos venue_coach venue_owner da arena, super_admin
Criar turma na arena Manter: dono cria e escolhe coach vinculado à mesma arena; ou permitir coach criar se venue_coach ativo e permissão nova academy.class.create_as_coach
Editar slots / capacidade Coach atribuído à turma + policy existente; dono pode override conforme matriz
Listar turmas “abertas a pedidos” Player autenticado; escopo: arenas públicas / coaches vinculados / só turmas com accepts_enrollment_requests = true
Pedir entrada role:player; não ser já membro; respeitar vaga ou lista de espera
Aprovar/rejeitar pedidos Coach da turma e/ou venue_owner (cravar uma fonte de verdade)
Convidar aluno (“chamar”) Coach da turma (e mesmas guards de arquivo/capacidade)

Documentar decisão única: aprovação de pedido é só coach, só dono, ou ambos (either).

5) Fluxos UX (resumo)

flowchart LR
  subgraph arena
    VO[Venue owner]
    VC[venue_coach]
    VO --> VC
  end
  subgraph turma
    C[Coach]
    T[AcademyClass + slots]
    VC --> C
    C --> T
  end
  P[Player]
  T -->|vagas| P
  P -->|pedido| ER[enrollment_request]
  ER -->|aprovar| C
  C -->|convite| INV[invitation]
  INV -->|aceitar| P
  1. Onboarding coach: dono adiciona coach à arena → coach vê apenas as suas arenas/turmas.
  2. Criar turma: escolher arena (validada), nome, slots, max_members, flag accepts_enrollment_requests.
  3. Aluno: ecrã “Turmas / Procurar” → filtra por arena, horário, vagas → “Pedir entrada”.
  4. Professor: fila de pedidos + botão “Convidar aluno” (busca de jogadores já na plataforma ou e-mail como hoje).

6) Fases de implementação sugeridas

Fase Entrega Risco / nota
1 venue_coach + UI dono “Professores da arena” + validação em CreateAcademyClassAction / policies Base para tudo o resto
2 academy_class_schedule_slots + UI criar/editar horários; manter schedule_label sync Migração + seeds
3 max_members + cálculo de vagas + listagem pública/restrita de turmas Performance: índices em membros
4 enrollment_requests + UI aluno + UI coach aprovar/rejeitar Integrar com AddAcademyClassMemberAction
5 Convites “chamar aluno” + e-mail em fila + opcional Reverb Reutilizar padrão de mail já existente
6 Lista de espera, convites por token anónimo, calendário Pós-MVP

7) Testes e qualidade

  • Pest: policy allow/deny para cada transição de enrollment_requests e invitations.
  • Casos negativos: coach não vinculado tenta criar turma; aluno pede turma cheia; turma arquivada.
  • docs/changes/... por fase; atualizar matriz ACL e kolos_registro-de-rotas.md.

8) Decisões em aberto (produto)

  • Turma sempre criada pelo dono vs coach pode criar na sua arena.
  • Pedido com turma cheia: bloquear ou lista de espera ordenada.
  • “Chamar” inclui aluno fora da app (só e-mail) no MVP ou só utilizadores registados.

Documento de estratégia; implementação incremental no módulo Kolos\Modules\Academy e Venue.