Skip to content

Occupancy Page Cross Linking and UI Polish#677

Merged
fernandol-nvidia merged 7 commits intomainfrom
fernandol/occupancy-followups
Mar 11, 2026
Merged

Occupancy Page Cross Linking and UI Polish#677
fernandol-nvidia merged 7 commits intomainfrom
fernandol/occupancy-followups

Conversation

@fernandol-nvidia
Copy link
Contributor

@fernandol-nvidia fernandol-nvidia commented Mar 11, 2026

Description

  • Pools details has quick-link to Occupancy page (with filters)
Screenshot 2026-03-10 at 7 57 33 PM
  • Pool parent rows have "View Workflows" and "View Pool" additional actions
Screenshot 2026-03-10 at 7 55 25 PM
  • User parent rows have "View Workflows"
Screenshot 2026-03-10 at 7 55 50 PM
  • Children rows are clickable links that lead to pre-filtered workflows
Screenshot 2026-03-10 at 10 29 40 PM
  • Occupancy page has status presets
Screenshot 2026-03-10 at 10 28 40 PM

Issue #682

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Occupancy view: dynamic grouping toggle (pool/user), cross-links to related workflows, and quick link from pool details.
    • Search now threads chips throughout occupancy tables and columns for richer navigation.
    • New status preset group for task/workflow status categories.
  • Bug Fixes

    • Explicit search filters now take precedence over opt-out defaults.
  • UI/UX Improvements

    • Toolbar: updated placeholder, presets, and consistent preset styling.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Group-by and search chip state were threaded through occupancy UI: toolbar, column generation, and data table now accept and propagate searchChips and groupBy; URL chip parsing and default-filter semantics were enhanced; status presets and pill styling were centralized; occupancy column defs gained cross-linking and parent-row actions; occupancy API payloads now signal absent filters.

Changes

Cohort / File(s) Summary
Occupancy page & wiring
src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx, src/ui/src/features/occupancy/components/occupancy-data-table.tsx, src/ui/src/features/occupancy/components/occupancy-toolbar.tsx
Thread searchChips and groupBy through page → toolbar → data table; add GroupByToggle and toolbar callbacks; update toolbar props and data table props to accept searchChips; adjust UI composition and placeholders.
Occupancy columns & search fields
src/ui/src/features/occupancy/components/occupancy-column-defs.tsx, src/ui/src/features/occupancy/lib/occupancy-search-fields.ts
createOccupancyColumns now takes searchChips; add cross-link helpers (buildWorkflowsUrl), parent-row actions, and dynamic priority column mapping; replace static OCCUPANCY_SEARCH_FIELDS with getOccupancySearchFields(groupBy).
Filter-bar hooks: URL parsing & defaults
src/ui/src/components/filter-bar/hooks/use-url-chips.ts, src/ui/src/components/filter-bar/hooks/use-default-filter.ts
Support multi-defaults and multi-param URL parsing via a multi-parser; normalize defaultValue to string[]; compute effectiveOptOut (opt-out suppressed by explicit URL chips); write all default chips when prepopulating.
Status presets & grouping
src/ui/src/lib/task-group-status-presets.tsx, src/ui/src/features/workflows/detail/components/panel/core/lib/task-search-fields.tsx
Introduce TASK_STATE_CATEGORIES, TASK_GROUP_STATUS_PRESETS, and TaskStateCategory; migrate from older task preset exports and switch callers to use group-based status presets.
Workflow search fields & preset relocation
src/ui/src/features/workflows/list/lib/workflow-search-fields.ts, src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts
Expose WORKFLOW_FIELD and derive WORKFLOW_STATIC_FIELDS from it; move preset-related exports (e.g., status presets/createPresetChips) to centralized module and update imports in toolbars/tests.
Preset pill styling utility & callers
src/ui/src/components/filter-bar/lib/preset-pill.ts, src/ui/src/features/datasets/list/components/toolbar/datasets-toolbar.tsx, src/ui/src/features/pools/components/pools-toolbar.tsx
Add presetPillClasses utility and replace local implementations with imports; unify pill styling and pass ring class for active state.
Occupancy quick link
src/ui/src/features/pools/components/panel/panel-content.tsx
Add an Occupancy quick link to pools panel linking to /occupancy with pool filter and groupBy=pool.
Workflows & tasks toolbar updates
src/ui/src/features/workflows/list/components/workflows-toolbar.tsx, src/ui/src/features/workflows/detail/components/.../group-tasks-tab.tsx, .../workflow-tasks-table.tsx, .../workflow-tasks-toolbar.tsx
Update imports to use centralized presets and preset pill utility; switch toolbar preset props from old TASK_PRESETS to TASK_GROUP_STATUS_PRESETS.
API adapter: occupancy request shaping
src/ui/src/lib/api/adapter/occupancy-shim.ts
Conditionally include users/pools in request when present; add all_users/all_pools flags to indicate fetching all records when filters absent.
Misc imports & cleanups
various files (toolbars, tests, minor edits)
Refactor imports, remove duplicated helpers, update placeholders and comments to reflect new props and presets.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OccupancyPage
  participant OccupancyToolbar
  participant OccupancyDataTable
  participant ColumnDefs
  participant WorkflowsApp

  User->>OccupancyPage: Open occupancy page (URL with chips)
  OccupancyPage->>OccupancyToolbar: provide `groupBy`, `searchChips`, handlers
  OccupancyToolbar->>OccupancyPage: emit groupBy/searchChips events
  OccupancyPage->>OccupancyDataTable: pass `searchChips`, `groupBy`, data props
  OccupancyDataTable->>ColumnDefs: createOccupancyColumns(groupBy, searchChips)
  ColumnDefs->>OccupancyDataTable: return columns (include workflow/pool links)
  User->>OccupancyDataTable: click "View Workflows"
  OccupancyDataTable->>WorkflowsApp: navigate to built workflows URL (includes chips, groupBy)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐇
