Skip to content

Redesign do frontend: páginas, navegação responsiva e acessibilidade - #35

Merged
matbrgz merged 7 commits into
masterfrom
feat/frontend-design-refresh
Sep 10, 2026
Merged

matbrgz merged 7 commits into
masterfrom
feat/frontend-design-refresh

Conversation

@matbrgz

@matbrgz matbrgz commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

A interface misturava layouts, cores e controles sem ação. Este PR unifica o frontend, melhora a leitura e torna navegação, filtros e recuperação de erros consistentes nas páginas públicas, de participantes e de administração.

  • Identidade em verde-petróleo, paleta semântica com temas claro/escuro, hover, feedback e contraste verificado. Hierarquia de títulos, tabelas, campos, botões e regiões de código padronizadas; cores personalizadas de problemas não comprometem a leitura das letras.
  • Redesenho de início, autenticação, problemas/enunciado/envio, placar, submissões, clarificações, ajuda, administração, julgamento e tarefas. Primeiros passos úteis e telas de recuperação 403/404/419/500.
  • Modais com fundo inert, contenção de foco, Escape e retorno ao acionador. Menu mobile, skip link, foco visível, redução de movimento e tratamento de cores forçadas.
  • Erros associados aos campos, resumo navegável, recuperação de entradas e da etapa/modal correto, feedback de envio e prevenção de submissões duplicadas, preservando os valores nativos do POST.
  • Assistente com seleção nativa por teclado, data local inicial e revisão final. Filtros de tabelas, banco de problemas e clarificações funcionam e persistem na URL. Vereditos compartilhados distinguem fila, avaliação e resultados, incluindo RE/RTE.
  • Remoção de filtros, salvamento global, cronômetro e rejulgamento sem ação. “Ver código” usa o detalhe real da submissão. Recuperação por e-mail orienta solicitar ajuda à organização, pois o endpoint atual somente simula uma confirmação de envio.

Vue monta apenas seus componentes, preservando o documento Blade. A worktree isolada integra a master com os PRs de backend #24–#29, #31 e #36–#38. Sem alterações em controllers, rotas, migrations ou contratos de API; sem novas dependências nesta revisão de cores.

Validação concluída:

  • npm run build.
  • npm run test:frontend: 14 testes, incluindo 68 combinações de contraste nos dois temas.
  • vendor/bin/phpunit --no-coverage em contêiner Linux temporário sem rede: 489 testes / 2.964 assertions no Linux.
  • git diff --check.
  • Navegador: revisão desktop do início, problemas e autenticação, além dos testes de navegação, tema e modal das revisões anteriores. A janela de outra extensão do Chrome interrompeu a rodada final em 375px; revisão visual final mobile/escura e leitor de tela real permanecem pendentes. Contraste dos dois temas foi validado numericamente. Não é certificação integral WCAG.

Análise completa por página e medições · Inventário · Auditoria de acessibilidade.

Listas filtram os registros carregados e atualizam dados por navegação/recarregamento. Templates históricos sem rotas permanecem arquivados. Recuperação real por e-mail e novos controles administrativos dependem de implementação no backend.

Closes #32

Integração final: preserva as novas páginas de perfil e gerenciamento de problemas por competição; ambas adaptadas ao design. Suíte completa passou no Linux. No macOS, somente os dois testes de compilação C dependem do ambiente Linux (Clang sem crt0.o).

@matbrgz

matbrgz commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

Review automatizado do PR #35

Não mergeei ainda — achou 3 regressões visíveis pro usuário que valem correção antes do merge, mais 4 de severidade média e 3 de manutenibilidade/performance (sem bug visível hoje). Reportando como spec pra cada achado ter arquivo/linha/cenário de falha claro.

Alta prioridade (regressões reais)

