Skip to content

Melhorias de UI/UX no app (issue #36) - #37

Open
Rossi-Luciano wants to merge 37 commits into
scieloorg:mainfrom
Rossi-Luciano:feature/issue-36-melhorias-ui-ux
Open

Melhorias de UI/UX no app (issue #36)#37
Rossi-Luciano wants to merge 37 commits into
scieloorg:mainfrom
Rossi-Luciano:feature/issue-36-melhorias-ui-ux

Conversation

@Rossi-Luciano

@Rossi-Luciano Rossi-Luciano commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

O que esse PR faz?

Implementa os 9 itens de UI/UX listados na issue #36, a partir de feedback de usabilidade recebido sobre o app (originalmente fora do escopo do relatório de validação, que é a issue #34/PR #35):

  1. Indicador visual de carregamento (spinner + "Validando...") no botão de validar, que fica desabilitado durante o envio.
  2. Identidade visual e layout mais cuidados na tela principal (tokens de cor com vermelho de marca, cards com sombra sutil, botões e campos padronizados).
  3. Lista de PDFs do histórico virou chips compactos com ícone e rótulo curto ("suppl1", "PDF principal") em vez de nomes de arquivo completos e repetitivos.
  4. Coluna de data do histórico não exibe mais microssegundos.
  5. Colunas "Relatório", "CSV", "HTML" e "PDF" consolidadas em uma única coluna "Ações", com ícones por tipo de ação.
  6. Paginação numerada estilo Django ("Anterior | 1 2 3 4 | Próxima") em vez de só Anterior/Próxima.
  7. Rodapé para de exibir "Compilado para macOS..." quando o app roda a partir do código-fonte em outra plataforma (is_running_from_source() nova em version.py, reaproveitada em build_metadata.py).
  8. Preferência de "itens por página" não é mais perdida ao validar um novo pacote (form de validação passa a repassar a querystring atual).
  9. "Artigos" e "Pacotes" viram abas sempre visíveis no mesmo painel, cada uma com paginação e filtro próprios (ver atualização abaixo).

Atualização depois do merge de main (PR #35 já mesclado): o relatório HTML (report.html), que veio pronto do PR #35 com um layout simples (Arial puro, sem cor de marca), foi reconstruído nesta branch com a mesma identidade visual usada no restante do app — cabeçalho com marca, cards de resumo por gravidade, barra de progresso, filtro por gravidade e busca por texto livre. A estrutura de dados e as rotas do backend não mudaram, só template/CSS/JS/tradução. A coluna "Ações" (item 5) também passou a incluir o chip "Relatório" nesta atualização.

Atualização depois da 2ª rodada de review (pitangainnovare):

  • Build Windows: build_windows.ps1 nunca gerava um build_info.py real (diferente de build_linux.sh/build_macos.sh), então o binário Windows empacotava o arquivo estático versionado no repo (com valores de macOS) e exibia "Compilado para macOS" incorretamente. Criado generate_build_info.ps1 (porta pro PowerShell a mesma lógica do script .sh equivalente) e adicionada a chamada em build_windows.ps1. Além disso, get_footer_build_label() ganhou uma checagem defensiva extra (platform.system() == "Darwin"), pra não confiar cegamente num build_info.py desatualizado se algum build futuro esquecer de regenerá-lo — o mesmo padrão de bug já apareceu duas vezes (Linux, depois Windows).
  • Aba "Artigos" redesenhada: antes, só listava os artigos do último pacote selecionado, sem paginação nem filtro. Agora lista todos os snapshots de artigos de todos os pacotes (JOIN entre package_article_snapshot e package_validation_history, sem necessidade de migração), com paginação e filtros próprios (pacote, DOI, PID, status), ordenados por data de validação. Clicar num pacote na aba "Pacotes" abre a aba "Artigos" já filtrada por aquele history_id, com um chip "Filtrando por pacote: X" e um link para remover o filtro. Novo endpoint GET /articles-list serve essa listagem via AJAX (mesmo padrão já usado por /history-list).

Inclui também tradução (en/es) das strings novas introduzidas por todos esses itens.

Onde a revisão poderia começar?

  • spsvalidator/src/spsvalidator/web/routes.py_paginated_articles()/_paginated_history(), endpoint /articles-list, index() simplificado (default_tab em vez de latest_result).
  • spsvalidator/src/spsvalidator/db/repository.pylist_articles/count_articles/get_package_name, novos, com testes em tests/test_articles_repository.py.
  • spsvalidator/src/spsvalidator/web/templates/index.html — abas sempre visíveis, formulário de filtro da aba Artigos, JS generalizado (bindLiveFilters) reaproveitado pelas duas abas.
  • spsvalidator/src/spsvalidator/web/templates/_articles_list.html — novo, espelha _history_list.html.
  • spsvalidator/src/spsvalidator/build_metadata.py e spsvalidator/packaging/generate_build_info.ps1/build_windows.ps1 — fix do rodapé no build Windows, com testes em tests/test_build_metadata.py.
  • spsvalidator/src/spsvalidator/web/templates/report.html — reconstrução visual do relatório, reaproveitando os dados de build_grouped_report já existentes.

Como este poderia ser testado manualmente?

  1. Rodar spsvalidator --browser e validar um pacote SPS real.
  2. Observar o botão "Validar" mudar para "Validando..." com spinner durante o envio.
  3. Após validar, a aba "Artigos" deve aparecer ativa, filtrada pelo pacote recém-validado, com o chip "Filtrando por pacote" visível.
  4. Clicar em "Limpar filtro" e conferir que a aba passa a listar artigos de todos os pacotes, com paginação.
  5. Testar os filtros da aba Artigos (nome do pacote, DOI, PID, status) isoladamente e em combinação.
  6. Configurar "Itens por página" (em qualquer uma das duas abas) para um valor diferente de 25, validar outro pacote e conferir que o valor se mantém.
  7. Com mais de uma página de histórico ou de artigos, conferir a paginação numerada e a navegação entre páginas (com e sem JavaScript), inclusive que filtrar/paginar numa aba não apaga o filtro ativo da outra na URL.
  8. Conferir a coluna "Ações" (Relatório/CSV/HTML/PDF) e a legibilidade dos chips de PDF.
  9. Rodando a partir do código-fonte (fora do macOS), conferir que o rodapé não mostra mais "Compilado para macOS".
  10. Abrir o relatório de um pacote (chip "Relatório") e conferir cabeçalho com marca, cards de resumo, filtro por gravidade e busca, além do checklist de correção (já existente, comportamento preservado).

Algum cenário de contexto que queira dar?

Feedback qualitativo recebido de um colega de equipe sobre a impressão de qualidade do app, focado em pontos que "custam pouco e pesam na percepção de acabamento" — inclusive uma comparação direta com um mockup de referência do relatório HTML, que motivou a reconstrução visual descrita acima. Cada item foi implementado e testado individualmente (Playwright headless) antes de avançar para o próximo, incluindo os itens endereçados na 2ª rodada de review.

Esta branch partiu de main antes do PR #35 ser mesclado; depois que o PR #35 foi integrado ao main do upstream, fiz merge de main nesta branch e resolvi os conflitos (esperados) em _history_list.html e nos catálogos de tradução — a coluna "Relatório" do PR #35 foi incorporada dentro do .actions/.chip-link já existente aqui, em vez de ficar em coluna separada.

Screenshots

image

Quais são os tickets relevantes?

Closes #36. Relacionado a #32 (rodapé "Built for macOS" estático) e #34/#35 (relatório HTML, já mesclado e reconstruído visualmente aqui).

Referências

Feedback de UI/UX recebido informalmente da equipe.


Segurança da informação (NSI.04)

Este PR manipula dados sensíveis ou pessoais (LGPD)?

  • Sim
  • Não

Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?

  • Sim
  • Não

Este PR introduz, atualiza ou remove dependências de terceiros?

  • Sim
  • Não

Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?

  • Sim
  • Não aplicável a este PR — repositório não tem pipeline de Sonar/Trivy configurado; validação feita via suíte de testes local e testes manuais (Playwright).

Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?

  • Sim — nomes de arquivo/pacote (derivados do zip enviado) são renderizados em templates Jinja2 com autoescape padrão (nenhum uso de |safe); os filtros novos da aba Artigos (pacote/doi/pid/status) são usados em SQL sempre via parâmetros posicionais (?), nunca concatenados na string da query.
  • Não

Este PR expõe novos endpoints, telas ou serviços?

  • Sim — novo endpoint GET /articles-list, mesmo padrão do /history-list já existente (retorna um fragmento HTML paginado/filtrado, consumido via AJAX pela própria aplicação; não expõe dado além do que já era acessível pela aba Artigos).
  • Não

Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?

  • Não, nenhum segredo foi commitado
  • Sim

Rossi-Luciano and others added 18 commits July 30, 2026 18:25
- botao "Validar" ganha um spinner (span .spinner) e o texto muda
  para "Validando..." assim que o formulario e submetido
- botao fica desabilitado durante o envio, evitando duplo clique
- listener em "pageshow" reseta o botao ao estado normal caso o
  usuario volte para a pagina pelo cache do navegador (bfcache),
  ja que nesse caso o JS nao roda de novo do zero
- testado com Playwright (headless): estado normal, estado
  "Validando..." (via evento submit) e reset apos voltar, alem do
  fluxo real de upload+clique+redirect ponta a ponta

Porque: a validacao de pacotes maiores demora e, sem nenhum feedback
visual, a tela parece travada — primeiro item da issue scieloorg#36
(melhorias de UI/UX). A traducao da nova string "Validando..." fica
para um commit posterior, junto com as demais strings novas da issue
scieloorg#36, seguindo o mesmo padrao usado na issue scieloorg#34.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- introduz tokens de cor via CSS custom properties (--brand, --surface,
  --border, --text, --critical/--success etc.), com vermelho de marca
  (#da202c) no topo do cabecalho e nos botoes primarios
  ("Validar")
- cards (.panel) com sombra sutil e cabecalho com linha divisoria,
  em vez de apenas uma borda cinza
  - botoes (.button/.button-primary) e campos de formulario
  (input/select) padronizados visualmente
- cabecalho de tabela (thead) com fundo sutil, texto em maiusculas e
  hover nas linhas; tabelas envolvidas em .table-wrap (overflow-x:
  auto) para nao vazar horizontalmente em telas estreitas
- nova classe .badge para status (usada no proximo commit, em
  _history_list.html), com cores diferentes para "Valido" e
  "Invalido/Erro"
- rodape fixado ao fim da coluna (margin-top: auto) e com estilo mais
  discreto

Porque: segundo item da issue scieloorg#36 (melhorias de UI/UX) — o layout
anterior usava so bordas cinza padrao de navegador e passava uma
impressao de inacabado, mesmo sendo um app standalone/offline. A
paleta segue a mesma linguagem visual ja usada no template de
relatorio HTML da issue scieloorg#34 (ainda em outra branch/PR), para que as
duas telas fiquem consistentes quando integradas.

Testado visualmente com Playwright (headless): tela vazia e tela com
historico populado (pacote real), cobrindo cabecalho, cards, botoes,
badges de status e tabela.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…status

- <table> do historico envolvida em <div class="table-wrap"> para
  rolagem horizontal segura em telas estreitas, sem estourar o layout
- texto do status ("Valido"/"Invalido"/"Erro") passa a ficar dentro
  de <span class="badge">, que ganha aparencia de pilula colorida via
  CSS ja definido em index.html

Porque: parte do segundo item da issue scieloorg#36 (melhorias de UI/UX);
mantem a estrutura e as colunas da tabela inalteradas, so aplica os
estilos introduzidos no commit anterior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- _pdf_previews_by_article() passa a retornar "pdfs" (lista de
  {filename, label}) em vez de "pdf_names" (lista de strings)
- nova _short_pdf_label(xml_stem, filename): remove o prefixo
  repetido (o proprio xml_stem) de cada nome de arquivo, retornando
  "PDF principal" para o arquivo sem sufixo e o sufixo restante (ex.
  "suppl1") para os demais; cai no nome completo se o arquivo nao
  seguir essa convencao

Porque: terceiro item da issue scieloorg#36 (melhorias de UI/UX) — a lista de
PDFs de um artigo mostra nomes de arquivo quase identicos (mesmo
prefixo longo, so muda o sufixo "-supplN"), dificultando diferencia-los
visualmente. O nome completo continua disponivel (usado como href e,
no proximo commit, como title/tooltip do link).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- novo CSS (.pdf-group, .pdf-list, .pdf-chip, .pdf-icon): cada PDF
  vira uma pilula compacta com icone de documento, fundo neutro e
  cor de texto secundaria, em vez de um link vermelho grande
  sublinhado ocupando uma linha inteira
- cor propositalmente neutra (nao usa --brand): a lista pode ter
  varios itens por linha do historico, entao reserva o vermelho de
  marca para acoes primarias e mantem esses links discretos

Porque: parte do terceiro item da issue scieloorg#36 (melhorias de UI/UX);
complementa o rotulo curto do commit anterior — sem esse estilo, os
rotulos como "suppl1"/"suppl2" ficavam soltos e ainda dificeis de
escanear visualmente como itens separados.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- coluna PDF passa a iterar group.pdfs (filename + label) em vez de
  group.pdf_names (strings), usando pdf.label como texto visivel e
  pdf.filename no href e no atributo title (nome completo disponivel
  via tooltip ao passar o mouse)
- cada item usa a classe .pdf-chip com um icone SVG de documento
  inline, estilizados no commit anterior

Porque: fecha o terceiro item da issue scieloorg#36 (melhorias de UI/UX).
Testado com Playwright contra o pacote real usado nos itens
anteriores: rotulos "suppl1".."suppl5" e "PDF principal" no lugar dos
nomes completos, com o nome de arquivo real ainda acessivel via
title/href.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- nova _format_validated_at(): faz o parse do "validated_at" (gravado
  via datetime.now(UTC).isoformat(), que inclui microssegundos) e
  reformata com timespec="seconds"
- aplicada a cada item em _paginated_history(), antes de passar pro
  template; em caso de valor invalido, mantem o original (fallback
  silencioso, sem afetar a listagem)

Porque: quarto item da issue scieloorg#36 (melhorias de UI/UX) — a coluna Data
mostrava "2026-07-30T21:34:36.960044+00:00", com microssegundos que
nao tem nenhuma utilidade pra quem esta lendo a tabela e so poluem a
linha. O valor gravado no banco continua com precisao total; so a
exibicao foi ajustada.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- redirect de sucesso de validate() passa a repassar os parametros
  atuais da querystring (q, status, page_size, page) pro
  url_for("web.index", ...), usando "or None" pra nao poluir a URL
  com parametros vazios quando nao estao presentes

Porque: quinto item da issue scieloorg#36 (melhorias de UI/UX) — configurar
"itens por pagina" pra 5, buscar por nome ou navegar pra outra pagina
funcionava corretamente, mas ao clicar em "Validar" o redirect sempre
levava de volta a "/?history_id=...", sem nenhum desses parametros, e
a lista voltava pro padrao (25 itens, sem filtro, pagina 1). O
proximo commit (index.html) faz o formulario de validacao enviar a
querystring atual, que e o que este trecho agora repassa adiante.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ao submeter #validate-form, se window.location.search nao estiver
  vazio, o "action" do formulario passa a ser "/validate" + essa
  querystring (ex.: "/validate?page_size=5&page=2")
- window.location.search ja reflete o filtro/paginacao atual porque
  bindLiveHistoryFilters() usa history.replaceState a cada mudanca

Porque: index.html e web/routes.py juntos fecham o quinto item da
issue scieloorg#36. O POST em si continua indo pro mesmo endpoint; so a
querystring anexada e o que routes.py le (request.args) pra decidir
com quais q/status/page_size/page redirecionar depois de validar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- nova _page_range(page, total_pages, window=2): retorna os numeros
  de pagina ao redor da pagina atual (ate 2 pra cada lado, recortado
  aos limites [1, total_pages])
- _paginated_history() passa "page_range" pro contexto do template

Porque: sexto item da issue scieloorg#36 (melhorias de UI/UX) — trocar a
paginacao simples (so "Anterior"/"Proxima") por uma no estilo Django
("Anterior | 10 | 11 | 12 | Proxima"), pedida explicitamente no
feedback. A marcacao que consome isso vem no proximo commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- substitui o rodape "Anterior/Proxima" por uma <nav class="pagination">
  com Anterior | numeros de pagina (page_range) | Proxima
- pagina atual exibida como <span> (nao clicavel, destacada); Anterior
  e Proxima tambem viram <span> desabilitado nas pontas da lista
- reaproveita a classe "page-link" e o atributo data-page ja usados
  pela delegacao de evento existente em bindLiveHistoryFilters() no
  index.html — nenhuma mudanca de JS foi necessaria

Porque: fecha o sexto item da issue scieloorg#36 (melhorias de UI/UX). Testado
com Playwright (page_size=3 pra forcar varias paginas): janela de
paginas corrige corretamente ao navegar, tanto pelo link <a> quanto
pela atualizacao via AJAX (fetch em /history-list).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- novo CSS pra .pagination/.pagination-links/.page-link: pilulas
  compactas, pagina atual em vermelho de marca
  (.page-link-current), Anterior/Proxima desabilitados em cinza claro
  (.page-link-disabled)
- ".page-link" com maior especificidade que o "a" global evita que o
  vermelho de link padrao vaze pros numeros de pagina nao-atuais

Porque: complementa a marcacao do commit anterior (issue scieloorg#36, sexto
item) — sem esse CSS os numeros de pagina ficariam sem distincao
visual entre "atual" e "clicavel".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- _load_version() ja distinguia "rodando do codigo-fonte" (pyproject.toml
  presente ao lado do pacote) de "build empacotado" (PyInstaller, sem
  pyproject.toml) pra decidir se confia em build_info.py; essa checagem
  vira a funcao publica is_running_from_source(), sem mudar o
  comportamento de _load_version()

Porque: primeiro passo do setimo item da issue scieloorg#36 (melhorias de
UI/UX, "Built for mac" aparecendo no Linux) — o proximo commit
(build_metadata.py) reusa essa mesma checagem, que ja existia e
resolvia exatamente esse problema pra APP_VERSION mas nao era
aplicada ao rodape.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…onte

- get_footer_build_label() so considera BUILD_MACOS_VERSION/BUILD_PLATFORM
  de build_info.py quando NAO esta rodando a partir do codigo-fonte
  (is_running_from_source() da version.py); do contrario cai direto na
  deteccao por platform.system(), como ja acontecia pra qualquer
  plataforma diferente de macOS

Porque: setimo item da issue scieloorg#36 (melhorias de UI/UX) e tambem a
causa raiz da issue scieloorg#32. spsvalidator/src/spsvalidator/build_info.py
e gerado por packaging/generate_build_info.sh e commitado no repo com
os valores da ultima vez que alguem rodou o build empacotado num Mac
("BUILD_PLATFORM = macOS"); quem roda o app direto do codigo-fonte
(pip install -e . + spsvalidator --browser/--desktop) nunca chama
esse script, entao o rodape sempre mostrava "Compilado para macOS
..." mesmo em Linux/Windows, e continuaria mostrando isso ate alguem
rodar um build real naquela plataforma. Verificado manualmente:
get_footer_build_label() retorna "Build de desenvolvimento (Linux)"
neste ambiente apos a correcao.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- as duas secoes que antes eram paineis separados (historico embaixo,
  artigos considerados por ultimo) viram abas dentro de um unico
  painel: "Artigos Considerados" e "Pacotes validados"
- quando ha latest_result (apos validar ou clicar na data de um item
  do historico), a aba "Artigos Considerados" comeca ativa/visivel;
  sem latest_result, so existe o conteudo do historico e nenhuma aba
  e exibida (comportamento igual ao anterior)
- troca de aba em JS puro (bindTabs()): alterna aria-selected nos
  botoes e o atributo hidden dos paineis, sem requisicao ao servidor
- estilo novo (.tabs/.tab/.tab-panel) seguindo a mesma linguagem
  visual (vermelho de marca na aba ativa)

Porque: nono e ultimo item pendente da issue scieloorg#36 (melhorias de
UI/UX) — antes, "Artigos Considerados" era renderizado depois da
tabela de historico inteira (ate 25 itens por padrao), entao quem
validava um pacote ou clicava na data de um item precisava rolar a
pagina inteira pra ver os detalhes. Com abas, o conteudo relevante
fica visivel imediatamente, sem rolagem, e o historico continua a um
clique de distancia.

Testado com Playwright: fluxo real de upload+validar (aba "Artigos
Considerados" ativa sem rolagem), troca pra aba "Pacotes validados" e
de volta, e o caso sem historico selecionado (sem abas, comportamento
inalterado).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- renomeia .pdf-chip/.pdf-icon/.pdf-group/.pdf-list (introduzidos no
  terceiro item da issue scieloorg#36, so pra PDF) pra .chip-link/.chip-icon/
  .action-group/.chip-list, sem mudar a aparencia
- nova .actions: container vertical que vai agrupar os chips de CSV,
  HTML e PDF numa unica coluna (proximo commit)

Porque: primeiro passo do oitavo e ultimo item da issue scieloorg#36
(melhorias de UI/UX) — consolidar as colunas "Validacao" (download de
CSV), "HTML" e "PDF" numa unica coluna "Acoes". O estilo de chip ja
existia pra PDF; renomear pra generico evita duplicar CSS quando HTML
e o botao de CSV passarem a usar o mesmo padrao visual.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- as 3 colunas ("Validação" com o link de CSV, "HTML", "PDF") viram
  uma so coluna "Ações", com todos os links renderizados como
  .chip-link dentro de um container .actions
- corrige de passagem o rotulo do link de CSV: era "Relatório" (texto
  herdado de antes de existir uma pagina de relatorio separada nesta
  branch) com aria-label "Baixar relatório de %(name)s"; agora o
  texto visivel e "CSV" e o aria-label vira "Baixar CSV de %(name)s",
  condizente com o que o link realmente faz (dispara download_csv)
- cada chip ganha um icone (download pro CSV, "olho" pro preview
  HTML, documento pro PDF, este ultimo ja existente) pra diferenciar
  o tipo de acao visualmente dentro da coluna unica
- classe "csv-download" mantida no link de CSV (bindCsvDownloads() em
  index.html continua funcionando sem alteracao)

Porque: fecha o oitavo e ultimo item da issue scieloorg#36 (melhorias de
UI/UX) — three colunas de acao lado a lado ocupavam bastante largura
e nao deixavam claro que eram todas "coisas que voce pode fazer com
esta validacao". Testado com Playwright: cabecalho unico "Ações",
download de CSV ainda dispara corretamente (mesmo arquivo, mesmo
feedback visual), chips de HTML/PDF lado a lado com quebra de linha.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- adiciona ao catalogo pt/en/es as 8 strings introduzidas pelos
  commits desta branch: "Validando...", "Ações", "Baixar CSV de
  %(name)s", "Baixar CSV", "CSV", "Pré-visualização HTML", "PDF
  principal" e "Paginação"
- remove o msgid "Relatório" (ligado ao antigo link de download de
  CSV, cujo texto virou "CSV" no commit que consolidou as colunas de
  acao); nao ha mais nenhuma referencia a ele no codigo
- pt fica com msgstr vazio (idioma fonte, como as demais entradas do
  catalogo); en e es traduzidos manualmente
- .mo recompilados com pybabel compile

Isolamento: a diff foi construida comparando um dump de .pot extraido
desta branch contra um dump equivalente extraido de "main" (mesmos
arquivos, so routes.py/index.html/_history_list.html/build_metadata.py/
version.py), pra pegar so o que estas mudancas introduziram — nao um
"pybabel update" cru, que teria reformatado o arquivo inteiro e
puxado dezenas de strings que ja estavam faltando nos catalogos antes
desta branch (ex.: "HTML"/"PDF"/status labels/paginacao antiga do
historico, nunca extraidos desde que foram adicionados em PRs
anteriores). Esse debt de i18n pre-existente continua sem tradução,
deliberadamente fora do escopo deste commit.

Porque: fecha o lote de traducao da issue scieloorg#36 (melhorias de UI/UX),
seguindo o mesmo padrao usado no commit db1d5ea da issue scieloorg#34.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rossi-Luciano and others added 3 commits July 31, 2026 09:28
# Conflicts:
#	spsvalidator/src/spsvalidator/translations/en/LC_MESSAGES/messages.po
#	spsvalidator/src/spsvalidator/translations/es/LC_MESSAGES/messages.po
#	spsvalidator/src/spsvalidator/translations/pt/LC_MESSAGES/messages.po
#	spsvalidator/src/spsvalidator/web/templates/_history_list.html
- Substitui o cabecalho simples por header com marca (circulo com "S"),
  eyebrow "SciELO · <nome do app>", titulo do pacote e acoes em botoes
  (Historico, CSV, Limpar marcacoes).
- Adiciona cards de resumo (total, contagem por gravidade, corrigidas)
  com barra de progresso segmentada por gravidade logo abaixo.
- Adiciona toolbar com filtro por gravidade (botoes "Todos"/CRITICAL/
  ERROR/WARNING/...) e busca por texto livre, que soma/oculta
  categorias e ocorrencias via atributos data-search-text calculados
  no proprio Jinja (mensagem + acao de correcao + detalhes tecnicos).
- Troca a paleta neutra (Arial puro, sem cor de marca) pelas variaveis
  de tema (--brand #da202c e derivadas, --critical/--error/--warning/
  --success) usadas tanto no cabecalho quanto nas secoes de gravidade,
  categorias e ocorrencias (chevrons, field-label colorido, contadores
  monoespacados).
- Mantem intacta a logica de checklist ja existente (checkbox por
  ocorrencia e por grupo, localStorage por history_id, botao "Limpar
  marcacoes"), so adaptando os seletores as novas classes.

Porque: a issue scieloorg#36 pediu CSS mais cuidado com identidade visual do
SciELO no app geral, mas o relatorio ficou de fora do escopo original
porque so existia no PR scieloorg#35 (issue scieloorg#34), ainda nao mesclado. Com o
merge de main nesta branch, apareceu a oportunidade - e a comparacao
direta com o mockup de referencia (compartilhado localmente, nao
versionado) mostrou que o relatorio simples nao correspondia ao que
foi pedido. Estrutura de dados (grouped_report, occurrence.key/advise/
details) e rotas do backend nao mudaram - e so template, CSS, JS e
traducao.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JMzp8hGLSHNcMaNPCZSf5h
- Roda pybabel extract/update e traduz para en/es as 9 strings
  introduzidas pela reconstrucao visual do relatorio HTML: "Historico",
  "Acoes do relatorio", "Resumo do relatorio", "Total de ocorrencias",
  "Filtrar por gravidade", "Todos (%(count)s)", "Buscar no relatorio",
  "Buscar problema, acao ou detalhe" e "Nenhum resultado para os
  filtros atuais.".
- O merge de main trouxe tambem strings do PR scieloorg#35 (ex.: "Relatorio",
  "Pacote", "Gravidade") que o pybabel update tentou casar por
  similaridade (fuzzy matching) com strings ja existentes - duas
  ficaram erradas ("Validando..." virou "Validate", "Baixar CSV de
  %(name)s" virou "Download CSV of %(name)s") e foram corrigidas aqui
  para o texto correto ja usado antes do merge.

Nao mexido (fora do escopo): duas strings ja tinham traducao errada em
en *antes* deste merge, herdada do main/PR scieloorg#35 - "Valido" e "Limpar"
ambas com msgstr "Validate" - registrado para uma issue de i18n futura,
nao corrigido aqui pra nao misturar com o trabalho desta branch.

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

@pitangainnovare pitangainnovare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sugiro alguns pontos:

A) Inverter as tabs e colocar termo menor, ou seja:
Mostrar

Pacotes | Artigos

Em lugar de

Pacotes validados | Artigos Considerados

B) Apresentar o valor da data de maneira mais amigável
Mostrar

2026-07-31 13:47:00

Em lugar de

2026-07-31T13:47:00+00:00

C) Pode ocorrer rótulo incorreto para PDFs com prefixo semelhante, pois _short_pdf_label() considera qualquer nome iniciado por xml_stem como um PDF relacionado por sufixo. Por exemplo, xml_stem="art" e filename="article.pdf" resultam no rótulo icle. O encurtamento deveria ocorrer apenas quando houver um separador - ou _; caso contrário, deve manter o nome completo. (CONFIRMAR com a Roberta)