I hopped through chips and tidy rows,
Grouped by pool where north wind blows.
Pills aligned, presets aglow,
Links to workflows set to go.
A little hop for code that grows!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Occupancy Page Cross Linking and UI Polish' accurately reflects the main changes: adding cross-linking capabilities (View Workflows/Pool actions, clickable child rows) and UI polish (status presets, quick links) throughout the Occupancy page and related components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fernandol/occupancy-followups

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/ui/src/features/pools/components/panel/panel-content.tsx (1)

191-202: Avoid hand-rolling another filter URL here.

This adds a third inline copy of the f= query serialization in the quick-links card. Please consider pushing these hrefs through a small shared route/filter builder so occupancy, workflows, and resources stay aligned if the chip/query format changes again.

As per coding guidelines, "Single source of truth for each concept - avoid multiple representations of the same data."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/features/pools/components/panel/panel-content.tsx` around lines
191 - 202, Replace the hand-rolled occupancy href that constructs
`?f=pool:${encodeURIComponent(pool.name)}&groupBy=pool` with a call to the
shared route/filter builder (e.g., use or add a helper like buildFilterUrl or
buildOccupancyLink) so the Link uses that single source of truth; update the
Link in panel-content.tsx to call the helper with the filter spec (pool:
pool.name) and groupBy="pool" instead of embedding `f=` manually, ensuring URL
encoding is handled by the shared builder.
src/ui/src/components/filter-bar/hooks/use-default-filter.ts (1)

72-77: Make the “explicit chip wins” rule synchronous.

This only cleans up the URL after commit. On the render where both optOut and an explicit chip are present, the hook still returns optOut=true, so callers can observe contradictory state for one render. Returning an effectiveOptOut = optOut && !hasDefaultInUrl would make the hook’s output match the precedence rule immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts` around lines 72
- 77, The hook useDefaultFilter should synchronously expose the precedence rule
so callers don’t see a transient contradictory state: compute an effectiveOptOut
= optOut && !hasDefaultInUrl and return that instead of raw optOut, while
keeping the existing useEffect to clear the URL (call setOptOut(false)) as a
side-effect when optOut && hasDefaultInUrl; update any return/exports from
useDefaultFilter to use effectiveOptOut so render-time consumers observe the
explicit-chip-wins rule immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/src/components/filter-bar/hooks/use-url-chips.ts`:
- Around line 35-37: The current parseAsChipStrings (created via
createMultiParser) is lossy because parse splits on "," and serialize joins with
","; update parse and serialize to avoid using an unescaped secondary separator.
Specifically, in parseAsChipStrings' parse function stop splitting each entry on
commas and instead treat each incoming string as one chip (e.g., return
values.filter(Boolean) or flatMap only for true multi-entry encodings), and
change serialize to return one array element per chip (e.g., return
Array.from(values) / values.slice()) so each chip is preserved as its own
parameter rather than conflating them with a comma join. Ensure you only
reference parseAsChipStrings, createMultiParser, parse, and serialize when
making the change.

In `@src/ui/src/features/occupancy/components/occupancy-column-defs.tsx`:
- Around line 149-158: The row actions button is invisible to keyboard focus
because it only becomes opaque on hover/open; update the className passed to the
button (the cn(...) call in occupancy-column-defs.tsx for the MoreHorizontal
trigger) to also reveal the control on keyboard focus and show a visible focus
ring—add a focus-visible:opacity-100 and appropriate focus-visible:ring and
focus-visible:outline-none (or your design-system ring classes) alongside the
existing hover/open classes, preserving the original.isExpanded check so
expanded rows remain visible.
- Around line 144-181: Wrap the Radix DropdownMenu in a mounted guard using the
existing useMounted() hook so it only renders on the client; specifically, check
useMounted() before rendering the DropdownMenu (the block that contains
DropdownMenu, DropdownMenuTrigger, DropdownMenuContent and DropdownMenuItem) and
return a fallback/omit the menu when mounted is false following the same pattern
used in theme-toggle.tsx to avoid hydration ID mismatches. Ensure the guard
surrounds the whole DropdownMenu subtree (including the MoreHorizontal trigger
button and Link items) and use the mounted boolean to conditionally render that
subtree.

In `@src/ui/src/lib/workflows/workflow-constants.ts`:
- Around line 92-101: WORKFLOW_FILTER_FIELDS currently includes "status", which
causes invalid deep-links because occupancy chips use TaskGroupStatus values
that don't map to WorkflowStatus; remove "status" from WORKFLOW_FILTER_FIELDS to
stop forwarding raw occupancy status values into workflow URLs, and instead add
a translation step wherever occupancy filters are converted into workflow query
params (map TaskGroupStatus -> WorkflowStatus, e.g., in the function that
constructs workflow URLs or the occupancy-to-workflow forwarding logic). Ensure
the translation explicitly handles unmappable values (drop or normalize them)
and reference TaskGroupStatus and WorkflowStatus in the translator to make the
mapping intent clear.

---

Nitpick comments:
In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts`:
- Around line 72-77: The hook useDefaultFilter should synchronously expose the
precedence rule so callers don’t see a transient contradictory state: compute an
effectiveOptOut = optOut && !hasDefaultInUrl and return that instead of raw
optOut, while keeping the existing useEffect to clear the URL (call
setOptOut(false)) as a side-effect when optOut && hasDefaultInUrl; update any
return/exports from useDefaultFilter to use effectiveOptOut so render-time
consumers observe the explicit-chip-wins rule immediately.

