Skip to content

fix(frontend): a section says which kind of empty it is - #2445

Merged
dzarlax merged 3 commits into
constructorfabric:mainfrom
dzarlax:fix/section-says-which-absence
Aug 12, 2026
Merged

fix(frontend): a section says which kind of empty it is#2445
dzarlax merged 3 commits into
constructorfabric:mainfrom
dzarlax:fix/section-says-which-absence

Conversation

@dzarlax

@dzarlax dzarlax commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

A section with nothing in it can mean two opposite things:

  • the sources behind it are connected and this person did none of that
    work in the period — a fact about them, worth asking about;
  • nothing feeds the section at all — a fact about the install, and
    nobody's performance.

The distinction exists (partState in lib/insight/coverage.ts) and the
sections page already draws it for the whole list via PersonCoverage.
It was lost in the two places a reader meets one section at a time.

The section screen

SingleGroupView asked groupHasData — whether any metric of the group
holds a value for this person — and rendered one sentence for both
answers. Opening the one section a reader cared enough to click threw
away what the page they came from had told them.

It now reads the same tenant-wide definitions listing the section
navigation already asks for (so it is served from cache) and says which
absence it is. Two consequences worth stating:

  • the listing is part of the answer, not decoration, so the screen waits
    for it rather than showing one sentence and swapping it for the other;
  • when that request fails it claims neither absence. With no listing
    every section looks unreachable, and announcing that nothing is
    measured here for anyone on the strength of a request that never
    arrived is the same class of fabrication the coverage helper exists to
    avoid.

The navigation mark

The sections nav drew one grey dot for both, so the distinction was
invisible until the section was opened. The standings hook has computed
peersHaveData for exactly this since the coverage work landed and
nothing read it.

A filled grey dot now means the section reads and holds nothing for this
person this period. A hollow ring means nothing feeds it — not worth
opening at all until that changes. The tooltip says which.

Copy

State Section screen
Sources connected, person absent Nothing recorded here for the selected period.
Nothing feeds the section No data reaches us for this section — nothing is measured here for anyone yet.
Listing could not be read Nothing to show here for the selected period.

The first two reuse the vocabulary PersonCoverage already uses on the
sections page, so the two screens do not describe the same state in
different words.

Verification

Four new tests: the section screen names each of the three states, and
the nav marks the two empties apart. Full unit suite green (1087),
typecheck and lint clean.

Summary by CodeRabbit

  • New Features

    • Added clearer section indicators distinguishing unavailable data from sections where a person has no data.
    • Added context-specific tooltips and empty-state messages for missing, unreachable, loading, and unavailable metrics.
    • Added retry support when metric data cannot be loaded.
  • Bug Fixes

    • Improved section rendering while metric definitions load or cannot be retrieved.
    • Clarified no-data messaging for individual reporting periods.
    • Prevented status indicators from appearing for pending sections.

Alexey Panfilov added 2 commits August 11, 2026 20:47
A section a person did none of, and a section nothing feeds, are
opposite findings: the first is about them, the second is about the
install and is nobody's performance. The sections page already draws
that line for the whole list, but the section screen itself asked only
whether any metric held a value, so both collapsed into "Nothing
recorded here for the selected period" — sending a reader to look for
missing work that was never being measured.

It now reads the same tenant-wide listing the section navigation
already asks for, and says which of the two it is. That listing is part
of the answer rather than decoration, so the screen waits for it instead
of showing one sentence and swapping it for the other; and when the
request fails it claims neither absence, because with no listing every
section looks unreachable.

Signed-off-by: Alexey Panfilov <Alexey.Panfilov@constructor.tech>
The section navigation drew one grey dot for both kinds of empty, so the
distinction the screen behind it now makes was invisible until the
reader opened the section. The standings hook has carried the flag that
tells them apart since the coverage work landed; nothing read it.

A grey dot now means the section reads and holds nothing for this person
this period. A hollow ring means nothing feeds it — not worth opening at
all until that changes. The tooltip says which.

Signed-off-by: Alexey Panfilov <Alexey.Panfilov@constructor.tech>
@dzarlax
dzarlax requested a review from a team as a code owner August 11, 2026 18:47
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bec0e894-bdf5-4496-888f-a131101511df

📥 Commits

Reviewing files that changed from the base of the PR and between 3fa3688 and cce2a43.

📒 Files selected for processing (2)
  • src/frontend/src/components/portal/context-pane.test.tsx
  • src/frontend/src/components/portal/context-pane.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/frontend/src/components/portal/context-pane.tsx
  • src/frontend/src/components/portal/context-pane.test.tsx

📝 Walkthrough

Walkthrough

The portal now uses metric reachability and peer data to distinguish unavailable sections, person-specific empty results, pending sections, and populated sections.

Changes

Section no-data states