1. layouts/auth.blade.php (~linha 29): mensagens de erro perderam a cor vermelha e o role="alert"
O loop de flash messages calcula $tone ('destructive'/'success') por chave de sessão mas nunca usa essa variável na classe da div — todas as mensagens (login, registro, esqueci senha) renderizam com a mesma caixa cinza neutra, com role="status" em vez de role="alert" pra erros. Antes disso, erro tinha border-destructive/50 bg-destructive/10 text-destructive. layouts/app.blade.php continua colorindo certo — a regressão é isolada no layout de auth.
Cenário de falha: login falha, usuário vê uma mensagem neutra em vez de vermelha, e leitores de tela não anunciam como alerta.

2. resources/js/components/ContestTimer.vue (~linha 56): uma única falha de rede no polling de 60s derruba o relógio durante um contest ativo
fetchContestData agora faz polling a cada 60s (antes só buscava uma vez no mount). No catch, sempre seta failed=true e contestData=null, mesmo pra falhas transitórias (timeout, 5xx passageiro). Nenhum uso de <contest-timer> no app passa props start-time/duration como fallback (todos são <contest-timer></contest-timer> puro), então não há fallback pro cronômetro local.
Cenário de falha: um juiz ou time olhando o relógio durante a competição vê "Relógio indisponível" aparecer do nada por causa de uma falha de rede passageira, mesmo com o contest rodando normalmente.

3. layouts/app.blade.php (linhas ~158, 168, 189): botão de menu mobile pode não responder em conexão lenta
onclick="toggleSidebar()" e onclick="toggleUserMenu()" continuam no HTML, mas o <script> inline que definia essas funções globais foi removido — agora elas só existem dentro de resources/js/ui.js, carregado via @vite(['resources/js/app.js']) (script de módulo externo).
Cenário de falha: em conexão lenta, se o usuário tocar no hambúrguer ou no menu de usuário antes do ui.js terminar de carregar, o navegador lança ReferenceError: toggleSidebar is not defined e o botão simplesmente não faz nada.

Média prioridade

4. layouts/setup.blade.php + RedirectIfNoContest middleware: banner de boas-vindas do primeiro acesso nunca aparece
O layout de setup virou um passthrough puro (@extends('layouts.app')), perdendo o bloco @if(session('info')). layouts/app.blade.php só trata session('success')/session('error'), nunca session('info') — e é exatamente essa chave que RedirectIfNoContest usa pra dar boas-vindas ao admin configurando a primeira maratona.

5. resources/css/app.css (~linha 160): drawer mobile fecha com corte seco em vez de deslizar
.app-sidebar:not(.is-open) { visibility: hidden; } não está incluído na transição — visibility não é animável via transition-transform, então ao fechar o menu em telas <1024px ele some instantaneamente em vez dos 300ms de slide-out que o resto do código sugere.

6. resources/views/exercises/submit.blade.php (~linha 58): texto corrompido no dropzone de upload
"Clique para selecionar ou arraste o arquivo" virou "Clique para selecionar de código-fonte" — perdeu a menção ao arrastar e ficou gramaticalmente quebrado. Visível pra todo participante submetendo solução.

7. backend/contest-wizard.blade.php (~linha 455): filtro de dificuldade não marca aria-pressed
filterProblems(difficulty) só alterna card.style.display, sem chamar setAttribute('aria-pressed', ...) como o equivalente em problem-bank.blade.php faz — o CSS .filter-btn[aria-pressed="true"] do redesign nunca é aplicado nesta tela especificamente.

Baixa prioridade (manutenibilidade/performance, sem bug visível hoje)

8. resources/js/ui.js (~linha 50): interceptor genérico de onclick mascara silenciosamente as funções openModal/closeModal locais ainda presentes em configurations.blade.php, exercises.blade.php, teams.blade.php, users.blade.php. Hoje é inofensivo (fazem a mesma coisa), mas é uma armadilha: uma edição futura numa dessas funções locais nunca vai executar de fato.