In `@src/ui/src/features/pools/components/panel/panel-content.tsx`:
- Around line 191-202: Replace the hand-rolled occupancy href that constructs
`?f=pool:${encodeURIComponent(pool.name)}&groupBy=pool` with a call to the
shared route/filter builder (e.g., use or add a helper like buildFilterUrl or
buildOccupancyLink) so the Link uses that single source of truth; update the
Link in panel-content.tsx to call the helper with the filter spec (pool:
pool.name) and groupBy="pool" instead of embedding `f=` manually, ensuring URL
encoding is handled by the shared builder.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 380dcf71-54cc-4532-8ce9-5a151213cce2

📥 Commits

Reviewing files that changed from the base of the PR and between 9cc68cd and eaec07e.

📒 Files selected for processing (12)
  • src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx
  • src/ui/src/components/filter-bar/hooks/use-default-filter.ts
  • src/ui/src/components/filter-bar/hooks/use-url-chips.ts
  • src/ui/src/features/occupancy/components/occupancy-column-defs.tsx
  • src/ui/src/features/occupancy/components/occupancy-data-table.tsx
  • src/ui/src/features/occupancy/components/occupancy-toolbar.tsx
  • src/ui/src/features/occupancy/lib/occupancy-search-fields.ts
  • src/ui/src/features/pools/components/panel/panel-content.tsx
  • src/ui/src/features/workflows/list/components/workflows-toolbar.tsx
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.ts
  • src/ui/src/lib/workflows/workflow-constants.ts
💤 Files with no reviewable changes (1)
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (6)
src/ui/src/components/filter-bar/hooks/use-default-filter.ts (1)

105-105: Memoize the hook result.

Line 105 allocates a new object every render, so any consumer that depends on the whole return value will re-run even when effectiveChips, handleChipsChange, and optOut are stable.

♻️ Suggested fix
-  return { effectiveChips, handleChipsChange, optOut: effectiveOptOut };
+  return useMemo(
+    () => ({ effectiveChips, handleChipsChange, optOut: effectiveOptOut }),
+    [effectiveChips, handleChipsChange, effectiveOptOut],
+  );

As per coding guidelines, "Memoize returned objects in hooks to prevent cascading re-renders when used as React Query keys (use useMemo for objects)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts` at line 105,
The hook currently returns a newly allocated object each render which causes
consumers to re-run; wrap the returned object in useMemo so it only changes when
its dependencies change (memoize the object containing effectiveChips,
handleChipsChange, and optOut derived from effectiveOptOut) — replace the plain
return of { effectiveChips, handleChipsChange, optOut: effectiveOptOut } with a
useMemo that lists [effectiveChips, handleChipsChange, effectiveOptOut] as
dependencies to ensure a stable reference from use-default-filter.ts.
src/ui/src/features/datasets/list/components/toolbar/datasets-toolbar.tsx (1)