D) O termo ``Compilado para macOS'' ainda aparece em builds Linux/Windows. Quando o app está empacotado, is_running_from_source() retorna `False` e o código confia no `build_info.py`, que contém valores estáticos de macOS. Como os scripts de build para Linux e Windows não regeneram esse arquivo, os binários dessas plataformas continuam exibindo incorretamente “Compilado para macOS”.

OBS) Tela de referência para os problemas A e B mencionados
Image

@@ -80,11 +113,6 @@ msgstr ""
msgid "Exceptions"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vamos traduzir esse termo também?

@@ -80,11 +113,6 @@ msgstr ""
msgid "Exceptions"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vamos traduzir esse termo também?

- Adiciona a chamada a `generate_build_info.sh` em `build_linux.sh`,
  igual ao que `build_macos.sh` já fazia.

Porque: `build_info.py` e um arquivo estatico versionado com valores
de macOS (BUILD_PLATFORM = "macOS"). Como `build_linux.sh` nunca
regenerava esse arquivo antes de empacotar, o binario Linux herdava o
conteudo commitado e o rodape da aplicacao continuava exibindo
"Compilado para macOS" mesmo rodando em Linux. `generate_build_info.sh`
ja tratava corretamente plataformas nao-Darwin (grava BUILD_PLATFORM
real via `uname -s` e BUILD_MACOS_VERSION = "development"); so faltava
ser chamado nesse script. Causa raiz identificada durante a review do
PR scieloorg#37 (pitangainnovare) e relacionada a issue scieloorg#32.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
Rossi-Luciano and others added 6 commits July 31, 2026 12:14
…orico