9. backend/problem-bank.blade.php (~linha 189): duplica a função normalize() de ui.js em vez de usar o mecanismo genérico [data-table-filter] que o próprio PR já usa em 10+ outras tabelas.

10. resources/js/ui.js (~linha 109): filtro de tabela sem debounce nem cache do texto normalizado — recalcula normalize(row.textContent) pra cada linha a cada tecla digitada, sem throttle. Perceptível em tabelas grandes (backend/users, judge/runs).


Revisão feita com o skill code-review (múltiplos agentes, achados verificados linha a linha contra o diff real). Não apliquei nenhuma correção — combinado que o frontend fica com o Codex.

matbrgz and others added 2 commits September 10, 2026 15:58
…est wizard

Unscoped hasOldInput() (no key argument) only checks whether the
session has ANY flashed old input at all, not whether this form's
fields are among it. Stale old-input flashed by a completely
unrelated failed form elsewhere in the app was enough to make the
is_active/is_public/languages[] checkboxes silently render unchecked
here, creating a new contest as inactive/private or with no languages
pre-selected. Scoped each check to its own field name
(hasOldInput('is_active') etc.) so it only applies when this form
itself was the one that failed validation.

Found in code review of #35.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011u3o4QCgpcqH51edYyRPMn
@matbrgz

matbrgz commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

Review

Great work on the redesign — the adaptation to the current backend (backend/problems.blade.php, resources/views/profile/edit.blade.php) after the merge conflict is spot on: field names, routes, CSRF, and old-input handling all line up correctly with ProblemManagementController and ProfileController. The contest wizard's language checkboxes correctly pull live from Language::getDefaultLanguages(), so it'll automatically reflect the toolchain changes from #39.

Fixed before merging (real correctness bugs)

  • contest-wizard.blade.php: the is_active/is_public/languages[] checkboxes used unscoped session()->hasOldInput() (no key) instead of hasOldInput('is_active') etc. Laravel's unscoped hasOldInput() just checks "is there any flashed old input in the session", not whether this form's keys are present — so stale old-input flashed by a completely unrelated failed form elsewhere in the app would make these checkboxes silently render unchecked, creating a new contest as inactive/private or with no languages pre-selected. Scoped the check to each field name to fix.

Follow-up debt (not blocking, documented for later)

  • resources/js/ui/forms.js: error-summary field matching doesn't resolve Laravel's dotted array-index error keys (problems.0) from wildcard validation rules — affects the "add problem from bank" form if an item is deleted mid-session.
  • resources/js/ui/status-filters.js: missing null guard on [role=status] lookup (ui.js's equivalent code guards it).
  • resources/views/backend/teams.blade.php / users.blade.php: leftover dead openModal/closeModal <script> blocks now fully superseded by resources/js/ui/dialogs.js — currently inert but will drift.
  • resources/views/backend/problem-bank.blade.php: one modal close button (onclick="closeModal()", no id) doesn't match dialogs.js's takeover regex, so it runs on a different code path than every other modal trigger.
  • Filter/count/empty-state logic is now implemented three times with drift (resources/js/ui.js, ui/status-filters.js, ui/wizard.js's filterProblems() uses style.display instead of hidden and skips URL persistence) — worth consolidating into one shared helper in a follow-up.
  • wizardForm is special-cased out of forms.js's generic submit pipeline, so it doesn't get the shared busy-state/error-summary wiring other forms get.
  • wizard.js's submit-time validity check re-derives its own logic instead of calling the already-defined validateStep() per step — could drift if a rule is added to one but not the other.
  • The wizard's .selected class on language/problem cards is now visually dead (CSS moved to :has(:checked) in resources/css/app.css), which loses the visual selected-state indicator in browsers without :has() support.

None of the follow-up items are merge-blockers on their own; flagging them here so they're tracked rather than lost. Full suite passes (494+ tests) against this branch merged with current master. Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redesign completo do frontend: navegação, páginas e acessibilidade

1 participant