54-63: Avoid hard-coding DATASET_STATIC_FIELDS by index.

This works today, but the toolbar is now coupled to the array’s export order. Adding or reordering a static dataset field will silently change which fields show up here; a named field map or explicit destructuring would make this safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/features/datasets/list/components/toolbar/datasets-toolbar.tsx`
around lines 54 - 63, The toolbar currently builds searchFields by hard-indexing
DATASET_STATIC_FIELDS, coupling UI to the array order; update the useMemo in
datasets-toolbar.tsx (searchFields) to select fields explicitly instead of by
index — either destructure named exports or filter DATASET_STATIC_FIELDS by an
explicit list of field keys/names (e.g., ["title","description","..."]) and
include userField, so reordering the export won't break the toolbar; ensure the
dependency array still includes userField.
src/ui/src/features/workflows/list/lib/workflow-search-fields.ts (1)

24-24: Keep WORKFLOW_FIELD key-safe.

Line 24 widens this object to Record<string, ...>, so typos like WORKFLOW_FIELD.priorty still type-check and only fail later as undefined. Use explicit keys or satisfies here so the keyed-map refactor actually preserves compile-time safety.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/features/workflows/list/lib/workflow-search-fields.ts` at line 24,
The WORKFLOW_FIELD object is typed as Record<string,
SearchField<WorkflowListEntry>> which permits typos like WORKFLOW_FIELD.priorty;
change its declaration to enforce exact keys by replacing the broad
Record<string,...> with a concrete key type or use TypeScript's satisfies
operator so the object literal is checked against an explicit key union (e.g. a
WorkflowFieldKey union) or a typed mapped type; update the declaration of
WORKFLOW_FIELD to use that exact-key type (and keep Object.freeze) so misspelled
property access fails at compile time rather than runtime, referencing
WORKFLOW_FIELD, SearchField and WorkflowListEntry in the fix.
src/ui/src/features/occupancy/lib/occupancy-search-fields.ts (1)

1-15: Copyright header format inconsistency.

Same issue as noted in occupancy-page-content.tsx - missing space after // in SPDX lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/features/occupancy/lib/occupancy-search-fields.ts` around lines 1
- 15, Update the SPDX license header comments in occupancy-search-fields.ts to
match the project's comment style by ensuring there is a single space after the
comment markers (i.e., change lines starting with "//SPDX" to "// SPDX" and
similarly add a space after "//" on all SPDX-related lines) so the header
formatting matches occupancy-page-content.tsx and other files.
src/ui/src/features/occupancy/components/occupancy-toolbar.tsx (1)

1-15: Copyright header format inconsistency.