- `_short_pdf_label()`: so encurta o nome do PDF quando o caractere
  logo apos o `xml_stem` for "-" ou "_"; caso contrario mantem o nome
  completo do arquivo.
- `_format_validated_at()`: troca `isoformat(timespec="seconds")` por
  `strftime("%Y-%m-%d %H:%M:%S")`.

Porque: `_short_pdf_label()` tinha um bug de fronteira de string —
`stem.startswith(xml_stem)` e verdadeiro pra qualquer nome de arquivo
que comece com o stem, mesmo sem separador. Com `xml_stem="art"` e
`filename="article.pdf"`, o corte gerava o rotulo errado "icle" em vez
do nome completo. `_format_validated_at()` ainda expunha o "T" do ISO
8601 e o offset "+00:00" (o timespec="seconds" so removia os
microssegundos), o que e ruido pra leitura na tabela de historico; o
valor gravado continua em UTC, so sem indicar o timezone
explicitamente. Ambos os pontos foram levantados na review do PR scieloorg#37
(pitangainnovare).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
- Troca `_("Exceptions")` por `_("Exceções")` no cabecalho da tabela
  de historico.

Porque: a review do PR scieloorg#37 (pitangainnovare) apontou que o termo
aparecia sem traducao tanto no catalogo pt quanto no es. A causa nao
era falta de traducao em es — era o msgid de origem estar em ingles
por engano, diferente das colunas vizinhas ("Ações", "Relatório"), que
ja nascem em pt-BR. Alinhar o msgid ao idioma-fonte do app resolve os
dois comentarios de uma vez: pt continua com msgstr vazio (convencao
ja usada no resto do catalogo) e en/es passam a ter uma traducao
explicita de verdade.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
- Rotulos das abas: "Artigos Considerados"/"Pacotes validados" viram
  "Artigos"/"Pacotes".
