Fleet UI: Host details page empty states#44884
Conversation
|
@claude review |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #44884 +/- ##
==========================================
+ Coverage 66.73% 66.80% +0.07%
==========================================
Files 2675 2693 +18
Lines 215826 216454 +628
Branches 9946 10184 +238
==========================================
+ Hits 144025 144609 +584
- Misses 58703 58746 +43
- Partials 13098 13099 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
WalkthroughThis pull request normalizes empty state handling across host-detail UI components. It adds a 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
@claude review |
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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
`@frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tsx`:
- Around line 46-47: The Add software CTA can be enabled via canAddSoftware but
leave onAddSoftware undefined, causing a clickable button with no action; update
the HostSoftwareLibraryTable component to require both conditions before
enabling the button and binding the handler (i.e., only render an enabled Add
button or attach the onClick when canAddSoftware && typeof onAddSoftware ===
'function'), and similarly apply the same guard for the other Add button/render
block around the code referenced by the 167-173 area so the UI is disabled or
non-interactive if onAddSoftware is not provided.
In `@frontend/pages/hosts/details/HostReportsTab/HostReportsTab.tsx`:
- Around line 76-77: The Schedule CTA currently renders whenever
canScheduleReport is true even if onScheduleReport is absent, producing a no-op
button; update the HostReportsTab render logic to only render (or enable) the
"Schedule a report" button when both canScheduleReport and a callable
onScheduleReport exist (e.g. canScheduleReport && typeof onScheduleReport ===
'function'), and apply the same guard where the CTA is rendered at lines
~261-267 so the button is never clickable without a handler (alternatively make
onScheduleReport required in the props if appropriate).
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a155fb5d-239d-4a31-ad1d-7b02bf201003
📒 Files selected for processing (15)
frontend/components/TableContainer/TableContainer.tsxfrontend/components/TableContainer/TableCount/TableCount.tsxfrontend/components/TableContainer/TableCount/_styles.scssfrontend/components/forms/fields/InputFieldWithIcon/_styles.scssfrontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scssfrontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsxfrontend/pages/hosts/details/HostReportsTab/HostReportsTab.tests.tsxfrontend/pages/hosts/details/HostReportsTab/HostReportsTab.tsxfrontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsxfrontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tests.tsxfrontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tsxfrontend/pages/hosts/details/cards/Policies/HostPolicies.tests.tsxfrontend/pages/hosts/details/cards/Policies/HostPolicies.tsxfrontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsxfrontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx
| canAddSoftware?: boolean; | ||
| onAddSoftware?: () => void; |
There was a problem hiding this comment.
Guard “Add software” CTA against missing click handler
canAddSoftware and onAddSoftware are independently optional, so this can render an enabled button with no action when canAddSoftware === true but onAddSoftware is unset.
💡 Suggested fix
- primaryButton={
- canAddSoftware ? (
+ primaryButton={
+ canAddSoftware && onAddSoftware ? (
<Button onClick={onAddSoftware} type="button">
Add software
</Button>
) : undefined
}Also applies to: 167-173
🤖 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
`@frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tsx`
around lines 46 - 47, The Add software CTA can be enabled via canAddSoftware but
leave onAddSoftware undefined, causing a clickable button with no action; update
the HostSoftwareLibraryTable component to require both conditions before
enabling the button and binding the handler (i.e., only render an enabled Add
button or attach the onClick when canAddSoftware && typeof onAddSoftware ===
'function'), and similarly apply the same guard for the other Add button/render
block around the code referenced by the 167-173 area so the UI is disabled or
non-interactive if onAddSoftware is not provided.
There was a problem hiding this comment.
Leave them optional. It's the right design — you only need onAddSoftware when canAddSoftware is true, and the only caller follows that
contract. Not a bug.
| canScheduleReport?: boolean; | ||
| onScheduleReport?: () => void; |
There was a problem hiding this comment.
Prevent rendering a no-op “Schedule a report” button
The CTA is gated only by canScheduleReport, but onScheduleReport is optional. This can expose a clickable button that does nothing.
💡 Suggested fix
- primaryButton={
- canScheduleReport ? (
+ primaryButton={
+ canScheduleReport && onScheduleReport ? (
<Button onClick={onScheduleReport} type="button">
Schedule a report
</Button>
) : undefined
}Also applies to: 261-267
🤖 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 `@frontend/pages/hosts/details/HostReportsTab/HostReportsTab.tsx` around lines
76 - 77, The Schedule CTA currently renders whenever canScheduleReport is true
even if onScheduleReport is absent, producing a no-op button; update the
HostReportsTab render logic to only render (or enable) the "Schedule a report"
button when both canScheduleReport and a callable onScheduleReport exist (e.g.
canScheduleReport && typeof onScheduleReport === 'function'), and apply the same
guard where the CTA is rendered at lines ~261-267 so the button is never
clickable without a handler (alternatively make onScheduleReport required in the
props if appropriate).
There was a problem hiding this comment.
Same situation — one caller, always passes both. The test (line 110) passes canScheduleReport: false without onScheduleReport, which is why it's optional. Not a bug.
There was a problem hiding this comment.
Pull request overview
This PR updates Host details page tab empty states (Reports, Software inventory/library, and Policies) to align with the “Normalize empty states in Fleet UI” effort, including disabling irrelevant controls in “truly empty” scenarios and adding permission-based CTAs.
Changes:
- Add “truly empty” empty-state UIs for Host Reports, Host Software tables, Host Software Library, and Host Policies, with disabled search/filter/sort controls.
- Add permission-aware CTAs (e.g., “Schedule a report”, “Add software”, “Manage policies”) and wire permissions from
HostDetailsPage. - Extend
TableContainerto support disabling its search input and introduce a styledTableCountcomponent for consistent counts.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scss | Adds disabled styling for the label filter select control. |
| frontend/pages/hosts/details/HostReportsTab/HostReportsTab.tsx | Introduces a new “No reports scheduled” empty state, disables controls when truly empty, and adds optional scheduling CTA. |
| frontend/pages/hosts/details/HostReportsTab/HostReportsTab.tests.tsx | Adds test coverage for new reports empty states and control disabling. |
| frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx | Adds per-host-team permission checks and wires CTAs into Reports/Policies tabs; hides Policies tab on unsupported platforms. |
| frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx | Adds “No software found” truly-empty state; disables search/filter controls when truly empty. |
| frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsx | Updates/adds tests for the new truly-empty vs filtered-empty behavior and disabled controls. |
| frontend/pages/hosts/details/cards/Policies/HostPolicies.tsx | Adds truly-empty policies state with count + optional “Manage policies” CTA; switches to TableCount. |
| frontend/pages/hosts/details/cards/Policies/HostPolicies.tests.tsx | Adds tests for new policies empty state copy/CTA behavior and count rendering. |
| frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tsx | Adds truly-empty library state with optional “Add software” CTA; disables search/sort filter when truly empty. |
| frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibraryTable/HostSoftwareLibraryTable.tests.tsx | Adds tests for new library empty states, CTA visibility, and disabled search. |
| frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsx | Adjusts “Add software” permission to be based on the host’s team (not nav-selected team) and passes CTA props down. |
| frontend/components/TableContainer/TableCount/TableCount.tsx | Adds a class for consistent styling of table counts. |
| frontend/components/TableContainer/TableCount/_styles.scss | Defines .table-count styles. |
| frontend/components/TableContainer/TableContainer.tsx | Adds disableSearch prop to disable the SearchField input. |
| frontend/components/forms/fields/InputFieldWithIcon/_styles.scss | Improves disabled-state icon coloring for inputs with icons. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| info={ | ||
| canScheduleReport | ||
| ? "Select Refetch to load the latest data from this host, or schedule a report." | ||
| : "Select Refetch to load the latest data from this host." | ||
| } | ||
| primaryButton={ | ||
| canScheduleReport ? ( | ||
| <Button onClick={onScheduleReport} type="button"> | ||
| Schedule a report | ||
| </Button> | ||
| ) : undefined |
| <EmptyState | ||
| header="No software found" | ||
| info={ | ||
| canAddSoftware | ||
| ? "Add software to install on this host." | ||
| : "No software has been added for this host." | ||
| } | ||
| primaryButton={ | ||
| canAddSoftware ? ( | ||
| <Button onClick={onAddSoftware} type="button"> | ||
| Add software | ||
| </Button> | ||
| ) : undefined |
| const target = deviceUser ? "your device" : "this host"; | ||
| const manageClause = canManagePolicies | ||
| ? ", or manage its policies." | ||
| : "."; | ||
|
|
||
| return ( | ||
| <EmptyState | ||
| header={ | ||
| <> | ||
| No policies are checked{" "} | ||
| {deviceUser ? `on your device` : `for this host`} | ||
| </> | ||
| } | ||
| info={ | ||
| <> | ||
| Expecting to see policies? Try selecting “Refetch” to ask{" "} | ||
| {deviceUser ? `your device ` : `this host `} | ||
| to report new vitals. | ||
| </> | ||
| } | ||
| /> | ||
| <> | ||
| <TableCount name="policies" count={0} /> | ||
| <EmptyState | ||
| header="No policies checked" | ||
| info={`Select Refetch to load the latest data from ${target}${manageClause}`} | ||
| primaryButton={ | ||
| canManagePolicies ? ( | ||
| <Button onClick={onManagePolicies} type="button"> | ||
| Manage policies | ||
| </Button> | ||
| ) : undefined |
There was a problem hiding this comment.
Yep, same thing. One caller (line 1604-1605), always passes both. Tests pass canManagePolicies: false without the handler. Not a bug. All three of these are the same pattern — the review flagged a theoretical type-level mismatch, but in practice the contract is always followed. These are all fine.
| return ( | ||
| <> | ||
| {renderBanner()} | ||
| <TableContainer | ||
| columnConfigs={tableHeaders} | ||
| data={generatePolicyDataSet(policies, !!conditionalAccessEnabled)} | ||
| isLoading={isLoading} | ||
| defaultSortHeader="status" | ||
| resultsTitle="policies" | ||
| emptyComponent={() => <></>} | ||
| showMarkAllPages={false} | ||
| isAllPagesSelected={false} | ||
| disableCount | ||
| renderCount={() => ( | ||
| <TableCount name="policies" count={policies.length} /> | ||
| )} |
There was a problem hiding this comment.
Refactoring to use emptyComponent={() => <></>} and remove the extra renderCount, PR incoming
|
Followup |
Issue
Closes #35483
Description
Screenrecording
Shows host details page > software library, reports, and policies empty states
Screen.Recording.2026-05-07.at.12.27.35.PM.mov
Testing
Summary by CodeRabbit
Release Notes
New Features
Improvements