Same issue as noted in other occupancy files - consider normalizing the format.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/features/occupancy/components/occupancy-toolbar.tsx` around lines
1 - 15, Replace the inconsistent copyright header in occupancy-toolbar.tsx with
the project-standard SPDX header used across other occupancy files; ensure it
matches the exact formatting (including SPDX-License-Identifier line and license
text sequence) used in files like other occupancy components so the header is
normalized across the codebase.
src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx (1)

1-15: Copyright header format inconsistency.

The copyright header uses //SPDX-FileCopyrightText: without a space after // and uses "NVIDIA CORPORATION." without "& AFFILIATES". Compare with other files in this PR (e.g., task-search-fields.tsx) which use the standard format:

// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved.

This appears to be a pre-existing issue in this file. Consider normalizing to match the standard format used elsewhere. As per coding guidelines: "Copyright headers must keep 'All rights reserved.' on the same line as 'NVIDIA CORPORATION & AFFILIATES'".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/app/`(dashboard)/occupancy/occupancy-page-content.tsx around lines
1 - 15, Replace the malformed SPDX header lines "//SPDX-FileCopyrightText:" and
"//SPDX-License-Identifier:" with the standard formatted header: add a space
after the comment slashes and update the copyright line to include "NVIDIA
CORPORATION & AFFILIATES. All rights reserved." on the same line (e.g., "//
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All
rights reserved."); keep the SPDX-License-Identifier line similarly formatted
("// SPDX-License-Identifier: Apache-2.0").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/src/components/filter-bar/hooks/use-url-chips.ts`:
- Around line 35-38: The current eq function in use-url-chips.ts only checks set
membership and thus treats arrays with different duplicate counts as equal;
change eq to compare multisets by counting occurrences: if lengths differ return
false, build frequency maps (or sort) for the two arrays and ensure every
element has the same count in both; update the function named eq to use these
count comparisons so duplicates are honored and URL/UI state stays in sync.

In `@src/ui/src/components/filter-bar/lib/preset-pill.ts`:
- Around line 1-15: Replace the current top-of-file comment block with the
standard NVIDIA Apache-2.0 SPDX header used for src/ui/src TypeScript files:
include the copyright line "Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
All rights reserved." on a single line, the Apache License 2.0 notice, the URL
http://www.apache.org/licenses/LICENSE-2.0, and the SPDX-License-Identifier:
Apache-2.0 token; ensure the entire header mirrors the project’s canonical
header format for ui TS/TSX files so the SPDX/license block and "All rights
reserved." placement match policy.

In `@src/ui/src/features/occupancy/components/occupancy-column-defs.tsx`:
- Around line 33-35: The CROSS_LINKABLE_FIELDS constant currently hard-codes
only ["pool","user","priority"], dropping other active occupancy chips (e.g.,
"status") when building the workflows link; replace this ad-hoc allow-list with
a single-source mapping or reuse the centralized translation function/mapping
used for occupancy→workflow filters (do not hard-code here), ensure all
applicable occupancy filter keys are forwarded or translated (including status)
when building the workflows URL, and remove the duplicate logic at the other
occurrence (lines referenced 84-87) so both places call the same shared
mapping/translator (refer to CROSS_LINKABLE_FIELDS and the code that constructs
the workflows link).
- Around line 104-109: The placeholder button and the mounted menu trigger both
use a generic aria-label "Row actions"; update their accessible names to include
the row identifier by interpolating original.key into the aria-label (e.g.
aria-label={`Row actions ${original.key}`}) so screen readers get row-specific
context; ensure you change the disabled placeholder button element and the
mounted trigger component that renders the real menu trigger (both referencing
original.key) to use the new aria-label format.

In `@src/ui/src/lib/task-group-status-presets.tsx`:
- Around line 1-15: Replace the current top-of-file comment block in
task-group-status-presets.tsx with the standard NVIDIA Apache-2.0 SPDX header: a
copyright line that reads "Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
All rights reserved." on the same line, followed by the full Apache License,
Version 2.0 notice and the SPDX-License-Identifier: Apache-2.0 line; ensure the
block replaces the existing lines 1–15 so the file conforms to
src/ui/src/**/*.{ts,tsx} header requirements.

---

Nitpick comments:
In `@src/ui/src/app/`(dashboard)/occupancy/occupancy-page-content.tsx:
- Around line 1-15: Replace the malformed SPDX header lines
"//SPDX-FileCopyrightText:" and "//SPDX-License-Identifier:" with the standard
formatted header: add a space after the comment slashes and update the copyright
line to include "NVIDIA CORPORATION & AFFILIATES. All rights reserved." on the
same line (e.g., "// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA
CORPORATION & AFFILIATES. All rights reserved."); keep the
SPDX-License-Identifier line similarly formatted ("// SPDX-License-Identifier:
Apache-2.0").

In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts`:
- Line 105: The hook currently returns a newly allocated object each render
which causes consumers to re-run; wrap the returned object in useMemo so it only
changes when its dependencies change (memoize the object containing
effectiveChips, handleChipsChange, and optOut derived from effectiveOptOut) —
replace the plain return of { effectiveChips, handleChipsChange, optOut:
effectiveOptOut } with a useMemo that lists [effectiveChips, handleChipsChange,
effectiveOptOut] as dependencies to ensure a stable reference from
use-default-filter.ts.

In `@src/ui/src/features/datasets/list/components/toolbar/datasets-toolbar.tsx`:
- Around line 54-63: The toolbar currently builds searchFields by hard-indexing
DATASET_STATIC_FIELDS, coupling UI to the array order; update the useMemo in
datasets-toolbar.tsx (searchFields) to select fields explicitly instead of by
index — either destructure named exports or filter DATASET_STATIC_FIELDS by an
explicit list of field keys/names (e.g., ["title","description","..."]) and
include userField, so reordering the export won't break the toolbar; ensure the
dependency array still includes userField.

In `@src/ui/src/features/occupancy/components/occupancy-toolbar.tsx`:
- Around line 1-15: Replace the inconsistent copyright header in
occupancy-toolbar.tsx with the project-standard SPDX header used across other
occupancy files; ensure it matches the exact formatting (including
SPDX-License-Identifier line and license text sequence) used in files like other
occupancy components so the header is normalized across the codebase.

