You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Como usuário do dashboard de organização (/[tenant]/dashboard, a home do app pós-login), eu quero que a página apareça em partes — shell primeiro, cada seção depois — para não olhar uma tela branca enquanto a query mais lenta da página termina.
Hoje a home é um único Server Component que dá await em todas as consultas antes de renderizar qualquer coisa: o TTFB da página inteira é o da query mais lenta, e o loading.tsx da rota é um skeleton genérico de página cheia que não corresponde ao layout real (ele desenha 6 hero cards + 4 KPIs + um bloco + um split, e o resto das ~14 seções aparece de uma vez, com salto de layout).
Acceptance criteria
A home renderiza o shell (nome da org, contagem de repos, WindowSelector) sem esperar as consultas das seções — o shell aguarda no máximo uma consulta leve, a que a contagem do header e o empty state precisam.
Cada seção da home resolve de forma independente, dentro do seu próprio <Suspense>, e não bloqueia as seções abaixo.
Cada fallback tem a forma aproximada da seção que substitui (linha de hero cards, grid de KPIs, bloco de gráfico, split de dois blocos), de modo que a seção resolvendo não empurre o conteúdo abaixo mais do que o necessário.
Seções que normalmente não têm conteúdo (ex.: alertas de mudança, onde a maioria das orgs tem zero) não exibem skeleton — nada de alerta-fantasma piscando a cada load.
loading.tsx da rota cobre apenas o shell, já que a página faz streaming e cada seção carrega o seu próprio fallback.
Consultas compartilhadas entre seções são feitas uma vez por request (dedupe), não uma vez por seção.
Empty state (org com 0 repos) e a restrição de visibilidade por role (owner/admin) continuam funcionando como hoje.
Sem regressão visual no conteúdo já resolvido: as mesmas seções, na mesma ordem, com os mesmos dados da janela de análise selecionada.
Scope / non-goals
Dentro:
platform/src/app/[tenant]/dashboard/ — page.tsx, loading.tsx, extração dos loaders de dados e das seções em módulos próprios.
Fallbacks de Suspense reutilizáveis para as formas de seção que se repetem na home.
Fora:
Outras rotas (/repos, /compare, /ai-exposure, /team, /settings) — todas já têm loading.tsx; revisar os fallbacks delas é trabalho separado.
A landing page pública (src/app/page.tsx) — estática, sem consultas, não precisa de estado de loading.
Otimizar as queries em si (índices, novas tabelas pré-agregadas). Aqui o ganho é de ordem de renderização, não de custo de consulta.
Skeletons animados customizados / novos componentes de UI além do Skeleton que já existe em @/components/ui/skeleton.
Se encaixa em qual estágio?
Stage 2 (refinamento da plataforma atual)
Implementation notes
Rota: platform/src/app/[tenant]/dashboard/page.tsx (~14 seções: org pulse, delivery quality, DORA, AI vs human, AI delivery timeline, tool comparison, AI agent usage, intent distribution, PR health, cycle time, health map, org timeline, hyper engineers, change alerts).
Padrão: um módulo por seção em ./panels/*.tsx, cada um dando await só nos loaders que usa; loaders em ./data.ts envolvidos em cache() do React para deduplicar por request (várias seções compartilham o mesmo payload — sem dedupe seriam N round-trips idênticos). Todos os callers precisam passar o mesmo par (orgId, windowDays), já que a memoização é por identidade de argumento.
Fallbacks compartilhados em ./panels/skeletons.tsx: linha de hero cards, grid de KPIs (n cards), seção simples (heading + bloco), seção split (heading + dois blocos).
<Suspense fallback={null}> para os alertas de mudança.
User story
Como usuário do dashboard de organização (
/[tenant]/dashboard, a home do app pós-login), eu quero que a página apareça em partes — shell primeiro, cada seção depois — para não olhar uma tela branca enquanto a query mais lenta da página termina.Hoje a home é um único Server Component que dá
awaitem todas as consultas antes de renderizar qualquer coisa: o TTFB da página inteira é o da query mais lenta, e oloading.tsxda rota é um skeleton genérico de página cheia que não corresponde ao layout real (ele desenha 6 hero cards + 4 KPIs + um bloco + um split, e o resto das ~14 seções aparece de uma vez, com salto de layout).Acceptance criteria
WindowSelector) sem esperar as consultas das seções — o shell aguarda no máximo uma consulta leve, a que a contagem do header e o empty state precisam.<Suspense>, e não bloqueia as seções abaixo.loading.tsxda rota cobre apenas o shell, já que a página faz streaming e cada seção carrega o seu próprio fallback.Scope / non-goals
Dentro:
platform/src/app/[tenant]/dashboard/—page.tsx,loading.tsx, extração dos loaders de dados e das seções em módulos próprios.Fora:
/repos,/compare,/ai-exposure,/team,/settings) — todas já têmloading.tsx; revisar os fallbacks delas é trabalho separado.src/app/page.tsx) — estática, sem consultas, não precisa de estado de loading.Skeletonque já existe em@/components/ui/skeleton.Se encaixa em qual estágio?
Stage 2 (refinamento da plataforma atual)
Implementation notes
platform/src/app/[tenant]/dashboard/page.tsx(~14 seções: org pulse, delivery quality, DORA, AI vs human, AI delivery timeline, tool comparison, AI agent usage, intent distribution, PR health, cycle time, health map, org timeline, hyper engineers, change alerts)../panels/*.tsx, cada um dandoawaitsó nos loaders que usa; loaders em./data.tsenvolvidos emcache()do React para deduplicar por request (várias seções compartilham o mesmo payload — sem dedupe seriam N round-trips idênticos). Todos os callers precisam passar o mesmo par(orgId, windowDays), já que a memoização é por identidade de argumento../panels/skeletons.tsx: linha de hero cards, grid de KPIs (n cards), seção simples (heading + bloco), seção split (heading + dois blocos).<Suspense fallback={null}>para os alertas de mudança.?window=, issue [FEAT] Janelas de análise selecionáveis (7/15/30/60/90 dias) em todo o produto #80) é resolvida no shell e passada para as seções — nenhuma seção deve reresolver a janela por conta própria.src/app/[tenant]/repos/loading.tsx,src/app/[tenant]/compare/loading.tsx.