- Ordem visual invertida: "Pacotes" aparece antes de "Artigos".

Porque: rotulos mais curtos foram sugeridos na review do PR scieloorg#37
(pitangainnovare); a ordem foi invertida depois, a pedido do usuario
durante a validacao manual da tela. A aba "Artigos" continua
selecionada por padrao (`aria-selected="true"`) pra preservar o
comportamento do item 9 da issue scieloorg#36 — o resultado da validacao fica
visivel imediatamente apos validar um pacote, sem precisar trocar de
aba; so a posicao visual dos botoes mudou.

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


- msgid "Exceptions" -> "Exceções" (coluna do historico).
- msgid "Artigos Considerados" -> "Artigos" (aba).
- Nova entrada msgid "Pacotes" (aba), referenciando
  index.html:335.

Porque: pt e o idioma-fonte do catalogo (msgstr fica vazio em todas
as entradas, o proprio msgid ja e o texto exibido), entao esse commit
so acompanha as mudancas de msgid feitas em `_history_list.html` e
`index.html` nesta mesma leva de correcoes da review do PR scieloorg#37.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
- msgid "Exceções" -> msgstr "Exceptions".
- msgid "Artigos" -> msgstr "Articles" (era "Articles Considered").
- Nova entrada msgid "Pacotes" -> msgstr "Packages".