In `@src/ui/src/features/occupancy/lib/occupancy-search-fields.ts`:
- Around line 1-15: Update the SPDX license header comments in
occupancy-search-fields.ts to match the project's comment style by ensuring
there is a single space after the comment markers (i.e., change lines starting
with "//SPDX" to "// SPDX" and similarly add a space after "//" on all
SPDX-related lines) so the header formatting matches occupancy-page-content.tsx
and other files.

In `@src/ui/src/features/workflows/list/lib/workflow-search-fields.ts`:
- Line 24: The WORKFLOW_FIELD object is typed as Record<string,
SearchField<WorkflowListEntry>> which permits typos like WORKFLOW_FIELD.priorty;
change its declaration to enforce exact keys by replacing the broad
Record<string,...> with a concrete key type or use TypeScript's satisfies
operator so the object literal is checked against an explicit key union (e.g. a
WorkflowFieldKey union) or a typed mapped type; update the declaration of
WORKFLOW_FIELD to use that exact-key type (and keep Object.freeze) so misspelled
property access fails at compile time rather than runtime, referencing
WORKFLOW_FIELD, SearchField and WorkflowListEntry in the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bfc58f61-317d-4052-b5a5-bbc3cce84ea3

📥 Commits

Reviewing files that changed from the base of the PR and between eaec07e and 5fa577f.

📒 Files selected for processing (18)
  • src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx
  • src/ui/src/components/filter-bar/hooks/use-default-filter.ts
  • src/ui/src/components/filter-bar/hooks/use-url-chips.ts
  • src/ui/src/components/filter-bar/lib/preset-pill.ts
  • src/ui/src/features/datasets/list/components/toolbar/datasets-toolbar.tsx
  • src/ui/src/features/occupancy/components/occupancy-column-defs.tsx
  • src/ui/src/features/occupancy/components/occupancy-data-table.tsx
  • src/ui/src/features/occupancy/components/occupancy-toolbar.tsx
  • src/ui/src/features/occupancy/lib/occupancy-search-fields.ts
  • src/ui/src/features/pools/components/pools-toolbar.tsx
  • src/ui/src/features/workflows/detail/components/panel/core/lib/task-search-fields.tsx
  • src/ui/src/features/workflows/detail/components/panel/ui/group/group-tasks-tab.tsx
  • src/ui/src/features/workflows/detail/components/panel/ui/table/workflow-tasks-table.tsx
  • src/ui/src/features/workflows/detail/components/panel/ui/table/workflow-tasks-toolbar.tsx
  • src/ui/src/features/workflows/list/components/workflows-toolbar.tsx
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.ts
  • src/ui/src/lib/task-group-status-presets.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts

@fernandol-nvidia fernandol-nvidia enabled auto-merge (squash) March 11, 2026 05:56
@fernandol-nvidia fernandol-nvidia changed the title Occupancy Page UX Polish Occupancy Page Cross Linking and UI Polish Mar 11, 2026
cypres
cypres previously approved these changes Mar 11, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/ui/src/lib/api/adapter/occupancy-shim.ts (1)

155-160: Only send all_users / all_pools when they are enabled (true).

The backend treats omission and explicit false identically via FastAPI's default parameter handling. Conditional omission is cleaner and better documents intent. Match the pattern in workflows-shim.ts:110 and align with "only when necessary."