Layer / File(s) Summary
Metric definition loading and reachability
src/frontend/src/components/portal/single-group-view.tsx, src/frontend/src/components/portal/single-group-view.test.tsx
SingleGroupView loads tenant metric definitions, waits for them before rendering, and derives reachable metrics. Tests cover loading, error, and default definition states.
Single-group empty-state rendering
src/frontend/src/components/portal/single-group-view.tsx, src/frontend/src/components/portal/single-group-view.test.tsx
The view distinguishes definition failures, unreachable sections, and period-specific no-data results. Tests verify each message and preserve drilldown behavior for readable sections.
Context navigation status indicators
src/frontend/src/components/portal/context-pane.tsx, src/frontend/src/components/portal/context-pane.test.tsx
ContextPane uses peer availability to show distinct tooltips and markers for person-only empty sections, unavailable sections, populated sections, and pending sections.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SingleGroupView
  participant MetricDefinitions
  participant CoverageHelpers
  participant SectionUI
  SingleGroupView->>MetricDefinitions: request tenant metric definitions
  MetricDefinitions-->>SingleGroupView: return definitions, loading, or error
  SingleGroupView->>CoverageHelpers: evaluate reachable metrics and section data
  CoverageHelpers-->>SingleGroupView: return coverage state
  SingleGroupView->>SectionUI: render empty state or drilldown
Loading

Possibly related PRs

Suggested reviewers: hello1101n

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: distinguishing and communicating the type of empty section state.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/frontend/src/components/portal/context-pane.test.tsx (1)

184-187: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the exact hollow-marker styling.

The test accepts any border class. A different filled background could still pass. Assert the intended border-muted-foreground/40 class.

Suggested assertion
-    expect(mark.className).toContain("border");
+    expect(mark.className).toContain("border-muted-foreground/40");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/components/portal/context-pane.test.tsx` around lines 184 -
187, Update the marker styling assertion in the relevant test to require the
exact border-muted-foreground/40 class instead of the generic border class,
while preserving the existing check that the marker lacks bg-muted-foreground.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/src/components/portal/context-pane.tsx`:
- Around line 526-531: Update the standing tooltip expression in the context
pane to prioritize standing.phrase whenever standing.isPending is true, before
evaluating hasData, while preserving the existing non-pending fallback behavior.
Add a title assertion in the context-pane tests verifying that pending standings
display standing.phrase.

---

Nitpick comments:
In `@src/frontend/src/components/portal/context-pane.test.tsx`:
- Around line 184-187: Update the marker styling assertion in the relevant test
to require the exact border-muted-foreground/40 class instead of the generic
border class, while preserving the existing check that the marker lacks
bg-muted-foreground.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 89600010-e74a-429c-be25-390172108909

📥 Commits

Reviewing files that changed from the base of the PR and between cb35079 and 3fa3688.

📒 Files selected for processing (4)
  • src/frontend/src/components/portal/context-pane.test.tsx
  • src/frontend/src/components/portal/context-pane.tsx
  • src/frontend/src/components/portal/single-group-view.test.tsx
  • src/frontend/src/components/portal/single-group-view.tsx

Comment on lines 526 to 531
standing?.hasData !== false
? standing?.phrase
: standing.peersHaveData
? "No data this period"
: "No data reaches us for this section"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -A18 -B5 'isPending:[[:space:]]*true' src/frontend/src/components/portal/context-pane.test.tsx

Repository: constructorfabric/insight

Length of output: 543


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '500,540p' src/frontend/src/components/portal/context-pane.tsx
sed -n '130,215p' src/frontend/src/components/portal/context-pane.test.tsx
rg -n -A12 -B8 'title=|standing\?\.phrase|No data this period|No data reaches us' src/frontend/src/components/portal/context-pane.tsx src/frontend/src/components/portal/context-pane.test.tsx

Repository: constructorfabric/insight

Length of output: 11120


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '540,590p' src/frontend/src/components/portal/context-pane.tsx
rg -n -A12 -B12 'isPending' src/frontend/src/components/portal/context-pane.tsx src/frontend/src/queries src/frontend/src/api

Repository: constructorfabric/insight

Length of output: 39532


Preserve the pending tooltip state.

When standing.isPending is true, use standing.phrase before evaluating hasData. Otherwise, the pending state can show "No data this period" while the marker is hidden. Add a title assertion to src/frontend/src/components/portal/context-pane.test.tsx.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/components/portal/context-pane.tsx` around lines 526 - 531,
Update the standing tooltip expression in the context pane to prioritize
standing.phrase whenever standing.isPending is true, before evaluating hasData,
while preserving the existing non-pending fallback behavior. Add a title
assertion in the context-pane tests verifying that pending standings display
standing.phrase.

Both coverage flags read false while the standings queries are in
flight, so the section tooltip fell through to the strongest of the
three claims — that nothing feeds this section — on an answer the hook
had not given yet. The mark beside the row is already hidden for that
reason; the words now follow it.

Signed-off-by: Alexey Panfilov <Alexey.Panfilov@constructor.tech>
@dzarlax

dzarlax commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in cce2a43, and the case is worse than described: while the
queries are in flight peersHaveData reads false too, so the tooltip
did not merely show "No data this period" — it showed "No data reaches
us for this section", the strongest of the three claims, on an answer
the hook had not given.

Falling back to standing.phrase would not have covered it either: the
phrase is derived from empty rank counts at that moment, so it is
whatever sectionStandingPhrase returns for nothing. A pending row now
carries no title at all, which is what the hidden mark beside it already
meant.

The pending test asserts the absent title.

@dzarlax
dzarlax enabled auto-merge August 12, 2026 08:11
@dzarlax
dzarlax added this pull request to the merge queue Aug 12, 2026
Merged via the queue into constructorfabric:main with commit 24af9aa Aug 12, 2026
56 checks passed
@dzarlax
dzarlax deleted the fix/section-says-which-absence branch August 12, 2026 08:19
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.

2 participants