Porque: acompanha a mudanca de msgid feita em `_history_list.html`
(coluna de excecoes, que estava sem traducao explicita em ingles) e a
mudanca de rotulo das abas em `index.html`, apontadas na review do
PR scieloorg#37 (pitangainnovare).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
- msgid "Exceções" -> msgstr "Excepciones" (antes "Exceptions",
  sem traducao pro espanhol).
- msgid "Artigos" -> msgstr "Artículos" (era "Artículos
  Considerados").
- Nova entrada msgid "Pacotes" -> msgstr "Paquetes".

Porque: resolve diretamente o comentario da review do PR scieloorg#37
(pitangainnovare) perguntando se "Exceptions" devia ser traduzido no
catalogo es, e acompanha a mudanca de rotulo das abas em
`index.html`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YThTF9o31pFcaYTS6MnhLP
@pitangainnovare
pitangainnovare self-requested a review July 31, 2026 16:40

@pitangainnovare pitangainnovare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entendo que faz sentido ajustar só mais duas coisas:

  1. As abas deveriam representar listas consistentes: atualmente, “Articles” só aparece como detalhe do último pacote selecionado. Sugiro manter “Packages | Articles” sempre visíveis e fazer “Articles” listar todos os snapshots, ordenados pela data da validação — mais recentes primeiro — com as colunas adicionais “Data da validação” e “Pacote”. Ao clicar em um pacote, a interface deveria abrir “Articles” com history_id como filtro ativo, indicando claramente o pacote selecionado e permitindo remover o filtro, o que então passaria a mostrar todos os articles de todos os pacotes. A estrutura atual já suporta isso por meio de um JOIN entre package_article_snapshot e package_validation_history, sem necessidade de migração do banco. Então, precisaria colocar filtros na aba Articles (package, doi, pid, status, etc, veja o que é relevante).
  2. O build Windows ainda usa metadados estáticos de macOS: build_windows.ps1 deveria gerar build_info.py antes do PyInstaller, preenchendo APP_VERSION a partir de pyproject.toml, BUILD_PLATFORM = "Windows" e BUILD_MACOS_VERSION = "development". Recomendo também validar platform.system() == "Darwin" antes de exibir os metadados de macOS, evitando que um arquivo antigo volte a produzir um rodapé incorreto. Idealmente, essa geração deveria ser movida para um script Python multiplataforma compartilhado pelos builds de macOS, Linux e Windows.

Referência para o detalhe 1

Onde está assim:
Image

Poderia ser sempre assim:

Image

Referência para o detalhe 2

A solução correta tem duas camadas:

  1. Gerar build_info.py durante o build Windows.
  2. Impedir defensivamente que metadados de macOS sejam usados fora do macOS.

No build_windows.ps1, antes do PyInstaller:

$BuildInfoPath = Join-Path $RootDir "src\spsvalidator\build_info.py"

$AppVersion = python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])"

