Skip to content

[DEV-IMP] Dashboard UI Enhancements and Performance Improvements#87

Open
moda20 wants to merge 9 commits intomasterfrom
Improvement/dashboard-ui-element-improvements
Open

[DEV-IMP] Dashboard UI Enhancements and Performance Improvements#87
moda20 wants to merge 9 commits intomasterfrom
Improvement/dashboard-ui-element-improvements

Conversation

@moda20
Copy link
Copy Markdown
Owner

@moda20 moda20 commented Apr 2, 2026

Improvements:

  • Updated the dashboard metric card to display job metrics separately
  • Implemented virtualization for jobs table to improve performance with large datasets
  • Implemented virtualization for job event drawer to handle long lists efficiently
  • Updated advanced filtering dialog to use regex by default for most inputs

Notes

  • [UI]: Added proper z-index to sidebar to resolve layering issues
  • [UI] : Added notification service list scrolling
  • [Note] (performance) : The virtualizer on the job event drawer needs to be manually refreshed with a setTimeout as it doesn't refresh correctly when the full list of events is updated via the event hook

Motivation and Context

How Has This Been Tested?

Screenshots (if applicable)

Add screenshots to help explain your changes.

Related Issues

Additional Context

Summary by CodeRabbit

  • New Features

    • Virtualized list rendering for faster, smoother scrolling with large datasets.
    • Metrics dashboard refreshed with clearer sections and integrated error indicators.
  • Improvements

    • More consistent borders, spacing and iconography across pickers, popovers and panels.
    • Better responsive layouts and refined transitions for sheets and tables.
    • Loading overlay added to content regions for clearer loading states.
  • Bug Fixes

    • Date/timestamp values normalized for more reliable display.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Implements list virtualization for event and table views, refactors dashboard metric UI and input control behavior, adds metric design tokens and border/z-index tweaks, and adjusts various layout, sizing, and data-shaping changes across multiple UI components.

Changes

Cohort / File(s) Summary
List Virtualization
src/components/custom/DrawerJobEvents.tsx, src/features/jobsTable/jobsTable.tsx
Replaced non-virtual rendering with useVirtualizer flows: DrawerJobEvents now concatenates latestEvents+events as a single virtualized list and measures rows; jobsTable adds TableBodyVirtualized, tableContainerRef scroll handling, and absolute-positioned translated rows.
Dashboard Metrics & Theme Tokens
src/components/custom/dashboard/statsBar.tsx, src/styles/global.css, tailwind.config.ts
Added six metric color CSS vars and Tailwind tokens; refactored stat cards into MetricSection with unified error handling, icon mapping, and responsive dividers.
Input Controls / Filtering
src/components/custom/general/MultiTypedInput.tsx, src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
Made FlexibleInput type controlled/optional with conditional type picker, memoized handlers, and effect to sync controlled type; advancedJobFilteringDialog now extracts nested .value fields and sets FlexibleInput to type="regex".
UI Borders, Z-Index & Small Styling
src/components/ui/date-picker-presets.tsx, src/components/ui/sidebar.tsx
Added explicit border-2 border-border to popover/select/calendar elements; increased sidebar root z-index to z-50.
Drawer & Notifications Layout
src/components/custom/DrawerLatestRuns.tsx, src/components/custom/notifications/NotificationServicesPanel.tsx
Fixed state setter naming; adjusted fetch error shape; shortened sheet transition duration and altered error item styling; set left column height and scroll behavior for notification services panel.
Table Columns & Page Layout
src/features/jobsTable/interfaces.tsx, src/features/jobsTable/jobsPage.tsx
Added explicit size/minSize constraints to multiple columns and centered action cell; changed sentinel height for intersection observer and adjusted sticky header padding; DataTable receives optional size prop.
Log Files & Cleanup
src/components/custom/general/LogFileList.tsx, src/features/system/database.tsx
Changed createAt to ISO string when date present (otherwise undefined); removed unused icon/card imports.
Date Picker Presets
src/components/ui/date-picker-presets.tsx
Select/popover/calendar elements receive explicit 2px border classes (visual refinement).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through lists both long and wide,
Merged events to dance and rows to glide,
Colored metrics warmed my burrow bright,
Inputs tuned to behave just right,
Borders stacked and tables leapt with pride. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately summarizes the main changes: dashboard UI enhancements (metrics card redesign, stat card updates, styling improvements) and performance improvements (virtualization in jobs table and event drawer).

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Improvement/dashboard-ui-element-improvements

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 and usage tips.

@moda20 moda20 changed the title [MAIN-Feat] Dashboard UI Enhancements and Performance Improvements [DEV-IMP] Dashboard UI Enhancements and Performance Improvements Apr 2, 2026
Copy link
Copy Markdown

@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: 16

Caution

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

⚠️ Outside diff range comments (1)
src/components/custom/general/LogFileList.tsx (1)

28-46: ⚠️ Potential issue | 🟠 Major

Complete the useMemo dependency list to avoid stale props.

props.originName and props.downloadLogFile are used inside the memoized callback but omitted from the dependency array. When these optional props change, the memoized FileList will not be recalculated, causing stale values in the rendered items.

💡 Proposed fix
-  }, [props.logFiles, props.readLogFile])
+  }, [
+    props.logFiles,
+    props.readLogFile,
+    props.downloadLogFile,
+    props.originName,
+  ])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/LogFileList.tsx` around lines 28 - 46, The
memoized FileList created by useMemo currently lists [props.logFiles,
props.readLogFile] but omits props.originName and props.downloadLogFile, causing
stale values when those props change; update the useMemo dependency array for
the FileList variable (the useMemo call that builds FileItemProps) to include
props.originName and props.downloadLogFile along with props.logFiles and
props.readLogFile so the memo recalculates when any of those inputs change.
🤖 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/components/custom/dashboard/statsBar.tsx`:
- Around line 148-173: The two MetricSection bindings are inconsistent: change
the Scheduled Jobs MetricSection to read the scheduled count from jobMetrics
(use jobMetrics.scheduled_jobs_count) instead of jobMetrics.running_jobs_count,
and change the Running Jobs MetricSection to use the snake_case running count
(jobMetrics.running_jobs_count) instead of jobMetrics.runningJobsCount so both
metrics match the payload's snake_case keys used elsewhere (e.g., job_count).
- Around line 47-50: The runtime-built Tailwind class `${bgColorLight}/5` in the
className passed to cn prevents Tailwind from generating the background opacity
utility; change the component API to accept a fully-qualified tint class (e.g.,
"bg-metric1-light/5") instead of bgColorLight and use that prop directly in the
className; update references to the prop name (e.g., tintClass or bgTint) in the
component where cn is used (and anywhere StatsBar is instantiated) and remove
any runtime string interpolation that appends "/5", preserving the hasError
branch that uses "bg-destructive/5".