Proposed change
   const response = await listTaskApiTaskGet({
     summary: true,
     limit: MAX_SUMMARY_ROWS,
     ...(hasUserFilter ? { users: params.users } : {}),
     ...(hasPoolFilter ? { pools: params.pools } : {}),
     ...(params.priorities && params.priorities.length > 0 ? { priority: params.priorities } : {}),
     ...(params.statuses && params.statuses.length > 0 ? { statuses: params.statuses } : {}),
-    all_pools: !hasPoolFilter,
-    all_users: !hasUserFilter,
+    ...(!hasPoolFilter ? { all_pools: true } : {}),
+    ...(!hasUserFilter ? { all_users: true } : {}),
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/lib/api/adapter/occupancy-shim.ts` around lines 155 - 160, The
object currently always includes all_pools and all_users (set to !hasPoolFilter
/ !hasUserFilter); change this so the properties are only added when true (i.e.,
when !hasPoolFilter is true add { all_pools: true }, and when !hasUserFilter is
true add { all_users: true }) following the same conditional-spread pattern used
above (use hasPoolFilter, hasUserFilter and params.users/params.pools as
reference points) so omission and explicit false are not sent to the backend.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/src/lib/api/adapter/occupancy-shim.ts`:
- Around line 155-160: The object currently always includes all_pools and
all_users (set to !hasPoolFilter / !hasUserFilter); change this so the
properties are only added when true (i.e., when !hasPoolFilter is true add {
all_pools: true }, and when !hasUserFilter is true add { all_users: true })
following the same conditional-spread pattern used above (use hasPoolFilter,
hasUserFilter and params.users/params.pools as reference points) so omission and
explicit false are not sent to the backend.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d7cc0eb-a40c-439c-b6e0-284c79b0955d

📥 Commits

Reviewing files that changed from the base of the PR and between 87e382c and 5b6bf8a.

📒 Files selected for processing (1)
  • src/ui/src/lib/api/adapter/occupancy-shim.ts

cypres
cypres previously approved these changes Mar 11, 2026
@github-actions
Copy link

github-actions bot commented Mar 11, 2026

📖 Docs preview: https://d3in15bfzp49i0.cloudfront.net/677/index.html

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx (2)

149-157: ⚠️ Potential issue | 🟡 Minor

Include the new table inputs in the boundary reset keys.

OccupancyDataTable now depends on groupBy and searchChips, but the boundary still only resets on groups.length. If a render error happens for one filter state and the next state has the same row count, the fallback will stay stuck.

Possible fix
         <InlineErrorBoundary
           title="Unable to display occupancy table"
-          resetKeys={[groups.length]}
+          resetKeys={[groupBy, groups.length, JSON.stringify(searchChips)]}
           onReset={refetch}
         >
As per coding guidelines, "Use component-level error boundaries for independent data sources. Wrap with InlineErrorBoundary with descriptive titles, onReset, and resetKeys."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/app/`(dashboard)/occupancy/occupancy-page-content.tsx around lines
149 - 157, The InlineErrorBoundary wrapping OccupancyDataTable currently only
uses resetKeys={[groups.length]}, so errors won't reset when table inputs
change; update the resetKeys to include the new inputs that affect rendering
(e.g., groupBy and searchChips) so the boundary will remount when those change —
locate the InlineErrorBoundary around OccupancyDataTable and change resetKeys to
include groups.length, groupBy, and searchChips (or stable identifiers derived
from them) while keeping onReset={refetch} and the same title.

73-81: ⚠️ Potential issue | 🟡 Minor

Prune expanded keys when the visible group set changes.

This only resets on groupBy, so a status/search change can leave stale keys from now-hidden groups in expandedKeys. That makes allExpanded drift out of sync with the rows currently on screen.

Possible fix
+  const visibleGroupKeys = useMemo(() => new Set(groups.map((group) => group.key)), [groups]);
+
   const expandedKeys = useMemo(
-    () => (expandedState.groupBy === groupBy ? expandedState.keys : new Set<string>()),
-    [expandedState, groupBy],
+    () => {
+      if (expandedState.groupBy !== groupBy) {
+        return new Set<string>();
+      }
+
+      return new Set([...expandedState.keys].filter((key) => visibleGroupKeys.has(key)));
+    },
+    [expandedState, groupBy, visibleGroupKeys],
   );
As per coding guidelines, "Single source of truth for each concept - avoid multiple representations of the same data."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/app/`(dashboard)/occupancy/occupancy-page-content.tsx around lines
73 - 81, expandedKeys can contain stale keys because you only reset when groupBy
changes; update the logic that derives expandedKeys to prune any keys not
present in the current visible group set (e.g. from the current
rows/status/search) so expandedState.keys stays in sync with what's actually
rendered. Concretely, in the code surrounding expandedState / setExpandedState
and the computed expandedKeys, filter expandedState.keys against the
currentVisibleGroupKeys (compute from the current rows or groupRows selector)
whenever those visible groups change (add that dependency), or update
setExpandedState when visibility changes to remove keys that are no longer
present.
🧹 Nitpick comments (1)
src/ui/src/components/filter-bar/hooks/use-default-filter.ts (1)

78-103: Code duplication: defaults array is constructed identically in two places.

The same mapping logic to create the defaults array appears in both effectiveChips (lines 80-85) and the prepopulate effect (lines 97-102). Extract this to a single memoized value to keep logic consistent and reduce duplication.

♻️ Proposed refactor to extract defaults computation
+  const defaultChips = useMemo(
+    (): SearchChip[] =>
+      normalizedDefaults.map((v) => ({
+        field,
+        value: v,
+        label: label ?? `${field}: ${v}`,
+      })),
+    [field, normalizedDefaults, label],
+  );
+
   const effectiveChips = useMemo((): SearchChip[] => {
     if (!shouldPrePopulate) return searchChips;
-    const defaults: SearchChip[] = normalizedDefaults.map((v) => ({
-      field,
-      value: v,
-      label: label ?? `${field}: ${v}`,
-    }));
-    return [...searchChips, ...defaults];
-  }, [searchChips, shouldPrePopulate, field, normalizedDefaults, label]);
+    return [...searchChips, ...defaultChips];
+  }, [searchChips, shouldPrePopulate, defaultChips]);

   // ... opt-out effect unchanged ...

   useEffect(() => {
     if (!shouldPrePopulate) return;
-    const defaults: SearchChip[] = normalizedDefaults.map((v) => ({
-      field,
-      value: v,
-      label: label ?? `${field}: ${v}`,
-    }));
-    void setSearchChips([...searchChips, ...defaults], { history: "replace" });
-  }, [shouldPrePopulate, searchChips, field, normalizedDefaults, label, setSearchChips]);
+    void setSearchChips([...searchChips, ...defaultChips], { history: "replace" });
+  }, [shouldPrePopulate, searchChips, defaultChips, setSearchChips]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts` around lines 78
- 103, Extract the duplicated defaults mapping into a single memoized value and
use it in both places: create a const like computedDefaults = useMemo(() =>
normalizedDefaults.map(v => ({ field, value: v, label: label ?? `${field}: ${v}`
})), [normalizedDefaults, field, label]) and replace the inline `defaults` in
effectiveChips and the prepopulate useEffect with computedDefaults; ensure the
dependencies arrays for effectiveChips and the prepopulate effect reference
computedDefaults instead of normalizedDefaults/field/label to avoid missing deps
and keep behavior identical (references: effectiveChips, setSearchChips,
normalizedDefaults, setOptOut).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts`:
- Around line 80-84: The current mapping in use-default-filter.ts assigns the
same custom label to every chip when defaultValue is an array; change the label
construction so the value is always included when there are multiple defaults:
inside the mapping that builds defaults from normalizedDefaults, use the
provided label (or field when label is undefined) as a prefix and append `:
${v}` for each chip when normalizedDefaults.length > 1 (but preserve the
original single-chip behavior when there's only one default), referencing the
normalizedDefaults array, the defaults mapping, field, label, and the SearchChip
shape.

---

Outside diff comments:
In `@src/ui/src/app/`(dashboard)/occupancy/occupancy-page-content.tsx:
- Around line 149-157: The InlineErrorBoundary wrapping OccupancyDataTable
currently only uses resetKeys={[groups.length]}, so errors won't reset when
table inputs change; update the resetKeys to include the new inputs that affect
rendering (e.g., groupBy and searchChips) so the boundary will remount when
those change — locate the InlineErrorBoundary around OccupancyDataTable and
change resetKeys to include groups.length, groupBy, and searchChips (or stable
identifiers derived from them) while keeping onReset={refetch} and the same
title.
- Around line 73-81: expandedKeys can contain stale keys because you only reset
when groupBy changes; update the logic that derives expandedKeys to prune any
keys not present in the current visible group set (e.g. from the current
rows/status/search) so expandedState.keys stays in sync with what's actually
rendered. Concretely, in the code surrounding expandedState / setExpandedState
and the computed expandedKeys, filter expandedState.keys against the
currentVisibleGroupKeys (compute from the current rows or groupRows selector)
whenever those visible groups change (add that dependency), or update
setExpandedState when visibility changes to remove keys that are no longer
present.

---

Nitpick comments:
In `@src/ui/src/components/filter-bar/hooks/use-default-filter.ts`:
- Around line 78-103: Extract the duplicated defaults mapping into a single
memoized value and use it in both places: create a const like computedDefaults =
useMemo(() => normalizedDefaults.map(v => ({ field, value: v, label: label ??
`${field}: ${v}` })), [normalizedDefaults, field, label]) and replace the inline
`defaults` in effectiveChips and the prepopulate useEffect with
computedDefaults; ensure the dependencies arrays for effectiveChips and the
prepopulate effect reference computedDefaults instead of
normalizedDefaults/field/label to avoid missing deps and keep behavior identical
(references: effectiveChips, setSearchChips, normalizedDefaults, setOptOut).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8022e46-cc76-4741-9bac-423443b0fdd3

📥 Commits

Reviewing files that changed from the base of the PR and between 5b6bf8a and c4bb9e7.

📒 Files selected for processing (2)
  • src/ui/src/app/(dashboard)/occupancy/occupancy-page-content.tsx
  • src/ui/src/components/filter-bar/hooks/use-default-filter.ts

@fernandol-nvidia fernandol-nvidia merged commit 4cc76c2 into main Mar 11, 2026
8 checks passed
@fernandol-nvidia fernandol-nvidia deleted the fernandol/occupancy-followups branch March 11, 2026 18:57
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.

3 participants