@"
APP_VERSION = "$AppVersion"
BUILD_MACOS_VERSION = "development"
BUILD_PLATFORM = "Windows"
"@ | Set-Content -Path $BuildInfoPath -Encoding UTF8

E em build_metadata.py:

if (
    platform.system() == "Darwin"
    and not is_running_from_source()
    and build_info.BUILD_MACOS_VERSION != "development"
    and build_info.BUILD_PLATFORM == "macOS"
):

Rossi-Luciano and others added 9 commits August 2, 2026 12:57
- Adiciona `platform.system() == "Darwin"` a condicao que decide se
  mostra "Compilado para macOS", alem das checagens ja existentes
  (is_running_from_source, BUILD_MACOS_VERSION, BUILD_PLATFORM).

Porque: build_info.py e um arquivo estatico que pode ficar
desatualizado (ex.: herdado de um build anterior de outra plataforma,
como aconteceu com Linux antes do fix fcc8075). Sem essa checagem
extra, um build_info.py com BUILD_PLATFORM="macOS" faria o rodape
mentir sobre a plataforma real sempre que o script de empacotamento de
uma nova plataforma esquecer de regenerar o arquivo - exatamente o
padrao de bug ja visto duas vezes. Apontado na 2a rodada de review do
PR scieloorg#37 (pitangainnovare).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- Porta pro PowerShell a mesma logica de generate_build_info.sh: le a
  versao do pyproject.toml via tomllib e grava build_info.py com
  BUILD_PLATFORM = "Windows" e BUILD_MACOS_VERSION = "development".