In `@src/components/custom/DrawerJobEvents.tsx`:
- Around line 74-83: The virtualizer is using index-based identity which causes
remounts when latestEvents are prepended; update the useVirtualizer call that
creates rowVirtualizer to include a getItemKey function that returns a stable
unique identifier from each event object in allEvents (e.g., event.id or
event.eventId, falling back to a composite of timestamp+type only if no stable
id exists) so items keep stable identity when latestEvents is prepended and you
can remove the timeout remount workaround; reference the useVirtualizer call
(rowVirtualizer) and the allEvents/latestEvents arrays when implementing this
change.

In `@src/components/custom/DrawerLatestRuns.tsx`:
- Around line 141-144: The code dereferences the resolved value of
getLatestRuns() (and similarly getMoreRuns()) without guarding against its
error-path fallback (which can resolve to []). Update the promise handlers for
getLatestRuns() and getMoreRuns() so they first check the resolved value and
provide a safe default (e.g., an object with data: [] and total: 0) before
calling setLogItems and setItemsTotal; alternatively, destructure with defaults
or early-return when data is falsy so the drawer falls back to the empty state
instead of crashing. Ensure you modify the promise then() callbacks where
setLogItems and setItemsTotal are invoked and handle both success shape and the
error fallback consistently.

In `@src/components/custom/general/LogFileList.tsx`:
- Line 36: The createAt value is being set using logFile.date?.toString(), which
yields a decimal string that moment() may misparse; update the code that assigns
createAt (used by FileItem) to pass a parse-safe ISO or numeric timestamp
instead—e.g., convert the numeric Unix timestamp to a Number or an ISO string
before assigning (use logFile.date as a Number or new
Date(logFile.date).toISOString()), so FileItem/moment() receives a reliably
parseable date; locate the createAt assignment in LogFileList and replace the
toString() usage accordingly.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Line 57: The change removed the default for the MultiTypedInput "type" prop
which unintentionally exposes the mode picker (see MultiTypedInput component
around the mode-picker logic at lines ~225-235) and allows "exact"/"regex"
values for fields like averageTime; to fix, restore the previous default type
behavior in MultiTypedInput (so the picker is hidden and "between"-style remains
the only selection) or update callers that expect only "between" (notably
advancedJobFilteringDialog's averageTime usage) to explicitly pass
type="between" or acceptedInputTypes={["between"]} so the UI cannot produce
invalid payloads.
- Around line 136-149: The handleRegexChange callback currently only reports
validation errors via onError when validateRegex(newValue) returns an error, but
never clears a previous error when the pattern becomes valid; update
handleRegexChange (and use validateRegex) so that after computing error you call
onError?.({ name }, error) when error exists and otherwise call onError?.({ name
}, undefined) (or null per your onError contract) to clear the stale error, then
proceed to setRegexValue and onChange as before.
- Around line 153-160: handleBetweenChange closes over the stale betweenValue;
change it to use a functional state update and remove betweenValue from the
dependency array: inside handleBetweenChange (the function defined with
useCallback) call setBetweenValue(prev => { const updated = { ...prev, [field]:
newValue, type: "between" }; onChange?.(updated); return updated; }) and keep
only onChange and setBetweenValue in the useCallback deps so the callback never
reads a stale betweenValue.
- Around line 102-117: handleTypeChange currently resets state and emits
payloads with the wrong shape when a controlled type prop is synced, and its
dependencies are incomplete; change the sync effect that reads prop `type` to
set `inputType` directly (do not call handleTypeChange on mount/prop-sync) so it
won’t reset values or emit mismatched payloads, and update handleTypeChange to
emit type-correct payloads (include the proper `type` field for exact/regex and
`{ type: "between", value1, value2 }` for between) when invoked by user actions;
also add the missing dependencies: include `name` in handleTypeChange, include
`betweenValue` in handleBetweenChange, and add `inputType` and
`handleTypeChange` to the effect that syncs controlled `type` prop so hooks
don’t use stale closures.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 218-220: The current code returns the full FlexibleInput objects
(e.g., values.name, values.cronSetting, values.consumer) which contain {value,
type} instead of scalar strings, causing advancedFilters to propagate nested
objects into JobsService (affecting filterJobs and exportJobsToJSON); change the
assignments so they return the scalar text (e.g., use values.name?.value ?
values.name.value : undefined for name, and likewise for cronSetting and
consumer) so advancedFilters sends plain strings to the service.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx`:
- Line 385: The details pane wrapper in NotificationServicesPanel (the div with
class "flex flex-col gap-2 overflow-y-auto") needs a bounded height so it
becomes the scroll container; update that element to include a height constraint
(e.g., add "h-full" or "min-h-0") in its class list so it doesn't auto-size and
the overflow-y works, and if the Spinner component wraps children with its own
container apply the same "min-h-0" constraint to the Spinner wrapper so the
scroll behavior remains contained.

In `@src/components/ui/date-picker-presets.tsx`:
- Line 147: The border classes are on the wrong element; remove "border-2
border-border" from the SelectValue className and add those classes to the
SelectTrigger className so the trigger renders the control chrome while
SelectValue keeps only text/placeholder styles (e.g., keep "bg-background
text-foreground" on SelectValue and ensure SelectTrigger receives "border-2
border-border" alongside its existing classes).

In `@src/components/ui/sidebar.tsx`:
- Line 225: The desktop sidebar's root element currently uses the class string
"group peer hidden md:block text-sidebar-foreground z-50" which conflicts with
modal/sheet/dropdown z-50 layers; update the sidebar's className in
src/components/ui/sidebar.tsx (the element with that exact string) to use a
lower z-index (e.g., z-40 or remove the z-* utility) so dialogs/sheets/menus
(which use z-50) deterministically render above the Sidebar.

In `@src/features/jobsTable/interfaces.tsx`:
- Around line 113-116: The flex container currently has the truncation class
which won't ellipsize because the flex item needs a shrinkable basis; update the
JSX so the outer div keeps "flex gap-2 text-[13px] items-center font-light"
(remove "truncate") and add "min-w-0" to that outer div, then move "truncate"
(and optionally "overflow-hidden" and "whitespace-nowrap" if used elsewhere) to
the consumer text node that renders row.original.consumer (the element alongside
<FileArchive />) so long consumer names properly ellipsize.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 71-75: The empty-state branch currently applies min-h-[800px] when
data.length === 0 causing an oversized blank panel; change the conditional class
logic inside the component that builds className (the cn call using size and
data.length) to apply a bounded, compact empty-state height (for example replace
"min-h-[800px]" with a smaller bounded class such as "min-h-[160px] h-auto
max-h-[40vh] flex items-center justify-center" or similar design-system
empty-state classes) so the table stays within the viewport-bounded area and
visually shows a no-results state; make the same adjustment to the other
identical block that handles the empty rows (the second cn/className usage
around the lines referenced).

In `@src/hooks/useJobEvents.tsx`:
- Around line 122-125: The callbacks in useJobEvents.tsx (notably readEvents
plus the callbacks at the .finally block and the other two callbacks referenced)
close over a stale changeCounter; replace setChangeCounter(changeCounter + 1)
with the functional updater setChangeCounter(c => c + 1) in each location so
updates are always based on the latest state, and ensure readEvents and the
other callbacks no longer rely on changeCounter in their dependency arrays (use
the functional updater instead of adding changeCounter to deps); look for the
symbol readEvents and the three occurrences of setChangeCounter in the file and
update them to use the functional form.

---

Outside diff comments:
In `@src/components/custom/general/LogFileList.tsx`:
- Around line 28-46: The memoized FileList created by useMemo currently lists
[props.logFiles, props.readLogFile] but omits props.originName and
props.downloadLogFile, causing stale values when those props change; update the
useMemo dependency array for the FileList variable (the useMemo call that builds
FileItemProps) to include props.originName and props.downloadLogFile along with
props.logFiles and props.readLogFile so the memo recalculates when any of those
inputs change.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a765adcb-6c53-4c4e-b723-c7a62adadd23

📥 Commits

Reviewing files that changed from the base of the PR and between 10e8378 and 00cb33e.

📒 Files selected for processing (15)
  • src/components/custom/DrawerJobEvents.tsx
  • src/components/custom/DrawerLatestRuns.tsx
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/LogFileList.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
  • src/components/custom/notifications/NotificationServicesPanel.tsx
  • src/components/ui/date-picker-presets.tsx
  • src/components/ui/sidebar.tsx
  • src/features/jobsTable/interfaces.tsx
  • src/features/jobsTable/jobsPage.tsx
  • src/features/jobsTable/jobsTable.tsx
  • src/hooks/useJobEvents.tsx
  • src/styles/global.css
  • tailwind.config.ts

Copy link
Copy Markdown

@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: 7

Caution

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

⚠️ Outside diff range comments (2)
src/components/custom/jobsTable/advancedJobFilteringDialog.tsx (1)

301-308: ⚠️ Potential issue | 🟠 Major

FlexibleInput with type="regex" will reset pre-filled values on mount.

Setting type="regex" triggers the useEffect in MultiTypedInput.tsx (lines 162-166) which calls handleTypeChange, resetting all internal state and emitting an empty value. If the form is opened with existing filter values, they will be silently wiped.

This is a downstream effect of the issue in MultiTypedInput.tsx. Once the root cause is fixed there (by syncing inputType directly instead of calling handleTypeChange), this usage will work correctly.

Also applies to: 321-328, 341-348

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

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx` around lines
301 - 308, The FlexibleInput usages (e.g., the instance with type="regex" in
advancedJobFilteringDialog) are getting their pre-filled values wiped because
MultiTypedInput.tsx's useEffect invokes handleTypeChange (lines around 162-166),
which resets internal state and emits an empty value; fix MultiTypedInput by
syncing the internal inputType state directly from props (set inputType =
props.type or similar) in that effect instead of calling handleTypeChange so you
don't reset other internal state or call the value-emitting logic; ensure
handleTypeChange remains the explicit user-triggered transition and that
existing field.value is preserved and not overwritten when the component mounts.
src/features/jobsTable/jobsTable.tsx (1)

78-102: ⚠️ Potential issue | 🟠 Major

Remove w-full from body cells or replace flex-grow with flex-shrink-0 in both headers and body to enforce strict column sizing.

Body cells (lines 185-187) have conflicting width constraints: w-full conflicts with the inline style={{ width: cell.column.getSize() }}. Headers lack w-full, creating inconsistency. Combined with mixed column sizing (some columns use fixed size, others use only minSize), the flex-grow property allows flex-basis to override explicit widths, causing subtle misalignment.

Remove w-full from body cells to match headers, or use flex-shrink-0 in both to enforce strict sizing without proportional expansion.

Also applies to: 181-193

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

In `@src/features/jobsTable/jobsTable.tsx` around lines 78 - 102, Table cells are
misaligning because body cells use the tailwind class "w-full" while headers use
"flex flex-grow" and widths are set via
header.column.getSize()/cell.column.getSize(), causing flex-basis to be
overridden; remove "w-full" from the body cell elements (the TableCell elements
that use style={{ width: cell.column.getSize() }}) so they mirror the TableHead
layout, or alternatively change both header and body containers from "flex-grow"
to "flex-shrink-0" (update classNames on TableRow/TableHead and the
corresponding TableCell) to enforce strict sizing based on column.getSize().
♻️ Duplicate comments (7)
src/components/custom/general/MultiTypedInput.tsx (4)

112-117: ⚠️ Potential issue | 🟠 Major

Emitted payloads have incorrect type field.

When newType is "regex", line 114 emits defaultMultiTypeNullValue which hardcodes type: "exact" (see line 28). The emitted object's type should match newType.

Suggested fix
      // Emit initial value for new type
      if (newType === "exact" || newType === "regex") {
-       onChange?.(defaultMultiTypeNullValue)
+       onChange?.({ value: "", type: newType })
      } else if (newType === "between") {
-       onChange?.({ value1: "", value2: "" })
+       onChange?.({ value1: "", value2: "", type: "between" })
      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 112 - 117,
The emitted payload uses defaultMultiTypeNullValue which hardcodes type:"exact",
so when newType is "regex" (or other types) the emitted object's type is wrong;
update the emission logic in the onChange block (the code referencing newType,
defaultMultiTypeNullValue, and onChange) to emit an object whose type field
equals newType (e.g., construct { type: newType, value: ... } or clone
defaultMultiTypeNullValue but set type: newType) and for "between" continue to
emit { value1: "", value2: "" } as before.

162-166: ⚠️ Potential issue | 🔴 Critical

Controlled type prop causes value reset on mount.

When a parent passes type="regex" along with a pre-filled value (as in advancedJobFilteringDialog.tsx lines 301-308), this useEffect fires on mount and calls handleTypeChange(type). That handler resets all internal state (lines 107-109) and emits an empty value via onChange, silently wiping the pre-filled field.value.

Synchronize inputType directly without invoking the reset logic:

Suggested fix
  useEffect(() => {
-   if (type) {
-     handleTypeChange(type)
+   if (type && type !== inputType) {
+     setInputType(type)
    }
- }, [type, handleTypeChange])
+ }, [type, inputType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 162 - 166,
The useEffect currently calls handleTypeChange(type) on mount which triggers the
reset logic (clearing internal state and calling onChange); instead, update the
internal inputType state directly without invoking reset/emit: in the component
MultiTypedInput, replace calling handleTypeChange from the useEffect with a
direct state sync (e.g., setInputType(type) or the existing inputType setter) so
the controlled prop synchronizes the UI but does not call handleTypeChange or
emit an empty value; alternatively, add an optional flag parameter to
handleTypeChange (e.g., handleTypeChange(type, { preserveValue: true })) and
call that from the useEffect to avoid clearing value and calling onChange.

153-160: ⚠️ Potential issue | 🔴 Critical

Stale closure on betweenValue causes lost edits.

handleBetweenChange spreads betweenValue (line 155) but the dependency array includes it, creating a new callback on every state change. Worse, if React batches updates, the closure can still reference stale state. Use functional setState to avoid the stale-closure bug entirely.

Suggested fix
  const handleBetweenChange = useCallback(
    (field: keyof BetweenValue, newValue: string) => {
-     const updated = { ...betweenValue, [field]: newValue, type: "between" }
-     setBetweenValue(updated)
-     onChange?.(updated)
+     setBetweenValue(prev => {
+       const updated = { ...prev, [field]: newValue, type: "between" }
+       onChange?.(updated)
+       return updated
+     })
    },
-   [onChange, setBetweenValue, betweenValue],
+   [onChange],
  )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 153 - 160,
The handleBetweenChange callback closes over betweenValue causing stale updates;
change it to use the functional form of setBetweenValue so you compute the new
state from the previous value (e.g. setBetweenValue(prev => ({ ...prev, [field]:
newValue, type: "between" }))) and call onChange with that computed updated
object; then remove betweenValue from the useCallback dependency array so the
callback only depends on onChange and setBetweenValue (reference:
handleBetweenChange, setBetweenValue, betweenValue, onChange).

143-147: ⚠️ Potential issue | 🟠 Major

Stale regex error persists after pattern is corrected.

onError is only called when validateRegex returns an error. Once the user fixes the pattern, the previous error is never cleared.

Suggested fix
      const error = validateRegex(newValue)
-     if (error) {
-       onError?.({ name }, error)
-     }
+     onError?.({ name }, error) // clears when error is undefined
      onChange?.(typedValue)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 143 - 147,
validateRegex is only used to call onError when an error exists, so once the
user corrects the pattern the previous error remains; after computing error =
validateRegex(newValue) call onError with a cleared error when no error is
present (e.g., onError?.({ name }, undefined) or null) before calling onChange
so that the parent clears validation state; update the block around
validateRegex/newValue/typedValue to explicitly clear the error when
validateRegex returns falsy.
src/components/custom/jobsTable/advancedJobFilteringDialog.tsx (1)

218-220: ⚠️ Potential issue | 🟠 Major

Verify backend API contract for filter payload shape.

These lines send the full { value, type } objects to the backend. Per JobsService.ts:25-34, filterJobs spreads advancedFilters directly into the POST body. If the backend expects scalar strings for name/cronSetting/consumer, this breaks filtering; if it expects the object shape to distinguish regex vs exact matching, this is correct.

#!/bin/bash
# Check how the backend handles these filter fields
# Look for filter handling in backend or API types

# Search for filterJobs endpoint handling or types
rg -n -C5 'filterJobs|advancedFilters' --type ts --type js

# Check if there are any API type definitions
fd -t f -i 'api' -e ts -e d.ts --exec cat {}

If the backend expects plain strings, apply this fix:

Suggested fix for scalar values
-       name: values.name?.value ? values.name : undefined,
-       cronSetting: values.cronSetting?.value ? values.cronSetting : undefined,
-       consumer: values.consumer?.value ? values.consumer : undefined,
+       name: values.name?.value ? values.name.value : undefined,
+       cronSetting: values.cronSetting?.value ? values.cronSetting.value : undefined,
+       consumer: values.consumer?.value ? values.consumer.value : undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx` around lines
218 - 220, The code currently sends full {value,type} objects for
name/cronSetting/consumer from advancedJobFilteringDialog; verify the backend
contract in JobsService.filterJobs (see JobsService.ts lines around 25-34) to
confirm whether advancedFilters should contain scalar strings or the
{value,type} shape; if the backend expects scalars, change the payload
construction in advancedJobFilteringDialog so name: values.name?.value ?
values.name.value : undefined (and likewise for cronSetting and consumer); if
the backend expects the object shape, leave as-is but add/type the
advancedFilters payload to reflect the {value,type} structure so filterJobs and
any API types match.
src/components/custom/notifications/NotificationServicesPanel.tsx (1)

385-385: ⚠️ Potential issue | 🟠 Major

Right details pane still lacks a bounded scroll container.

Line 385 is still an unconstrained column wrapper, so in short viewports this section can grow/clamp under the parent overflow-hidden container instead of scrolling internally.

Suggested fix
-                <div className="flex flex-col gap-2">
+                <div className="flex h-full min-h-0 flex-col gap-2 overflow-y-auto">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx` at line
385, The right details pane in NotificationServicesPanel uses an unconstrained
<div className="flex flex-col gap-2"> which allows content to grow past the
parent overflow-hidden instead of scrolling; update that element (or its
immediate wrapper in NotificationServicesPanel) to be a bounded, scrollable
container by adding sizing and scroll utility classes such as: include min-h-0
and either flex-1 or a max-h value plus overflow-auto (e.g. className="flex
flex-col gap-2 flex-1 min-h-0 overflow-auto") so the column can shrink and
scroll correctly inside the parent.
src/features/jobsTable/jobsTable.tsx (1)

71-75: ⚠️ Potential issue | 🟡 Minor

Remove min-h-[800px] for empty state—already handled in TableBodyVirtualized.

The empty state is properly rendered in TableBodyVirtualized (lines 141-146) with a "No results" message. The min-h-[800px] on the wrapper creates an unnecessarily large blank panel when data is empty, conflicting with the viewport-bounded height passed via size.

🔧 Suggested fix
       className={cn(
         "rounded-md border border-border w-full overflow-auto relative",
         size,
-        data.length > 0 ? "" : "min-h-[800px]",
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/jobsTable/jobsTable.tsx` around lines 71 - 75, The wrapper div
in jobsTable.tsx currently injects "min-h-[800px]" when data is empty, causing a
large blank area; remove that fallback from the className expression so the
wrapper only uses cn("rounded-md border border-border w-full overflow-auto
relative", size) and let TableBodyVirtualized handle the empty state rendering
(refer to TableBodyVirtualized and the size and data variables used in the
className logic).
🤖 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/components/custom/dashboard/statsBar.tsx`:
- Around line 79-86: The Tooltip instance (Tooltip, TooltipTrigger,
TooltipContent) needs to be wrapped with Radix's TooltipProvider to provide
context; import TooltipProvider and wrap the existing Tooltip block (the
component using FileWarningIcon and tooltipContent) with <TooltipProvider> so
the provider is an ancestor of Tooltip; ensure the import list for Tooltip
includes TooltipProvider and that any props required by TooltipProvider (if used
elsewhere) are passed through.
- Around line 57-63: The nested ternary mapping of the textSize prop in the
className (inside the StatsBar component in statsBar.tsx) is redundant because
textSize already contains the Tailwind class string; replace the ternary logic
in the cn(...) call to include textSize directly (e.g., pass textSize) and
provide a fallback default like "text-2xl" when textSize is falsy to preserve
current behavior; update any references to the textSize prop usage to rely on
the direct class string instead of remapping.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Line 119: The dependency arrays in MultiTypedInput's hooks include stable
React state setters (setExactValue, setRegexValue, setBetweenValue)
unnecessarily; remove these setters from the dependency arrays of the affected
callbacks/effects (the hooks referencing onChange, onError, name) so only actual
changing deps (e.g., onChange, onError, name) remain—apply the same removal to
the other two occurrences in the file that match the pattern.

In `@src/features/jobsTable/jobsPage.tsx`:
- Line 271: The sentinel div with ref (the line <div ref={ref}
className="h-[1px]"></div>) is used with useInView but useInView is defaulting
to the viewport, so when the table has a fixed internal scroll
(size="h-[calc(100vh-150px)]") the sentinel stays visible and the sticky header
never toggles; update the useInView call to supply the table scrolling container
as the root option (i.e., pass the element reference for the table
container/wrapper used for the fixed-height scroll) so the intersection observer
is scoped to that container, or alternatively remove/adjust the sentinel and
sticky styling if sticky behavior is no longer desired.

In `@src/features/jobsTable/jobsTable.tsx`:
- Line 176: The className string in the JSX element in jobsTable.tsx contains a
leading space (" flex absolute w-full border-b-border"); remove the leading
space so the attribute reads "flex absolute w-full border-b-border". Locate the
JSX element with className=" flex absolute w-full border-b-border" and update
the string to drop the initial space to match other class definitions.
- Around line 126-132: The overscan for the virtualized table is set very high
in the rowVirtualizer configuration (useVirtualizer call) which defeats
virtualization benefits; change the overscan value from 42 to a much smaller,
typical number (e.g., 5-10—try 8) in the options passed to useVirtualizer to
reduce offscreen rendering while keeping smooth scroll behavior.
- Line 49: The ref tableContainerRef is initialized with useRef(null) which
infers RefObject<null>; update its initialization to explicitly type it as
React.RefObject<HTMLDivElement> (or useRef<HTMLDivElement | null>(null)) so it
matches the TableBodyProps typing; change the declaration of tableContainerRef
to useRef<HTMLDivElement | null>(null) (or the equivalent
React.RefObject<HTMLDivElement>) to ensure type safety across the component.

---

Outside diff comments:
In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 301-308: The FlexibleInput usages (e.g., the instance with
type="regex" in advancedJobFilteringDialog) are getting their pre-filled values
wiped because MultiTypedInput.tsx's useEffect invokes handleTypeChange (lines
around 162-166), which resets internal state and emits an empty value; fix
MultiTypedInput by syncing the internal inputType state directly from props (set
inputType = props.type or similar) in that effect instead of calling
handleTypeChange so you don't reset other internal state or call the
value-emitting logic; ensure handleTypeChange remains the explicit
user-triggered transition and that existing field.value is preserved and not
overwritten when the component mounts.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 78-102: Table cells are misaligning because body cells use the
tailwind class "w-full" while headers use "flex flex-grow" and widths are set
via header.column.getSize()/cell.column.getSize(), causing flex-basis to be
overridden; remove "w-full" from the body cell elements (the TableCell elements
that use style={{ width: cell.column.getSize() }}) so they mirror the TableHead
layout, or alternatively change both header and body containers from "flex-grow"
to "flex-shrink-0" (update classNames on TableRow/TableHead and the
corresponding TableCell) to enforce strict sizing based on column.getSize().

---

Duplicate comments:
In `@src/components/custom/general/MultiTypedInput.tsx`:
- Around line 112-117: The emitted payload uses defaultMultiTypeNullValue which
hardcodes type:"exact", so when newType is "regex" (or other types) the emitted
object's type is wrong; update the emission logic in the onChange block (the
code referencing newType, defaultMultiTypeNullValue, and onChange) to emit an
object whose type field equals newType (e.g., construct { type: newType, value:
... } or clone defaultMultiTypeNullValue but set type: newType) and for
"between" continue to emit { value1: "", value2: "" } as before.
- Around line 162-166: The useEffect currently calls handleTypeChange(type) on
mount which triggers the reset logic (clearing internal state and calling
onChange); instead, update the internal inputType state directly without
invoking reset/emit: in the component MultiTypedInput, replace calling
handleTypeChange from the useEffect with a direct state sync (e.g.,
setInputType(type) or the existing inputType setter) so the controlled prop
synchronizes the UI but does not call handleTypeChange or emit an empty value;
alternatively, add an optional flag parameter to handleTypeChange (e.g.,
handleTypeChange(type, { preserveValue: true })) and call that from the
useEffect to avoid clearing value and calling onChange.
- Around line 153-160: The handleBetweenChange callback closes over betweenValue
causing stale updates; change it to use the functional form of setBetweenValue
so you compute the new state from the previous value (e.g. setBetweenValue(prev
=> ({ ...prev, [field]: newValue, type: "between" }))) and call onChange with
that computed updated object; then remove betweenValue from the useCallback
dependency array so the callback only depends on onChange and setBetweenValue
(reference: handleBetweenChange, setBetweenValue, betweenValue, onChange).
- Around line 143-147: validateRegex is only used to call onError when an error
exists, so once the user corrects the pattern the previous error remains; after
computing error = validateRegex(newValue) call onError with a cleared error when
no error is present (e.g., onError?.({ name }, undefined) or null) before
calling onChange so that the parent clears validation state; update the block
around validateRegex/newValue/typedValue to explicitly clear the error when
validateRegex returns falsy.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 218-220: The code currently sends full {value,type} objects for
name/cronSetting/consumer from advancedJobFilteringDialog; verify the backend
contract in JobsService.filterJobs (see JobsService.ts lines around 25-34) to
confirm whether advancedFilters should contain scalar strings or the
{value,type} shape; if the backend expects scalars, change the payload
construction in advancedJobFilteringDialog so name: values.name?.value ?
values.name.value : undefined (and likewise for cronSetting and consumer); if
the backend expects the object shape, leave as-is but add/type the
advancedFilters payload to reflect the {value,type} structure so filterJobs and
any API types match.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx`:
- Line 385: The right details pane in NotificationServicesPanel uses an
unconstrained <div className="flex flex-col gap-2"> which allows content to grow
past the parent overflow-hidden instead of scrolling; update that element (or
its immediate wrapper in NotificationServicesPanel) to be a bounded, scrollable
container by adding sizing and scroll utility classes such as: include min-h-0
and either flex-1 or a max-h value plus overflow-auto (e.g. className="flex
flex-col gap-2 flex-1 min-h-0 overflow-auto") so the column can shrink and
scroll correctly inside the parent.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 71-75: The wrapper div in jobsTable.tsx currently injects
"min-h-[800px]" when data is empty, causing a large blank area; remove that
fallback from the className expression so the wrapper only uses cn("rounded-md
border border-border w-full overflow-auto relative", size) and let
TableBodyVirtualized handle the empty state rendering (refer to
TableBodyVirtualized and the size and data variables used in the className
logic).
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fbd1c300-2100-4501-bd5b-502d5c08cd11

📥 Commits

Reviewing files that changed from the base of the PR and between 00cb33e and 66f38af.

📒 Files selected for processing (8)
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
  • src/components/custom/notifications/NotificationServicesPanel.tsx
  • src/components/ui/date-picker-presets.tsx
  • src/features/jobsTable/jobsPage.tsx
  • src/features/jobsTable/jobsTable.tsx
  • src/features/system/database.tsx

Copy link
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (3)
src/components/custom/dashboard/statsBar.tsx (1)

7-11: ⚠️ Potential issue | 🔴 Critical

TooltipProvider is used but not imported — runtime error.

Line 75 uses <TooltipProvider> but it's missing from the import. This will cause a ReferenceError when rendering the error state.

🐛 Proposed fix
 import {
   Tooltip,
   TooltipContent,
   TooltipTrigger,
+  TooltipProvider,
 } from "@/components/ui/tooltip"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/dashboard/statsBar.tsx` around lines 7 - 11, The
component is using TooltipProvider (rendered at line ~75) but it's not imported,
causing a runtime ReferenceError; fix by adding TooltipProvider to the import
list alongside Tooltip, TooltipContent, TooltipTrigger from
"@/components/ui/tooltip" so the symbol is available when rendering the error
state (update the import statement that currently imports Tooltip,
TooltipContent, TooltipTrigger to also import TooltipProvider).
src/components/custom/general/LogFileList.tsx (2)

61-68: ⚠️ Potential issue | 🟡 Minor

Missing key prop on virtualized list items.

Each FileItem rendered in the .map() lacks a key prop. This causes React reconciliation issues—especially problematic in virtualized lists where elements are reused. Use virtualRow.index or a unique identifier like fileId.

🔧 Proposed fix
         {rowVirtualizer.getVirtualItems().map((virtualRow, index) => (
           <FileItem
+            key={virtualRow.index}
             className="mb-2 absolute"
             style={{
               transform: `translateY(${virtualRow.start}px)`,
             }}
             {...FileList[virtualRow?.index ?? index]}
           />
         ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/LogFileList.tsx` around lines 61 - 68, The
FileItem elements rendered from rowVirtualizer.getVirtualItems() are missing a
React key; update the map so each FileItem receives a stable key (use
virtualRow.index or a unique identifier from the data). For example, inside the
mapping that spreads {...FileList[virtualRow?.index ?? index]}, add a key prop
referencing virtualRow.index or FileList[virtualRow.index].fileId (fall back to
virtualRow.index if no fileId) to ensure proper reconciliation for the FileItem
components.

48-48: ⚠️ Potential issue | 🟡 Minor

Missing props.downloadLogFile in useMemo dependency array.

props.downloadLogFile is captured on line 45 but not listed in the dependencies. If this prop changes, FileList won't re-compute, leading to stale callback references.

🔧 Proposed fix
-  }, [props.logFiles, props.readLogFile])
+  }, [props.logFiles, props.readLogFile, props.downloadLogFile, props.originName])

Note: props.originName (line 39) is also missing from the dependency array.

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

In `@src/components/custom/general/LogFileList.tsx` at line 48, The useMemo that
builds FileList is missing dependencies: add props.downloadLogFile and
props.originName to the dependency array alongside props.logFiles and
props.readLogFile so the memo recomputes when those callbacks/props change;
update the dependency list used in the useMemo call within the
FileList-producing block to include downloadLogFile and originName to avoid
stale references.
♻️ Duplicate comments (6)
src/components/custom/DrawerJobEvents.tsx (2)

83-83: ⚠️ Potential issue | 🟡 Minor

Add fallback for potentially undefined items in getItemKey.

If allEvents[index] is undefined during a render-update race, this will throw. The previous review suggested adding a fallback which appears to have been partially addressed but the safety guard is missing.

🛡️ Suggested fix
-    getItemKey: index => String(allEvents[index].id),
+    getItemKey: index => String(allEvents[index]?.id ?? index),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` at line 83, The getItemKey
function can throw if allEvents[index] is undefined during render; update
getItemKey (in the DrawerJobEvents component) to guard against missing items and
return a stable fallback key, e.g. use the event id when present and otherwise
fall back to the index or a prefixed index string (e.g. use allEvents[index]?.id
?? index), ensuring the returned value is always a string.

144-149: 🧹 Nitpick | 🔵 Trivial

key={allEvents.length} forces unnecessary remounts.

Using allEvents.length as the key on the container div forces React to unmount and remount the entire virtualized list whenever items are added or removed. This defeats some benefits of virtualization and may cause scroll position resets.

Consider removing this key or using a more stable identifier if the goal is to trigger re-measurement. The refreshVirtualizer callback with rowVirtualizer.measure() should handle updates.

♻️ Proposed fix
           <div
             className="relative w-full py-4"
             style={{
               height: `${rowVirtualizer.getTotalSize()}px`,
             }}
-            key={allEvents.length}
           >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` around lines 144 - 149, The
container div is using key={allEvents.length}, which forces full remounts of the
virtualized list; remove that unstable key (or replace it with a stable
identifier if you truly need remounting) and rely on the virtualizer re-measure
flow instead—call refreshVirtualizer / rowVirtualizer.measure() after updates so
the list recomputes sizes rather than forcing React to unmount/remount the
element; update the JSX around the div in DrawerJobEvents.tsx to drop the
length-based key and ensure any existing refreshVirtualizer usage invokes
rowVirtualizer.measure() when items change.
src/components/custom/general/MultiTypedInput.tsx (4)

155-162: 🛠️ Refactor suggestion | 🟠 Major

Callback recreated on every value change; prefer functional setState.

Adding betweenValue to the dependency array fixes the stale closure but defeats memoization—the callback is recreated on every keystroke. Using functional state update avoids the closure issue while keeping a stable callback identity.

Proposed fix using functional setState
   const handleBetweenChange = useCallback(
     (field: keyof BetweenValue, newValue: string) => {
-      const updated = { ...betweenValue, [field]: newValue, type: "between" }
-      setBetweenValue(updated)
-      onChange?.(updated)
+      setBetweenValue(prev => {
+        const updated = { ...prev, [field]: newValue, type: "between" }
+        onChange?.(updated)
+        return updated
+      })
     },
-    [onChange, setBetweenValue, betweenValue],
+    [onChange],
   )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 155 - 162,
The handleBetweenChange callback closes over betweenValue causing either stale
state or recreation on every keystroke; change it to use the functional form of
setBetweenValue so the callback can omit betweenValue from its deps and remain
stable: inside handleBetweenChange call setBetweenValue(prev => { const updated
= { ...prev, [field]: newValue, type: "between" }; onChange?.(updated); return
updated; }); then update the dependency array to only include onChange (and any
other stable refs) so handleBetweenChange remains memoized; reference
functions/values: handleBetweenChange, setBetweenValue, onChange, BetweenValue.

113-114: ⚠️ Potential issue | 🔴 Critical

Emitted payload has incorrect type field for regex mode.

When newType === "regex", the code emits defaultMultiTypeNullValue which is hardcoded to { value: "", type: "exact" }. The emitted value should reflect the actual selected type.

Proposed fix
       // Emit initial value for new type
       if (newType === "exact" || newType === "regex") {
-        onChange?.(defaultMultiTypeNullValue)
+        onChange?.({ value: "", type: newType })
       } else if (newType === "between") {
-        onChange?.({ value1: "", value2: "" })
+        onChange?.({ value1: "", value2: "", type: "between" })
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 113 - 114,
The emitted payload uses defaultMultiTypeNullValue which hardcodes type:"exact",
so when newType === "regex" the emitted value has the wrong type; update the
logic in the MultiTypedInput component where newType is handled (the block that
calls onChange?.(defaultMultiTypeNullValue)) to emit a value whose type matches
newType (e.g., clone/construct the default payload but set type: newType) so
onChange receives the correct type for "regex" and "exact".

164-168: ⚠️ Potential issue | 🔴 Critical

Effect resets values on mount when controlled type is provided.

Calling handleTypeChange(type) in this effect resets all internal state and emits empty payloads. On mount, if both type and value props are provided, the value initialization effect (lines 86-99) runs, then this effect fires and clears everything. The effect should only synchronize inputType state without triggering the reset logic.

Proposed fix
   useEffect(() => {
-    if (type) {
-      handleTypeChange(type)
+    if (type && type !== inputType) {
+      setInputType(type)
     }
-  }, [type, handleTypeChange])
+  }, [type, inputType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 164 - 168,
The effect currently calls handleTypeChange(type) on mount which triggers full
reset logic; instead only synchronize the local inputType state when a
controlled type prop is provided. Replace the call to handleTypeChange(type) in
the useEffect with logic that sets the internal inputType (e.g.
setInputType(type) or equivalent) and avoid invoking handleTypeChange so the
value initialization effect (value prop handling) isn't clobbered by the reset;
ensure you still react to actual type changes by invoking handleTypeChange only
when a real user-driven change is intended.

136-152: ⚠️ Potential issue | 🟡 Minor

Missing name in dependency array causes stale closure.

The callback references name at lines 145 and 147 but the dependency array at line 151 only includes [onChange, onError]. If the name prop changes, the callback will use the stale value.

Proposed fix
     [onChange, onError],
+    [name, onChange, onError],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 136 - 152,
The handleRegexChange callback captures the prop name but it's not declared in
the dependency array, causing a stale closure; update the useCallback dependency
array for handleRegexChange to include name (in addition to onChange and
onError) so the closure sees updated name values, and ensure related symbols
(handleRegexChange, setRegexValue, validateRegex, onError, onChange, name) are
referenced correctly when updating the dependency list.
🤖 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/components/custom/DrawerJobEvents.tsx`:
- Around line 86-92: The workaround using setTimeout in refreshVirtualizer and
the remount via key={allEvents.length} causes unreliable virtualizer updates;
remove the key={allEvents.length} and replace the timeout-based refresh by
adding a useEffect that watches allEvents (or its length/identity) and calls
rowVirtualizer.measure() when it changes (while keeping refreshVirtualizer for
manual triggers if needed); ensure you reference rowVirtualizer.measure and the
allEvents state inside the effect and clean up any stale closures by including
rowVirtualizer in the effect dependency array.
- Line 19: Remove the unused import "debounce" from the top of the
DrawerJobEvents component file; locate the import line that reads import {
debounce } from "@/utils/generalUtils" and delete it (or remove debounce from
the named imports if combined), ensuring the DrawerJobEvents component and any
other imports remain unchanged.
- Around line 70-72: The useMemo for allEvents includes redundant dependencies
(latestEvents.length and events.length); update the dependency array to only
include latestEvents and events so it becomes useMemo(() => [...latestEvents,
...events], [latestEvents, events]) — modify the dependency list around the
allEvents declaration to remove the .length entries while keeping the useMemo
logic unchanged.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 170-195: The TableRow inside TableBodyRow has a redundant key prop
(key={row.id}) because the parent map already sets the key; remove the inner key
attribute from the TableRow in the TableBodyRow component (leave data-index,
ref, className, style, and children unchanged) so there’s a single key provided
by the parent mapping over rows.
- Around line 137-146: The TableBody is given a fixed height from
rowVirtualizer.getTotalSize(), which is 0 when rows.length === 0 and causes the
"No results." TableRow (h-24) to be clipped; update the rendering in
jobsTable.tsx so the style for TableBody only sets height when rows exist (e.g.,
if rows.length > 0 use height: `${rowVirtualizer.getTotalSize()}px`) or
alternatively apply a minHeight (e.g., minHeight: '6rem') when rows are empty;
locate the TableBody usage that references rowVirtualizer.getTotalSize() and
adjust the conditional styling to ensure the empty-state TableRow is visible.

---

Outside diff comments:
In `@src/components/custom/dashboard/statsBar.tsx`:
- Around line 7-11: The component is using TooltipProvider (rendered at line
~75) but it's not imported, causing a runtime ReferenceError; fix by adding
TooltipProvider to the import list alongside Tooltip, TooltipContent,
TooltipTrigger from "@/components/ui/tooltip" so the symbol is available when
rendering the error state (update the import statement that currently imports
Tooltip, TooltipContent, TooltipTrigger to also import TooltipProvider).

In `@src/components/custom/general/LogFileList.tsx`:
- Around line 61-68: The FileItem elements rendered from
rowVirtualizer.getVirtualItems() are missing a React key; update the map so each
FileItem receives a stable key (use virtualRow.index or a unique identifier from
the data). For example, inside the mapping that spreads
{...FileList[virtualRow?.index ?? index]}, add a key prop referencing
virtualRow.index or FileList[virtualRow.index].fileId (fall back to
virtualRow.index if no fileId) to ensure proper reconciliation for the FileItem
components.
- Line 48: The useMemo that builds FileList is missing dependencies: add
props.downloadLogFile and props.originName to the dependency array alongside
props.logFiles and props.readLogFile so the memo recomputes when those
callbacks/props change; update the dependency list used in the useMemo call
within the FileList-producing block to include downloadLogFile and originName to
avoid stale references.

---

Duplicate comments:
In `@src/components/custom/DrawerJobEvents.tsx`:
- Line 83: The getItemKey function can throw if allEvents[index] is undefined
during render; update getItemKey (in the DrawerJobEvents component) to guard
against missing items and return a stable fallback key, e.g. use the event id
when present and otherwise fall back to the index or a prefixed index string
(e.g. use allEvents[index]?.id ?? index), ensuring the returned value is always
a string.
- Around line 144-149: The container div is using key={allEvents.length}, which
forces full remounts of the virtualized list; remove that unstable key (or
replace it with a stable identifier if you truly need remounting) and rely on
the virtualizer re-measure flow instead—call refreshVirtualizer /
rowVirtualizer.measure() after updates so the list recomputes sizes rather than
forcing React to unmount/remount the element; update the JSX around the div in
DrawerJobEvents.tsx to drop the length-based key and ensure any existing
refreshVirtualizer usage invokes rowVirtualizer.measure() when items change.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Around line 155-162: The handleBetweenChange callback closes over betweenValue
causing either stale state or recreation on every keystroke; change it to use
the functional form of setBetweenValue so the callback can omit betweenValue
from its deps and remain stable: inside handleBetweenChange call
setBetweenValue(prev => { const updated = { ...prev, [field]: newValue, type:
"between" }; onChange?.(updated); return updated; }); then update the dependency
array to only include onChange (and any other stable refs) so
handleBetweenChange remains memoized; reference functions/values:
handleBetweenChange, setBetweenValue, onChange, BetweenValue.
- Around line 113-114: The emitted payload uses defaultMultiTypeNullValue which
hardcodes type:"exact", so when newType === "regex" the emitted value has the
wrong type; update the logic in the MultiTypedInput component where newType is
handled (the block that calls onChange?.(defaultMultiTypeNullValue)) to emit a
value whose type matches newType (e.g., clone/construct the default payload but
set type: newType) so onChange receives the correct type for "regex" and
"exact".
- Around line 164-168: The effect currently calls handleTypeChange(type) on
mount which triggers full reset logic; instead only synchronize the local
inputType state when a controlled type prop is provided. Replace the call to
handleTypeChange(type) in the useEffect with logic that sets the internal
inputType (e.g. setInputType(type) or equivalent) and avoid invoking
handleTypeChange so the value initialization effect (value prop handling) isn't
clobbered by the reset; ensure you still react to actual type changes by
invoking handleTypeChange only when a real user-driven change is intended.
- Around line 136-152: The handleRegexChange callback captures the prop name but
it's not declared in the dependency array, causing a stale closure; update the
useCallback dependency array for handleRegexChange to include name (in addition
to onChange and onError) so the closure sees updated name values, and ensure
related symbols (handleRegexChange, setRegexValue, validateRegex, onError,
onChange, name) are referenced correctly when updating the dependency list.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a819b92c-6b92-44ea-bbd7-02dcef828a68

📥 Commits

Reviewing files that changed from the base of the PR and between 66f38af and 62c525a.

📒 Files selected for processing (6)
  • src/components/custom/DrawerJobEvents.tsx
  • src/components/custom/DrawerLatestRuns.tsx
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/LogFileList.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/features/jobsTable/jobsTable.tsx

import { ListCheck } from "lucide-react"
import { useVirtualizer } from "@tanstack/react-virtual"
import LoadingOverlay from "@/components/custom/LoadingOverlay"
import { debounce } from "@/utils/generalUtils"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Unused import: debounce.

The debounce function is imported but never used in this file. This appears to be leftover from development.

🧹 Proposed fix
 import LoadingOverlay from "@/components/custom/LoadingOverlay"
-import { debounce } from "@/utils/generalUtils"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { debounce } from "@/utils/generalUtils"
import LoadingOverlay from "@/components/custom/LoadingOverlay"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` at line 19, Remove the unused
import "debounce" from the top of the DrawerJobEvents component file; locate the
import line that reads import { debounce } from "@/utils/generalUtils" and
delete it (or remove debounce from the named imports if combined), ensuring the
DrawerJobEvents component and any other imports remain unchanged.

Comment on lines +70 to +72
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents, latestEvents.length, events.length])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Redundant dependency array entries.

The .length properties are redundant since events and latestEvents references change when the arrays are updated.

🧹 Simplified dependencies
   const allEvents = useMemo(() => {
     return [...latestEvents, ...events]
-  }, [events, latestEvents, latestEvents.length, events.length])
+  }, [events, latestEvents])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents, latestEvents.length, events.length])
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` around lines 70 - 72, The useMemo
for allEvents includes redundant dependencies (latestEvents.length and
events.length); update the dependency array to only include latestEvents and
events so it becomes useMemo(() => [...latestEvents, ...events], [latestEvents,
events]) — modify the dependency list around the allEvents declaration to remove
the .length entries while keeping the useMemo logic unchanged.

Comment on lines +86 to +92
const refreshVirtualizer = useCallback(() => {
setTimeout(() => {
// TODO find another solution for this timeout issue :
// the allEvent array although is always updated, doesn't trigger the virtualizer getVirtualItems() function to return filled item array
rowVirtualizer.measure()
}, 200)
}, [rowVirtualizer])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Acknowledged workaround with setTimeout.

The PR notes mention this timeout is needed because the virtualizer doesn't refresh correctly when the event list updates. This is likely related to the key={allEvents.length} forcing remounts and the timing of when the virtualizer measures.

Consider addressing the root cause by:

  1. Removing the key={allEvents.length} from line 149
  2. Using useEffect to call rowVirtualizer.measure() when allEvents changes

This would be more React-idiomatic than the current onOpenChange + setTimeout approach.

Would you like me to help draft an alternative implementation using useEffect to handle virtualizer refresh more reliably?

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

In `@src/components/custom/DrawerJobEvents.tsx` around lines 86 - 92, The
workaround using setTimeout in refreshVirtualizer and the remount via
key={allEvents.length} causes unreliable virtualizer updates; remove the
key={allEvents.length} and replace the timeout-based refresh by adding a
useEffect that watches allEvents (or its length/identity) and calls
rowVirtualizer.measure() when it changes (while keeping refreshVirtualizer for
manual triggers if needed); ensure you reference rowVirtualizer.measure and the
allEvents state inside the effect and clean up any stale closures by including
rowVirtualizer in the effect dependency array.

Comment on lines +137 to +146
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
}}
>
{rows.length === 0 ? (
<TableRow>
<TableCell colSpan={columnsLength} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential layout issue: empty state inside zero-height container.

When rows.length === 0, rowVirtualizer.getTotalSize() returns 0, setting TableBody height to 0px. However, the "No results." row inside has h-24 (6rem) content. The relative positioning on TableBody with explicit height: 0px may clip or hide the empty-state row.

Consider conditionally setting height only when rows exist:

     <TableBody
       className="grid relative"
       style={{
-        height: `${rowVirtualizer.getTotalSize()}px`,
+        height: rows.length > 0 ? `${rowVirtualizer.getTotalSize()}px` : undefined,
       }}
     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/jobsTable/jobsTable.tsx` around lines 137 - 146, The TableBody
is given a fixed height from rowVirtualizer.getTotalSize(), which is 0 when
rows.length === 0 and causes the "No results." TableRow (h-24) to be clipped;
update the rendering in jobsTable.tsx so the style for TableBody only sets
height when rows exist (e.g., if rows.length > 0 use height:
`${rowVirtualizer.getTotalSize()}px`) or alternatively apply a minHeight (e.g.,
minHeight: '6rem') when rows are empty; locate the TableBody usage that
references rowVirtualizer.getTotalSize() and adjust the conditional styling to
ensure the empty-state TableRow is visible.

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.

1 participant