Skip to content

Mural Olímpico — Liberação de Resultados

Fluxo completo de publicação, snapshots materializados e sincronização de cache.


1. Visão Geral

O Mural Olímpico permite que coordenadores publiquem resultados de olimpíadas para visualização no mural de alunos/responsáveis. O fluxo utiliza snapshots materializados para garantir performance e controle de acesso granular.

Fluxo Principal

Coordenador insere resultados → Configura nota de corte/premiação (opcional)
→ Publica via card de liberação → Snapshots gerados em `mural_dados_publicados`
→ Aluno/responsável vê no mural

2. Tabelas Envolvidas

TabelaFunção
resultados_alunoPontuações brutas por fase/nível
configuracoes_fase_nivelNota de corte e faixas de premiação (ouro/prata/bronze/menção)
mural_liberacoesFlags de publicação por fase/nível
mural_dados_publicadosSnapshots materializados para o mural
escola_olimpiadasVínculo escola↔olimpíada (status ativa)

3. Flags de Liberação (mural_liberacoes)

FlagO que controlaEfeito no mural
liberar_notasPontuação do alunoMostra nota obtida
liberar_resultadosClassificação + premiaçãoMostra posição, medalha, certificado

Exclusividade: Ativar notas desativa resultados e vice-versa. O backend detecta a mudança e aplica a exclusividade automaticamente.

Config embutida (JSONB)

  • config_notas: { exibirNotaMaxima, exibirClassificacao, exibirPorNivel, exibirPorSerie }
  • config_resultados: { exibirPontuacao, exibirClassificacao, exibirPorNivel, exibirPorSerie }

4. Snapshots Materializados

Geração

Ao publicar, o backend (mural-escola action toggle_liberacao):

  1. Busca inscrições da escola+olimpíada em chunks de 80 IDs (limite PostgREST)
  2. Para cada inscrição, resolve resultado da fase correspondente
  3. Calcula empates por nível (empate_nivel) e por série (empate_serie)
  4. Resolve pontuação máxima via fallback: resultado.pontuacao_maximafase.pontuacao_maxima_por_nivel[nivel]
  5. Upsert em mural_dados_publicados

SSOT do helper: supabase/functions/_shared/snapshot-publicados.ts (computeAndUpsertSnapshot())

Recomputação Automática

Snapshots são recomputados automaticamente em:

  • Inserção/importação de resultados (recomputeSnapshotsIfPublished)
  • Deleção de resultados (parcial ou total)
  • Atualização de prêmios ou nota de corte
  • Botão "Republicar snapshot" (🔄) no UI do coordenador

Limpeza

Quando liberar_notas=false E liberar_resultados=false, os snapshots da fase/nível são deletados (não apenas desativados).


5. "Limpar Fase" — Efeitos Cascata

A action delete_fase_results em gestao-resultados executa:

  1. Deleta resultados_aluno da fase (+ filtro por nível se especificado)
  2. Deleta inscricoes_olimpiada correspondentes
  3. Deleta configuracoes_fase_nivel da fase/nível (nota de corte + faixas de premiação)
  4. Recomputa snapshots (que serão vazios → deletados)
  5. Auto-despublica: seta liberar_notas=false e liberar_resultados=false em mural_liberacoes
  6. Varredura de fantasmas: para toda a olimpíada, verifica liberações com true que não possuem resultados reais → despublica

6. Badge "Publicado" — Lógica do Frontend

getStatusGeral(escolaOlimpiadaId)

Localização: src/components/mural-olimpico/mural-liberacoes.tsx

O badge é calculado cruzando mural_liberacoes com statsMap:

Para cada liberação com flag true:
  → Verificar se existe stat com total > 0 para mesma fase+nível
  → Só contar como "publicado" se houver dados reais
ResultadoBadgeCor
≥1 flag true COM dadosPublicadoVerde (bg-green-500)
Flags true mas SEM dadosPendenteAmarelo (bg-amber-500)
Nenhuma flag truePendenteAmarelo (bg-amber-500)

Isso evita badges fantasma: liberações que ficaram true no banco mas cujos dados foram limpos.


7. Sincronização de Cache (Frontend)

Query Keys envolvidas

KeyConteúdoOnde usado
['mural-liberacoes']Lista de LiberacaoMural (flags)useMuralEscola, badge, cards
['mural-liberacao-stats', 'batch', ...]Stats por fase/nível (total, fases)Cards de liberação, badge
['gestao-resultados', ...]Resultados do coordenadorTabela de resultados

Invalidação obrigatória

Toda operação que altera resultados ou liberações DEVE invalidar:

typescript
// Em useGestaoResultados.invalidateAll()
queryClient.invalidateQueries({ queryKey: resultadosKeys.all });
queryClient.invalidateQueries({ queryKey: ['mural-liberacao-stats'], refetchType: 'all' });
queryClient.invalidateQueries({ queryKey: ['mural-liberacoes'], refetchType: 'all' });

O refetchType: 'all' garante refetch mesmo de queries inativas (usuário em outra tela).

Pontos de invalidação

OperaçãoInvalida
Inserir resultados (manual ou importação)resultados + stats + liberacoes
Limpar faseresultados + stats + liberacoes
Toggle liberação (publicar/despublicar)stats + liberacoes
Importação em background (sessão concluída)resultados + stats + liberacoes

8. Renderização dos Cards de Liberação

Layout unificado (todas as olimpíadas)

Olimpíada (tab no topo, uma por escola_olimpiada)
└── Fase (tab secundária)
    └── Nível (botão dentro da fase)
        └── Card de liberação (Notas ou Resultados)

Independente do modo_config_fases da olimpíada (uniforme ou por_nivel), o Mural sempre renderiza na hierarquia Fase → Nível. O modo por_nivel é relevante apenas para configuração interna do especialista (questões/tempo), não para a visualização do coordenador.

Visibilidade dos cards

Um card de liberação só aparece se stats.total > 0 para aquela combinação fase+nível. Sem dados (ex: após limpar fase), o card não é renderizado.


9. Checklist — Alterações no Fluxo

markdown
□ Alterou resultados? → invalidar stats + liberacoes
□ Alterou configuracoes_fase_nivel? → recomputar snapshots se publicado
□ Limpou fase? → deletar configs + auto-despublicar + varrer fantasmas
□ Novo tipo de liberação? → atualizar types/mural.ts + mural-escola backend
□ Cache não atualiza? → verificar refetchType: 'all' nas invalidações
□ Badge fantasma? → verificar getStatusGeral cruza com stats