[🛠] Arrumar a confusão do painel de estatísticas do blog
✨ Resumo do GPT-5.5
Registro de como corrigi a página pública de estatísticas que misturava visitas e visualizações, separando visitas por sessão de 30 minutos de visualizações por abertura de página em um painel legível também no celular.
Logo depois de criar a página pública de estatísticas do blog, uma parte já me incomodava.
Os números apareciam.
Mas os nomes deles se misturavam fácil demais.
Visitantes, visitas, visualizações, sessões, page views. Na tela de um blog parecem parecidos, mas no modelo de contagem são coisas diferentes. Se um contador público deixa isso nebuloso, os números ficam difíceis de confiar mesmo quando renderizam corretamente.
No começo parecia suficiente mostrar “visualizações de hoje”, “visualizações deste mês” e “visualizações totais”. Mas o que eu queria na barra lateral era mais próximo de quantas visitas o blog recebeu hoje, não quantas vezes um post foi aberto.

Então separei o modelo.
Visitas
-> sessões com timeout de 30 minutos de inatividade
Visualizações
-> número de aberturas de páginas ou posts

4943 não era contagem de visitas
A primeira coisa a corrigir era o significado do baseline.
O valor 4943 importado do Google Analytics era screenPageViews. Em termos simples, eram visualizações de página. Mostrar isso como visitantes ou visitas totais seria errado.
Por isso arrumei o Worker e a documentação juntos.
totalPageViews
-> GA screenPageViews baseline + D1 page views
totalSessions
-> GA sessions baseline + D1 sessions
Visitas e visualizações não podem usar o mesmo baseline. Adicionei uma rota separada para semear o baseline de sessões.
/admin/seed-session-baseline
Também coloquei essa etapa no fluxo de deploy do Worker no GitHub Actions. Se o Worker fosse publicado e o baseline ficasse vazio, o total público de visitas pareceria quebrado.
Usar a resposta de /track imediatamente
A primeira estrutura dava uma volta.
O cliente enviava /track para contar e depois lia /analytics de novo para preencher a tela. Com cache e ordem de requisições, a visita recém-contada podia não aparecer de imediato.
Agora /track devolve também o payload mais recente de analytics.
Carregamento da página
-> POST /track
-> Worker atualiza sessão/visualizações
-> a mesma resposta inclui analytics atualizado
-> cliente renderiza barra lateral e visualização da página atual
As atualizações periódicas continuam lendo /analytics.
A primeira tela usa o valor recém-atualizado. Depois disso, a API de leitura mantém a interface atualizada.
Uma sessão não deve aumentar a cada troca de página
Visualizações podem aumentar quando o leitor passa de um post para outro.
Visitas são diferentes. Se uma pessoa lê três posts na mesma sessão do blog, isso não deve virar três visitas. Adicionei uma tabela sessions ao Worker e conectei sessões por visitor hash e último horário de atividade.
O limite é 30 minutos.
Dentro de 30 minutos desde a última atividade
-> mesma sessão
Depois de mais de 30 minutos
-> nova sessão
Assim, as visitas de hoje/mês/total na barra lateral oscilam menos que page views. As visualizações por post continuam usando o page path.
As páginas multilíngues seguem a mesma regra.
/daily-review/.../ e /en/daily-review/.../ são versões do mesmo post. A chave de visualização usa o canonical path, e mudar de idioma dentro da mesma sessão não cria outra visita.
Transformar o painel em algo mais parecido com tabela
A primeira página de estatísticas era centrada em cartões KPI e listas com barras.
Não era ruim, mas quando visitas e visualizações passaram a aparecer juntas, a estrutura ficou ambígua. Mais cartões deixavam a tela mobile comprida e dificultavam ver qual número era visita e qual era visualização em cada período.
Por isso transformei a comparação superior em algo mais próximo de uma tabela.

Período Visitas Visualizações
Intervalo n n
Hoje n n
Este mês n n
Total n n
O total também precisava de duas linhas.
Total
-> rastreamento direto desde 2026/06/09
Total
-> inclui estatísticas antes de 2026/06/09
São valores com significados diferentes.
O rastreamento direto é o valor apoiado pelas dimensões em D1 desde o início da coleta pública. O total com histórico soma o baseline do GA. Misturar tudo numa linha deixa o número maior, mas ele não bate com as dimensões detalhadas.
Também separei o texto de resumo.
Visitas: exclui retornos dentro de 30 min. Visualizações: aberturas de página.
O rastreamento direto começa em 2026/06/09.
Tornar a entrada de datas menos irritante
Em uma página de estatísticas, datas mudam o tempo todo.
No começo pensei que range=today, range=month, range=total e range=custom bastavam. Mas intervalos diretos exigem editar início e fim várias vezes.
Então deixei os inputs mais parecidos com uma pequena ferramenta.
formato YYYY/MM/DD
date picker escondido
seleção de segmento ano/mês/dia
ajuste com ArrowUp / ArrowDown
ajuste com roda do mouse
clamp entre início e fim
Visualmente continuam sendo dois inputs pequenos, mas mudar datas ficou menos cansativo.
Também parei de esconder o intervalo customizado até apertar um botão. Os inputs ficam sempre visíveis, e os presets só movem as datas para o intervalo correspondente.
Isso ficou mais previsível.
Separar dimensões detalhadas em abas
Mostrar todas as dimensões de uma vez bagunçava a página.
Páginas, ambiente do visitante, origem de tráfego, região e idioma são úteis, mas juntos numa tela só ficam difíceis de ler. Agrupei tudo em abas.
Páginas
Ambiente do visitante
Tráfego
Região/idioma
Paths podem ficar longos, então adicionei title aos rótulos e ajustei o CSS para wrapping e truncamento não quebrarem o layout. Valores de página que começam com / viraram links.
O objetivo não era um dashboard chamativo.
Eu queria abrir no celular e ver rápido quais posts estavam sendo lidos, quantas visitas vieram hoje e quantas visualizações foram contadas.
Ajustar também os rótulos multilíngues
A página de estatísticas não existe só em coreano.
Também há páginas ativas como /en/analytics/, /ja/analytics/ e /zh-Hans/analytics/. Rótulos como “visitas”, “visualizações”, “sessões de 30 minutos” e “visualizações GA + D1” precisavam mudar no front matter de cada locale.
Se isso fica de fora, a estrutura muda, mas textos antigos permanecem em outros idiomas.
Num blog multilíngue, corrigir uma tela não é corrigir uma única página. O significado dos rótulos também precisa acompanhar.
O que verifiquei
Verifiquei principalmente renderização e significado das métricas.
node --check cloudflare/ga-stats-worker.js
node --check assets/js/custom/analytics-dashboard.js
node --check assets/js/custom/visitor-stats.js
bundle exec jekyll build
Cloudflare Worker deploy
GitHub Pages deploy
O critério foi este.
A barra lateral mostra sessions como visitas
Visualizações por post continuam sendo page views
Visitas totais e visualizações totais usam baselines diferentes
Total direto e total com GA ficam separados
Entrada de datas não conflita com today/month/total
A tabela período/visitas/visualizações não transborda no mobile
Depois disso, a página de estatísticas ficou mais silenciosa.
Há mais números, mas eles confundem menos. Visita é visita, visualização é visualização. Total é total, mas total direto e total com histórico são diferentes.
O ponto não era fazer os números parecerem maiores.
Era não esconder o que cada número significa.
Deixe um comentário