Porque: build_windows.ps1 nunca gerava um build_info.py real, entao o
binario Windows empacotava o arquivo estatico versionado no repo (com
valores de macOS) e exibia "Compilado para macOS" incorretamente -
mesmo bug ja corrigido no Linux (fcc8075), agora tambem no Windows.
Apontado na 2a rodada de review do PR scieloorg#37 (pitangainnovare).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- Adiciona a chamada a generate_build_info.ps1 antes do pyinstaller, no
  mesmo ponto em que build_linux.sh/build_macos.sh chamam o script
  equivalente em bash.

Porque: sem essa chamada, o build_info.py gerado pelo script anterior
nunca chega a ser usado - ver commit anterior pro contexto completo do
bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- 3 testes: rodape correto quando roda em macOS de verdade, rodape
  ignora build_info.py com BUILD_PLATFORM="macOS" quando
  platform.system() nao e Darwin, e modo de desenvolvimento.

Porque: fixa em teste o comportamento da checagem defensiva adicionada
no commit anterior, pra nao regredir se alguem mexer nessa funcao no
futuro sem se dar conta do bug que ela previne.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- Novas funcoes espelhando list_validations/count_validations, mas com
  JOIN entre package_article_snapshot e package_validation_history;
  filtros por nome de pacote, doi, pid, status do artigo e history_id.
