fix(dashboard): an empty project says it's empty instead of drawing empty charts (BEA-51) - #101
Merged
ssowonny merged 2 commits intoJul 31, 2026
Conversation
…mpty charts (BEA-51)
A brand-new project's Dashboard rendered ~840px of empty bordered SVG
frames with the "hot + stale" / "hot + fresh" / "cold + stale" quadrant
labels floating over nothing: Treemap and Scatter had no empty guard, and
the quadrant labels come from the HOT_READS/STALE_DAYS constants rather
than from data. Only HotPath said anything, and what it said ("No reads in
the window yet") is the wrong claim — the project has no files at all.
The guard goes one level up in Insights, where the three panel headers and
the lens switcher also live, so those go away too instead of sitting over
nothing. Gated on "no files", never on "no reads": files-with-no-reads is
the other zero state and it already behaves correctly (Treemap pads every
file to reads + 1 so unread files keep a sliver).
Two new optional props. `installHref` puts a real <a> in the empty state,
routed through linkProps so it stays copyable and middle-clickable — the
dashboard route passes it, the project home doesn't, because ConnectGuide
directly above it IS the set-up-a-device guide. `loading` keeps the tree's
first frame from claiming a populated project has no files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # internal/webapp/static/assets/index-B9pybzqe.css # internal/webapp/static/assets/index-iGTfiq-J.js # internal/webapp/static/index.html
ssowonny
deleted the
bea-51-ph-scan-bug-knowledge-insights-renders-empty-chart-frames
branch
July 31, 2026 09:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
archiveto chart yet.").orphanslist joins the guard condition. fix(dashboard): keep reads for a deleted file on the map (BEA-49) #100 is still open, so this lands with!scoped.lengthand a comment marking the spot — see Deviation below.Screenshots
Dashboard on a project with no files:
Project home (
/<project-id>) — same message, no second CTA under ConnectGuide:Folder-scoped route with nothing under it (
/<project-id>/dashboard/archive):The populated project, unchanged — this is the "files-but-no-reads is untouched" criterion:
What changed
Insights.tsxrendered its three panels unconditionally.ptsderives fromflatFiles, so a project with no files gavepts = []— and thenTreemapemitted an empty<svg>at its fixed 720×480 viewBox whileScatterdrew its axes, threshold lines and three quadrant labels, all of which come from theHOT_READS/STALE_DAYSconstants rather than from data. OnlyHotPathguarded, and its message ("No reads in the window yet") is the wrong claim for a project that has no files at all.One early return, placed one level up from the charts — where the three
<h3>s and the lens switcher also live, which is what makes "three headers over nothing" go away too.TreemapandScatterkeep no guards of their own.Two new optional props on
Insights:installHref— a real<a>rather than a callback, routed throughnav.ts'slinkPropsso it stays copyable and middle-clickable while still navigating synchronously in-app. The dashboard route passes it; the project home doesn't, sinceConnectGuidedirectly above it is the install guide and a second button six inches under the first reads as two different steps. (This was the plan's flagged judgment call — one prop to flip if you'd rather have it in both places.)loading— not in the plan, and needed.useTreestarts withflatFiles = [], so without it the guard fires on the first frame of every dashboard load and a populated project flashes "This project has no files" before the charts arrive. Verified absent: a polling check during a cold load of the seededwikinever observes.in-blank.Gated on
scoped.length, never onpts.some(p => p.reads)— files-with-no-reads is the other zero state and it was already right (Treemappads every file toreads + 1so unread files keep a sliver).CSS:
.in-blankfor spacing on top of the existing.dl-empty, plustext-decoration: noneadded to the existing.pbtnrule now that it also styles an anchor. No new tokens.Deviation from the plan
The plan's step 1 says land BEA-49 first, because its
orphanslist belongs in this guard (a project with zero files but orphaned heat rows should still chart). #100 is open and unmerged, so this takes the plan's own stated fallback: the condition is!scoped.length, with the comment above it naming BEA-49 and what joins it. The two PRs will conflict inInsights.tsx; whichever merges second re-derives the guard as!scoped.length && !orphans.length.Acceptance
go build ./...,go vet ./...,go test ./...— all pass.npm run e2e— 99 passed, including two new cases indashboard.spec.ts: the empty project (no.in-chart, no "hot + stale" anywhere in the body, message contains "no files", CTA href is/<id>/install) and a no-regression case asserting the seeded project still renders.in-treemapwith no.in-blank. The empty project is created and deleted at runtime — a permanent fixture named beforewikiwould move where the app lands and breakhome.spec.ts.frontend/check-dist.sh—internal/webapp/static is fresh.Architecture changes
None —
Insightsis already inarchitecture/webapp-frontend.mdand no type or relationship moves. Two optional props on an existing component.Build session
(Only works on the machine that ran the build.)