Skip to content

Lazy-load config versions on expand & extract inline SVGs to icon components#96

Open
Ayush8923 wants to merge 11 commits intomainfrom
feature/config-library-lazy-loading
Open

Lazy-load config versions on expand & extract inline SVGs to icon components#96
Ayush8923 wants to merge 11 commits intomainfrom
feature/config-library-lazy-loading

Conversation

@Ayush8923
Copy link
Copy Markdown
Collaborator

@Ayush8923 Ayush8923 commented Mar 27, 2026

Issue: #95

Summary:

  • Config Library now makes only 1 API call on initial load (GET /api/configs) instead of 1 + 2N calls (versions + version details per config)
  • Version details load on demand when a user clicks/expands a specific config card, triggering GET /versions and GET /versions/{latest} only for that config
  • Extracted hardcoded SVGs from ConfigEditorPane, Configurations page into reusable icon components
  • Implement the dynamic search and pagination things.

Summary by CodeRabbit

  • New Features

    • Incremental infinite scroll for configurations (no "Load More" button)
    • Expandable configuration cards that lazily load full details
    • Debounced search feeding the server-side results
  • Improvements

    • Column-based, stable multi-column layout for results
    • Unified/shared icon components and updated loading spinner
    • Loader UI and messaging standardized across views

@Ayush8923 Ayush8923 self-assigned this Mar 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 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

This PR centralizes config types, adds paginated/infinite-scroll list hooks, refactors the configurations page to use incremental loading and columnized layout, implements lazy per-config version loading with de-duplication, and consolidates/adds shared icon components.

Changes