- get_package_name(): lookup simples pra exibir o nome do pacote quando
  a aba de Artigos estiver filtrada por history_id.

Porque: primeiro passo pra atender a 2a rodada de review do PR scieloorg#37
(pitangainnovare) - a aba "Artigos" precisa listar todos os snapshots
de artigos, nao so os do ultimo pacote selecionado, com filtros
proprios. O schema ja suporta isso sem migracao (history_id ja e chave
estrangeira em package_article_snapshot).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- Testes de join com nome/data do pacote, filtro por history_id, nome
  de pacote, doi, pid, status, paginacao (limit/offset) e contagem.

Porque: fixa em teste o comportamento das funcoes adicionadas no commit
anterior antes de conecta-las as rotas.

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

- Nova `_paginated_articles()` (mesmo padrao de `_paginated_history()`),
  endpoint `/articles-list` pra paginacao/filtro via AJAX, parametros
  de querystring com prefixo `article_` pra nao colidir com os do
  historico de pacotes (q/status/page/page_size).
- `index()` simplificado: nao usa mais get_validation_details/
  latest_result pra decidir o que mostrar; so calcula `default_tab`
  ("articles" quando history_id esta na query, "history" caso
  contrario) - a aba Artigos passa a ser sempre calculada e sempre
  disponivel.
- `history_id` na querystring agora e um filtro da aba Artigos (via
  _paginated_articles), nao mais um gatilho pra carregar um pacote
  especifico.
- Redirect apos validar preserva tambem os parametros article_* (mesma
  logica ja aplicada a q/status/page/page_size).

Porque: pedido explicito da 2a rodada de review do PR scieloorg#37
(pitangainnovare) - "Articles" deve sempre listar todos os snapshots
(JOIN ja existente, sem migracao), ordenados por data, e ao clicar num
pacote deve abrir Artigos filtrado por history_id, com filtro
removivel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
- Espelha _history_list.html: tabela com pacote/data/xml/titulo/
  autores/doi/pid/status, paginacao numerada, mensagem de "nenhum
  artigo encontrado" sensivel aos filtros ativos, e chip "Filtrando
  por pacote: X" com link "Limpar filtro" quando history_id esta
  setado.

Porque: parte do redesenho da aba Artigos pedido na 2a rodada de review
do PR scieloorg#37; usado tanto no carregamento inicial de `/` quanto no
endpoint `/articles-list` (AJAX), igual ao papel de _history_list.html
pro historico de pacotes.

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

- Remove o gate `{% if latest_result %}` que escondia as abas e o
  conteudo de Artigos quando nenhum pacote estava selecionado; a aba
  ativa por padrao agora vem de `default_tab` (calculado nas rotas).
- Adiciona formulario de filtro da aba Artigos (pacote/doi/pid/status/
  itens por pagina), espelhando o formulario ja existente pro
  historico de pacotes.
- Generaliza `bindLiveHistoryFilters()` em `bindLiveFilters(config)`,
  reaproveitado pras duas abas; corrige o bug de `window.history.
  replaceState` sobrescrever a querystring inteira em vez de mesclar -
  cada bloco agora parte de `new URLSearchParams(window.location.
  search)` e so altera os proprios campos, entao filtrar/paginar numa
  aba nao apaga o estado da outra na URL.
- Adiciona estilos `.status-ok`/`.status-issue` (badges da aba Artigos)
  e `.active-filter` (chip de filtro por pacote).

Porque: fecha a 2a rodada de review do PR scieloorg#37 (pitangainnovare) -
"Articles" precisa ser uma lista sempre visivel e completa, nao um
detalhe do ultimo pacote validado. Testado com Playwright headless
contra dados reais (filtro por nome/doi/pid/status, clique em pacote,
limpar filtro, preservacao cruzada de querystring entre as abas) -
screenshots conferidos visualmente antes deste commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015N4tShpu6YUfgyPJR1PKnD
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.

Melhorias de UI/UX no app (validação, histórico e paginação)

3 participants