Cohort / File(s) Summary
Type System Reorganization
app/lib/configTypes.ts, app/lib/types/configs.ts
Deleted legacy configTypes.ts exports and moved/rewrote config-related types and API response shapes into app/lib/types/configs.ts.
List & Scroll Hooks
app/hooks/usePaginatedList.ts, app/hooks/useInfiniteScroll.ts
Added usePaginatedList<T> for paginated API queries (limit/skip/query) and useInfiniteScroll that attaches to a scroll container and triggers loads when near bottom.
Configurations Page
app/(routes)/configurations/page.tsx
Replaced previous useConfigs flow with usePaginatedList + useInfiniteScroll; switched search to debounced server query; moved to fixed columnCount distribution; removed "Load More" button in favor of scroll-driven loading.
Lazy Version Loading & Cache
app/components/ConfigCard.tsx, app/lib/store/configStore.ts
ConfigCard now accepts config: ConfigPublic and async onLoadVersions/onLoadSingleVersion; expands to load versions lazily and uses store-backed versionItemsCache with pending-load de-duplication.
Icon Consolidation & New Icons
app/components/icons/index.tsx, app/components/icons/common/*, app/components/icons/prompt-editor/*
Added common and prompt-editor icons (PlusIcon, SearchIcon, SidebarToggleIcon, ChevronRightIcon, InfoIcon, SpinnerIcon) and consolidated exports in the icons index.
Prompt Editor / UI Adjustments
app/components/prompt-editor/ConfigEditorPane.tsx, app/components/prompt-editor/ConfigDrawer.tsx, app/components/prompt-editor/HistorySidebar.tsx, app/components/ConfigDrawer.tsx, app/components/ConfigModal.tsx, app/components/SimplifiedConfigEditor.tsx
Switched several type imports to @/app/lib/types/configs; replaced inline SVGs with icon components; adjusted styling/tooltip/input border classes.
Supporting Updates
app/hooks/useConfigs.ts, app/lib/configFetchers.ts, app/api/configs/route.ts, app/lib/constants.ts, app/components/Loader.tsx, app/globals.css, eslint.config.mjs, app/lib/utils.ts, app/lib/context/AuthContext.tsx
Renamed PAGE_SIZEDEFAULT_PAGE_LIMIT; API route forwards dynamic query string; Loader converted to Tailwind spinner and CSS .loader-spinner added; minor doc/comment/lint changes and imports updated.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ConfigsPage as Configurations Page
    participant usePaginatedList as usePaginatedList Hook
    participant useInfiniteScroll as useInfiniteScroll Hook
    participant API as Backend API
    participant configStore as Config Store

    User->>ConfigsPage: Open page / type search
    ConfigsPage->>usePaginatedList: init(endpoint, debouncedQuery)
    usePaginatedList->>API: GET /api/v1/configs?skip=0&limit=10&query=...
    API-->>usePaginatedList: return items + metadata
    usePaginatedList-->>ConfigsPage: items, hasMore

    ConfigsPage->>useInfiniteScroll: attach scrollRef to container
    User->>useInfiniteScroll: scroll near bottom
    useInfiniteScroll->>ConfigsPage: trigger onLoadMore
    ConfigsPage->>usePaginatedList: loadMore()
    usePaginatedList->>API: GET /api/v1/configs?skip=10&limit=10
    API-->>usePaginatedList: next page
    usePaginatedList-->>ConfigsPage: append items, update hasMore

    User->>ConfigsPage: expand ConfigCard
    ConfigsPage->>configStore: check versionItemsCache[configId]
    configStore-->>ConfigsPage: cache miss
    ConfigsPage->>ConfigsPage: call onLoadVersions(configId)
    ConfigsPage->>API: GET /api/configs/:id/versions
    API-->>configStore: store version list
    ConfigsPage->>ConfigsPage: call onLoadSingleVersion(configId, version)
    ConfigsPage->>API: GET /api/configs/:id/versions/:version
    API-->>ConfigsPage: return SavedConfig
    ConfigsPage->>User: render expanded details
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Suggested Labels

enhancement

Suggested Reviewers

  • Prajna1999
  • vprashrex

Poem

🐇 I hop through pages, loading more as you scroll,

Debounced searches hum, columns neatly roll.
Icons all gathered, types tidy and new,
Versions wake lazy when cards say "peek through".
Cheers — a small rabbit applauds this refactor crew.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.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 clearly and concisely summarizes the two main changes: lazy-loading config versions on card expansion and extracting inline SVGs into icon components. It accurately reflects the primary objectives of the PR.

✏️ 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 feature/config-library-lazy-loading

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.

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

🧹 Nitpick comments (4)
app/lib/utils.ts (1)

4-10: Consolidate imports from the same module and use consistent path alias.

Lines 4-9 import from @/app/lib/types/configs while Line 10 imports from ./types/configs. These should be consolidated into a single import statement using the @/ path alias consistently.

♻️ Proposed fix
 import {
   ConfigPublic,
   ConfigVersionPublic,
   ConfigVersionItems,
   Tool,
+  SavedConfig,
+  ConfigGroup,
 } from "@/app/lib/types/configs";
-import { SavedConfig, ConfigGroup } from "./types/configs";

As per coding guidelines: "Use the @/ path alias for all imports instead of relative paths."

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

In `@app/lib/utils.ts` around lines 4 - 10, Consolidate the two import statements
into one that uses the "@/app/lib/types/configs" alias: import ConfigPublic,
ConfigVersionPublic, ConfigVersionItems, Tool, SavedConfig, and ConfigGroup from
the same module so all types are imported consistently; update the existing
imports in app/lib/utils.ts (symbols: ConfigPublic, ConfigVersionPublic,
ConfigVersionItems, Tool, SavedConfig, ConfigGroup) to a single aliased import
and remove the relative "./types/configs" import.
app/components/SimplifiedConfigEditor.tsx (1)

30-43: Consider using the shared SavedConfig type.

This local SavedConfig interface duplicates the one defined in @/app/lib/types/configs with minor differences (missing description, type, and promptContent fields). Consider importing and extending the shared type to maintain consistency and reduce duplication.

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

In `@app/components/SimplifiedConfigEditor.tsx` around lines 30 - 43, The local
SavedConfig interface in SimplifiedConfigEditor.tsx duplicates the shared type;
replace it by importing the shared SavedConfig from "@/app/lib/types/configs"
and either use it directly or extend it to add any component-specific optional
fields (e.g., description, type, promptContent) to avoid divergence; update
references in this file to use the imported/extended SavedConfig type (look for
the SavedConfig interface and any functions or props that consume it) and remove
the local duplicate declaration.
app/components/icons/prompt-editor/ChevronRightIcon.tsx (1)

1-19: Consider extracting the shared IconProps interface to reduce duplication.

The IconProps interface is identical across multiple icon files. While this works, extracting it to a shared location (e.g., app/components/icons/types.ts) would reduce duplication and ensure consistency.

♻️ Optional: Shared IconProps type

Create app/components/icons/types.ts:

export interface IconProps {
  className?: string;
  style?: React.CSSProperties;
}

Then import in each icon file:

+import { IconProps } from "../types";
-interface IconProps {
-  className?: string;
-  style?: React.CSSProperties;
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/icons/prompt-editor/ChevronRightIcon.tsx` around lines 1 - 19,
Extract the duplicated IconProps interface into a shared module and update the
icon component to import it: create a new module exporting interface IconProps {
className?: string; style?: React.CSSProperties } and replace the local
IconProps declaration in ChevronRightIcon with an import; ensure
ChevronRightIcon({ className, style }: IconProps) still types correctly and
update any other icon files to import the shared IconProps to remove
duplication.
app/components/prompt-editor/ConfigEditorPane.tsx (1)

780-784: Keep these new borders and tooltip colors on the shared theme tokens.

border-gray-300, bg-[#1f2937], text-white, and border-r-[#1f2937] bypass the colors palette, so this part of the editor will drift from the rest of the themed UI. Tailwind is fine for layout/spacing here, but the color values should stay in style via the shared palette.

Based on learnings: Use Tailwind CSS for layout and spacing, and inline styles (referencing the colors object) for colors instead of mixing multiple styling approaches.

Also applies to: 796-809, 824-828

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

In `@app/components/prompt-editor/ConfigEditorPane.tsx` around lines 780 - 784,
The JSX in ConfigEditorPane.tsx is mixing Tailwind color classes (e.g.,
"border-gray-300", "text-white", and hardcoded hex borders like
"border-r-[`#1f2937`]") with inline styles; remove these color-related Tailwind
classes and instead use the shared theme tokens from the colors object in the
element's style (e.g., set borderColor, backgroundColor, color via
colors.border.*, colors.bg.*, colors.text.*) so all color values come from the
shared palette; update the same change for the other affected JSX blocks
referenced (around the other ranges) so no component uses hardcoded hex/Tailwind
color classes and all color styling is done via the colors object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/`(routes)/configurations/page.tsx:
- Around line 40-50: The current round-robin split (used with setColumnCount in
the useEffect and the code that partitions configs into separate column
wrappers) causes wrong DOM order and remounts ConfigCard; instead render a
single flat list and let CSS handle columns. Remove the partitioning logic and
any function that builds per-column arrays, render configs via configs.map(...)
(keeping stable keys like config.id) inside a single container, and set the
column count via CSS grid (e.g., set style or class using gridTemplateColumns:
`repeat(columnCount, 1fr)`) so layout changes don't move nodes between different
subtrees; apply the same change wherever the round-robin split is used (the
other similar blocks noted in the review).

In `@app/components/ConfigCard.tsx`:
- Around line 221-223: The current conditional branch hides primary action
buttons when latestVersion is falsy, blocking edits/evaluations even though
handleEdit and handleUseInEvaluation can work with only config.id; update the UI
so the "Edit" and "Use in evaluation" buttons are rendered regardless of
latestVersion (either move the buttons out of the latestVersion conditional or
duplicate them in the fallback), ensuring their onClick calls still invoke
handleEdit and handleUseInEvaluation with config.id (and latestVersion where
available). Locate the buttons and the conditional using the symbols
latestVersion, handleEdit, handleUseInEvaluation, and config.id in
ConfigCard.tsx and adjust the markup so primary actions remain visible on lazy
detail load failure.
- Around line 44-47: The fast-path that returns early when latestVersion is set
(in the component using latestVersion, setExpanded, and onLoadVersions) causes
stale UI after parent updates; change the logic to also check the config
metadata timestamp (e.g., config.updated_at) or reset the cached detail state
when config.updated_at changes so that latestVersion is cleared and
onLoadVersions runs again on refresh; update the fast-path in the block
referencing latestVersion (and similar checks between lines 50-80 and 84-97) to
include a guard comparing stored metadata timestamp to config.updated_at or call
a state reset to clear latestVersion before returning.

---

Nitpick comments:
In `@app/components/icons/prompt-editor/ChevronRightIcon.tsx`:
- Around line 1-19: Extract the duplicated IconProps interface into a shared
module and update the icon component to import it: create a new module exporting
interface IconProps { className?: string; style?: React.CSSProperties } and
replace the local IconProps declaration in ChevronRightIcon with an import;
ensure ChevronRightIcon({ className, style }: IconProps) still types correctly
and update any other icon files to import the shared IconProps to remove
duplication.

In `@app/components/prompt-editor/ConfigEditorPane.tsx`:
- Around line 780-784: The JSX in ConfigEditorPane.tsx is mixing Tailwind color
classes (e.g., "border-gray-300", "text-white", and hardcoded hex borders like
"border-r-[`#1f2937`]") with inline styles; remove these color-related Tailwind
classes and instead use the shared theme tokens from the colors object in the
element's style (e.g., set borderColor, backgroundColor, color via
colors.border.*, colors.bg.*, colors.text.*) so all color values come from the
shared palette; update the same change for the other affected JSX blocks
referenced (around the other ranges) so no component uses hardcoded hex/Tailwind
color classes and all color styling is done via the colors object.

In `@app/components/SimplifiedConfigEditor.tsx`:
- Around line 30-43: The local SavedConfig interface in
SimplifiedConfigEditor.tsx duplicates the shared type; replace it by importing
the shared SavedConfig from "@/app/lib/types/configs" and either use it directly
or extend it to add any component-specific optional fields (e.g., description,
type, promptContent) to avoid divergence; update references in this file to use
the imported/extended SavedConfig type (look for the SavedConfig interface and
any functions or props that consume it) and remove the local duplicate
declaration.

In `@app/lib/utils.ts`:
- Around line 4-10: Consolidate the two import statements into one that uses the
"@/app/lib/types/configs" alias: import ConfigPublic, ConfigVersionPublic,
ConfigVersionItems, Tool, SavedConfig, and ConfigGroup from the same module so
all types are imported consistently; update the existing imports in
app/lib/utils.ts (symbols: ConfigPublic, ConfigVersionPublic,
ConfigVersionItems, Tool, SavedConfig, ConfigGroup) to a single aliased import
and remove the relative "./types/configs" import.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ca100cfd-3f06-4753-9a5f-3c3a28b044e4

📥 Commits

Reviewing files that changed from the base of the PR and between 419b9e7 and 78c33cb.

📒 Files selected for processing (20)
  • app/(routes)/configurations/page.tsx
  • app/(routes)/configurations/prompt-editor/page.tsx
  • app/components/ConfigCard.tsx
  • app/components/ConfigDrawer.tsx
  • app/components/ConfigModal.tsx
  • app/components/SimplifiedConfigEditor.tsx
  • app/components/icons/index.tsx
  • app/components/icons/prompt-editor/ChevronRightIcon.tsx
  • app/components/icons/prompt-editor/InfoIcon.tsx
  • app/components/icons/prompt-editor/PlusIcon.tsx
  • app/components/icons/prompt-editor/SpinnerIcon.tsx
  • app/components/prompt-editor/ConfigDrawer.tsx
  • app/components/prompt-editor/ConfigEditorPane.tsx
  • app/components/prompt-editor/HistorySidebar.tsx
  • app/hooks/useConfigs.ts
  • app/lib/configFetchers.ts
  • app/lib/configTypes.ts
  • app/lib/store/configStore.ts
  • app/lib/types/configs.ts
  • app/lib/utils.ts
💤 Files with no reviewable changes (1)
  • app/lib/configTypes.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.

🧹 Nitpick comments (1)
app/lib/types/configs.ts (1)

69-80: Consider documenting the reason for the index signature.

The [key: string]: any index signature weakens type safety. While this is acknowledged by the eslint-disable comment, consider adding a brief comment explaining why it's needed (e.g., "allows pass-through of additional backend-specific fields"). This helps future maintainers understand the intent.

📝 Suggested documentation improvement
   // Backend expects these as direct fields (flattened from tools array)
   knowledge_base_ids?: string[];
   max_num_results?: number;
-  // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
+  // Allow additional backend-specific fields to pass through without strict typing
+  // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
   [key: string]: any;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/types/configs.ts` around lines 69 - 80, The index signature "[key:
string]: any" on the CompletionParams interface weakens type safety and lacks
intent documentation; update the CompletionParams declaration to include a
concise comment above the index signature (and retain the existing
eslint-disable) explaining why it's required — e.g., "allows pass-through of
additional backend-specific or provider-specific fields (flattened from frontend
tools) for compatibility" — so future maintainers understand its purpose;
reference the CompletionParams interface and the existing index signature when
making the comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/lib/types/configs.ts`:
- Around line 69-80: The index signature "[key: string]: any" on the
CompletionParams interface weakens type safety and lacks intent documentation;
update the CompletionParams declaration to include a concise comment above the
index signature (and retain the existing eslint-disable) explaining why it's
required — e.g., "allows pass-through of additional backend-specific or
provider-specific fields (flattened from frontend tools) for compatibility" — so
future maintainers understand its purpose; reference the CompletionParams
interface and the existing index signature when making the comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb6f3b05-1c07-4ee7-b8d5-eba3cb03c6f2

📥 Commits

Reviewing files that changed from the base of the PR and between 78c33cb and 42ad650.

📒 Files selected for processing (2)
  • app/(routes)/configurations/prompt-editor/page.tsx
  • app/lib/types/configs.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/(routes)/configurations/prompt-editor/page.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: 1

🧹 Nitpick comments (4)
app/components/Loader.tsx (1)

74-74: Consider migrating LoaderBox states to skeleton/pulse instead of spinner

Since LoaderBox now directly exposes spinner-based loading at Line 74, this is a good point to align with the component loading convention and use Tailwind pulse skeletons for these states.

Based on learnings: Use skeleton loaders with Tailwind's pulse animation for loading states instead of spinners or other loading indicators.

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

In `@app/components/Loader.tsx` at line 74, The LoaderBox currently returns a
spinner via the Loader component (return <Loader size={size} message={message}
/>); replace that spinner-based UI with a Tailwind "skeleton/pulse" pattern:
update LoaderBox (and the Loader component if it enforces spinner) to render one
or more divs styled with Tailwind classes such as animate-pulse, rounded, and
bg-gray-200/neutral variants sized by the existing size prop, and optionally a
smaller/text bar for message instead of an actual spinner; ensure the new
skeleton respects the same props (size, message) and accessibility
(aria-busy/aria-label) as the previous Loader so consumers of LoaderBox get the
pulse skeleton loading state.
app/hooks/usePaginatedList.ts (2)

126-137: Consider surfacing loadMore errors to the UI.

Currently, loadMore catches errors and logs to console (line 132), while refetch sets the error state. This inconsistency means users won't see feedback if a pagination request fails.

🔧 Proposed fix to set error state
     fetchPage(skipRef.current, false)
-      .catch((e) => console.error(`Failed to load more from ${endpoint}:`, e))
+      .catch((e) => {
+        console.error(`Failed to load more from ${endpoint}:`, e);
+        setError(e.message);
+      })
       .finally(() => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/usePaginatedList.ts` around lines 126 - 137, The loadMore handler
currently swallows errors by only logging to console; update loadMore (the
function using loadingMoreRef, setIsLoadingMore and fetchPage) to surface
failures to the same error state used by refetch by calling the shared setError
(or setFetchError) in the catch block instead of only console.error, passing the
caught error (and optionally contextual info like endpoint); keep the finally
block that resets loadingMoreRef and setIsLoadingMore unchanged so UI loading
state is cleared even on error.

85-87: Consider including the API error message in the thrown error.

The response interface includes an error field (line 26), but when the request fails, the thrown error uses a generic message. Including the API's error message would help with debugging.

🔧 Proposed fix
       if (!data.success || !data.data) {
-        throw new Error(`Failed to fetch from ${endpoint}.`);
+        throw new Error(data.error || `Failed to fetch from ${endpoint}.`);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/usePaginatedList.ts` around lines 85 - 87, The thrown error when a
fetch fails in usePaginatedList.ts currently uses a generic message; modify the
failure branch that checks if (!data.success || !data.data) to include the
API-provided error (data.error) in the thrown Error alongside the endpoint so
the message becomes descriptive (e.g., include `${endpoint}: ${data.error ||
'unknown error'}`); update any related throw site inside the function handling
fetch responses (references: data.success, data.data, data.error, endpoint) so
callers receive the API error text.
app/hooks/useInfiniteScroll.ts (1)

25-39: Consider debouncing or additional guard for short-content scenarios.

When scrollHeight <= clientHeight (lines 31-34), onLoadMore fires immediately on every render cycle where isLoading is false. If the fetched content still doesn't fill the viewport, this can trigger rapid successive calls before isLoadingMore propagates back.

The current design relies on loadMore in usePaginatedList having its own loadingMoreRef guard, which should prevent actual duplicate fetches. However, consumers might pass a different onLoadMore without such protection.

Consider either:

  1. Documenting that isLoading should be isLoading || isLoadingMore when used with usePaginatedList
  2. Adding a local debounce/throttle for the short-content case
📝 Suggestion: Add JSDoc note for consumers
 /**
  * Generic infinite-scroll hook.
  * Attach the returned ref to a scrollable container; `onLoadMore` fires
  * when the user scrolls within `threshold` pixels of the bottom.
  *
  * Reusable across any paginated list (configs, datasets, evaluations, etc.).
+ *
+ * `@param` isLoading - Pass `true` while any fetch is in-flight (initial or pagination)
+ *                    to prevent premature `onLoadMore` calls. For usePaginatedList,
+ *                    pass `isLoading || isLoadingMore`.
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/useInfiniteScroll.ts` around lines 25 - 39, In useInfiniteScroll
(checkShouldLoad), add a local short-call guard to avoid repeatedly invoking
onLoadMore when scrollHeight <= clientHeight: create a ref like shortLoadRef (or
a small debounce using setTimeout) and before calling onLoadMore check
!shortLoadRef.current && !isLoading; when you call onLoadMore set
shortLoadRef.current = true and clear it when isLoading becomes true or after a
small timeout (or when content size increases), and keep existing checks for
hasMore, isLoading, threshold, scrollRef to ensure compatibility with consumers
who don't provide their own loading guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/Loader.tsx`:
- Line 74: The component now returns only <Loader />, removing the structural
wrapper (<LoaderBox>) and breaking spacing/box affordances; restore the default
boxed wrapper by returning the original LoaderBox structure that wraps <Loader
/> (and/or a wrapper div with the same classes) and add an explicit prop (e.g.,
inline or boxed) on the Loader/LoaderBox API so callers can opt into inline
rendering; update the Loader.tsx export (LoaderBox / Loader) to honor that prop
and preserve existing usage where forms expect the boxed layout.

---

Nitpick comments:
In `@app/components/Loader.tsx`:
- Line 74: The LoaderBox currently returns a spinner via the Loader component
(return <Loader size={size} message={message} />); replace that spinner-based UI
with a Tailwind "skeleton/pulse" pattern: update LoaderBox (and the Loader
component if it enforces spinner) to render one or more divs styled with
Tailwind classes such as animate-pulse, rounded, and bg-gray-200/neutral
variants sized by the existing size prop, and optionally a smaller/text bar for
message instead of an actual spinner; ensure the new skeleton respects the same
props (size, message) and accessibility (aria-busy/aria-label) as the previous
Loader so consumers of LoaderBox get the pulse skeleton loading state.

In `@app/hooks/useInfiniteScroll.ts`:
- Around line 25-39: In useInfiniteScroll (checkShouldLoad), add a local
short-call guard to avoid repeatedly invoking onLoadMore when scrollHeight <=
clientHeight: create a ref like shortLoadRef (or a small debounce using
setTimeout) and before calling onLoadMore check !shortLoadRef.current &&
!isLoading; when you call onLoadMore set shortLoadRef.current = true and clear
it when isLoading becomes true or after a small timeout (or when content size
increases), and keep existing checks for hasMore, isLoading, threshold,
scrollRef to ensure compatibility with consumers who don't provide their own
loading guard.

In `@app/hooks/usePaginatedList.ts`:
- Around line 126-137: The loadMore handler currently swallows errors by only
logging to console; update loadMore (the function using loadingMoreRef,
setIsLoadingMore and fetchPage) to surface failures to the same error state used
by refetch by calling the shared setError (or setFetchError) in the catch block
instead of only console.error, passing the caught error (and optionally
contextual info like endpoint); keep the finally block that resets
loadingMoreRef and setIsLoadingMore unchanged so UI loading state is cleared
even on error.
- Around line 85-87: The thrown error when a fetch fails in usePaginatedList.ts
currently uses a generic message; modify the failure branch that checks if
(!data.success || !data.data) to include the API-provided error (data.error) in
the thrown Error alongside the endpoint so the message becomes descriptive
(e.g., include `${endpoint}: ${data.error || 'unknown error'}`); update any
related throw site inside the function handling fetch responses (references:
data.success, data.data, data.error, endpoint) so callers receive the API error
text.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 92716a4a-263a-4bb3-b3da-2a538cbc30da

📥 Commits

Reviewing files that changed from the base of the PR and between 3274e9d and 0c1d83c.

📒 Files selected for processing (18)
  • app/(routes)/configurations/page.tsx
  • app/api/configs/route.ts
  • app/components/Loader.tsx
  • app/components/icons/common/CheckIcon.tsx
  • app/components/icons/common/ChevronDownIcon.tsx
  • app/components/icons/common/GearIcon.tsx
  • app/components/icons/common/PlusIcon.tsx
  • app/components/icons/common/RefreshIcon.tsx
  • app/components/icons/common/SearchIcon.tsx
  • app/components/icons/common/SidebarToggleIcon.tsx
  • app/components/icons/common/WarningTriangleIcon.tsx
  • app/components/icons/index.tsx
  • app/hooks/useConfigs.ts
  • app/hooks/useInfiniteScroll.ts
  • app/hooks/usePaginatedList.ts
  • app/lib/constants.ts
  • app/lib/context/AuthContext.tsx
  • eslint.config.mjs
💤 Files with no reviewable changes (1)
  • app/lib/context/AuthContext.tsx
✅ Files skipped from review due to trivial changes (1)
  • eslint.config.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/hooks/useConfigs.ts
  • app/(routes)/configurations/page.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: 1

♻️ Duplicate comments (1)
app/(routes)/configurations/page.tsx (1)

84-92: ⚠️ Potential issue | 🟠 Major

Keep the cards in a single DOM list.

Line 90 still round-robins configs into per-column arrays, and Lines 397-408 render those arrays column-by-column. That keeps the visual layout, but tab/screen-reader order becomes 0,3,6... instead of row order, and ConfigCard local state is remounted whenever cards move between column wrappers. Render configs.map(...) once and let CSS place the cards.

Flatten the render tree
- {columns.map((col, colIdx) => (
-   <div key={colIdx} className="flex flex-col gap-4">
-     {col.map((config) => (
-       <ConfigCard
-         key={config.id}
-         config={config}
-         evaluationCount={evaluationCounts[config.id] || 0}
-         onLoadVersions={loadVersionsForConfig}
-         onLoadSingleVersion={loadSingleVersion}
-       />
-     ))}
-   </div>
- ))}
+ {configs.map((config) => (
+   <ConfigCard
+     key={config.id}
+     config={config}
+     evaluationCount={evaluationCounts[config.id] || 0}
+     onLoadVersions={loadVersionsForConfig}
+     onLoadSingleVersion={loadSingleVersion}
+   />
+ ))}

Also applies to: 393-409

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

In `@app/`(routes)/configurations/page.tsx around lines 84 - 92, The current
useMemo block named columns (created by useMemo with configs and columnCount)
and the rendering that iterates over those per-column arrays causes DOM order
and remounting issues for ConfigCard; instead remove the round-robin grouping
and render a single flat list by calling configs.map(...) once (render each
ConfigCard in original array order), let CSS handle the multi-column visual
placement (use columnCount-driven grid or column styles), and ensure ConfigCard
instances are not wrapped by per-column containers so their local state is
preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/`(routes)/configurations/page.tsx:
- Around line 125-149: The cached version list (configState.versionItemsCache)
used by loadVersionsForConfig prevents refreshed server data from being shown;
update the refetch/refresh path to invalidate or version that cache so cards
will reload fresh. Concretely, when the user triggers refetch (the handler
calling refetch), clear the specific config's entry or the entire
configState.versionItemsCache and also clear pendingVersionLoads for that config
(or increment a refresh/version counter stored alongside the cache and have
loadVersionsForConfig check it) so loadVersionsForConfig(configId) will fetch
from the server instead of returning the stale cached array.

---

Duplicate comments:
In `@app/`(routes)/configurations/page.tsx:
- Around line 84-92: The current useMemo block named columns (created by useMemo
with configs and columnCount) and the rendering that iterates over those
per-column arrays causes DOM order and remounting issues for ConfigCard; instead
remove the round-robin grouping and render a single flat list by calling
configs.map(...) once (render each ConfigCard in original array order), let CSS
handle the multi-column visual placement (use columnCount-driven grid or column
styles), and ensure ConfigCard instances are not wrapped by per-column
containers so their local state is preserved.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87cdafe2-70b8-4c8a-951e-189aafb4c8a0

📥 Commits

Reviewing files that changed from the base of the PR and between 0c1d83c and 7a5b11f.

📒 Files selected for processing (3)
  • app/(routes)/configurations/page.tsx
  • app/components/Loader.tsx
  • app/globals.css
✅ Files skipped from review due to trivial changes (1)
  • app/globals.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/Loader.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant