Skip to content

Feat: Add review concurrency/comment limits, motion UI components, and stats page refactor#26

Open
devarshishimpi wants to merge 12 commits into
devfrom
feature/ui-slider-jobs-concurrency-refactor
Open

Feat: Add review concurrency/comment limits, motion UI components, and stats page refactor#26
devarshishimpi wants to merge 12 commits into
devfrom
feature/ui-slider-jobs-concurrency-refactor

Conversation

@devarshishimpi

Copy link
Copy Markdown
Owner

Description

This branch bundles several related improvements to the review pipeline and dashboard UI:

  • Review performance settings: New SteppedSlider and ConfirmDialog components let users configure job concurrency and max comment limits from the Settings page, with autosave and API routes (/api/settings) backed by a new 004_review_performance_settings.sql migration. Review logic in review.ts now respects these limits.
  • Diff parsing & custom file matchers: Refactored diff.ts/review.ts to support custom file matchers when parsing diffs during review.
  • Motion UI components: Added tabs.tsx for motion-based tabs, reworked dropdown-menu.tsx and select.tsx, and integrated Lenis for smooth scrolling (smooth-scroll.tsx).
  • Stats page refactor: Consolidated stats page graphs into a single MetricsGrid component, expanded stats.ts to return job statuses, triggers, severities, categories, and performance metrics, and removed unused rpm/tpm/rpd fields from model configs (005_drop_model_rate_limits.sql), including matching schema, route, and validation cleanup.

Closes #22 #23

Type of change

  • New feature (non-breaking change which adds functionality)
  • Chore (refactoring, dependency updates, etc.)

How Has This Been Tested?

  • Unit Tests (test/api.spec.ts, test/review-flow.spec.ts updated for new data structures)
  • Manual Dashboard Verification (settings sliders, stats page layout)

Checklist:

  • I have starred Codra on GitHub
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have signed the CLA

- Implemented database migration to insert default review settings.
- Created a SteppedSlider component for selecting concurrency levels and max comments.
- Added ConfirmDialog component for user confirmation on exceeding limits.
- Updated SettingsPage to manage and display review settings with automatic saving.
- Introduced API routes for fetching and updating review settings.
- Enhanced review logic to respect concurrency and comment limits based on user settings.
- Updated the stats page to improve the layout and structure of metrics display, consolidating various graph components into a more cohesive MetricsGrid component.
- Removed unused properties (rpm, tpm, rpd) from model configurations in the database schema and related queries.
- Enhanced the stats retrieval function to include additional metrics such as job statuses, triggers, severities, categories, and performance metrics.
- Updated the shared schema to reflect changes in the stats structure, including new fields for statuses, triggers, severities, categories, and performance.
- Adjusted API model routes and validation schemas to remove references to removed properties.
- Updated tests to align with the new data structure and ensure proper functionality.
…ew settings API, and add tests for concurrency limits

@codra-app-personal codra-app-personal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Codra Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e48346dc1

ℹ️ About Codra in GitHub

Your team has set up Codra to review pull requests in this repo. Reviews are triggered when you:

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codra-app review"

If Codra has suggestions, it will comment; otherwise it will react with 👍.

Codra can also answer questions or update the PR. Try commenting "@codra-app address that feedback".

Note

40 comments were omitted from this review to reduce noise and respect the configured max_comments limit (10). Showing the most critical issues.

Comment thread scripts/migrate.mjs
WHERE rpm = 1 AND tpm = 1 AND rpd = 1
`);

await query(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Potential database constraint violation in migration

The migration removes the commands that make 'rpm', 'tpm', and 'rpd' columns nullable (DROP NOT NULL), while simultaneously removing those columns from the subsequent INSERT statements. If these columns remain in the schema as NOT NULL and lack default values, the INSERT queries will fail with a constraint violation error. If these fields are deprecated, they should be explicitly dropped from the table using 'ALTER TABLE model_configs DROP COLUMN ...' to ensure schema consistency across environments.

Suggested change
await query(
// If the columns are no longer needed:
await query('ALTER TABLE model_configs DROP COLUMN IF EXISTS rpm');
await query('ALTER TABLE model_configs DROP COLUMN IF EXISTS tpm');
await query('ALTER TABLE model_configs DROP COLUMN IF EXISTS rpd');

import {
createContext,
useContext,
useEffect,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Massive loss of functionality during refactor

The refactor completely removes several critical components provided by the previous Radix-UI implementation: DropdownMenuSub, DropdownMenuCheckboxItem, DropdownMenuRadioItem, and DropdownMenuLabel. This is a breaking change that removes essential UI patterns (checkboxes, radio selections, and nested submenus) and should be flagged as a regression rather than a refactor.

</motion.button>

</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 SSR Crash via createPortal

The component calls createPortal(..., document.body) directly within the render path. In Server-Side Rendering (SSR) environments (e.g., Next.js), document is not defined on the server, which will cause the application to crash during the initial render. Portal targets should be managed within a useEffect or handled via a client-only mounting check.

Suggested change
// Use a state variable 'mounted' set to true in useEffect
{mounted && createPortal(<motion.div ... />, document.body)}

Comment thread src/server/core/diff.ts
@@ -93,21 +124,30 @@

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Deleted files lose all hunk content

In lines 125-128, when a line starts with 'deleted file mode ', the code sets 'isIgnored = true'. Because the check 'if (isIgnored) continue;' occurs at line 146 (before hunk processing), all content for deleted files is skipped. This means the resulting FileDiff object will have 'isDeleted: true' but an empty 'hunks' array, losing the actual changes being deleted.

Suggested change
@@ -93,21 +124,30 @@ export function parseUnifiedDiff(rawDiff: string): FileDiff[] {
Remove 'isIgnored = true;' from the 'deleted file mode' block. Deleted files should still have their hunks parsed to show what was removed.

setStyle({ position: 'fixed', top, left, visibility: 'visible' });
};
update();
window.addEventListener('scroll', update, true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 bottleneck in scroll listener

The useLayoutEffect adds a window 'scroll' event listener that calls 'setStyle' (a state update) on every single scroll event. Because it uses capture: true, this will trigger a re-render of the DropdownMenuContent component continuously during any page scroll while the menu is open, potentially leading to layout thrashing and decreased FPS.

Suggested change
window.addEventListener('scroll', update, true);
Consider using a requestAnimationFrame wrapper around the 'update' function or using CSS variables and updating them via ref.current.style.setProperty to avoid React state re-renders.

Comment thread src/client/app.css
@@ -903,8 +903,7 @@
gap: 0.625rem !important;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Excessive use of !important

The CSS relies heavily on !important for almost every property (e.g., lines 906, 917, 924, 994). This creates extreme specificity that makes the styles nearly impossible to override or extend without further !important declarations, leading to a 'specificity war' and making the codebase difficult to maintain.

Suggested change
gap: 0.625rem !important;
Remove !important and instead increase selector specificity or reorganize the CSS cascade to ensure styles are applied correctly.

{/* Portaled to <body> so the panel always renders above cards, tables, and
other stacking contexts — it can't be clipped/hidden by an ancestor. */}
{createPortal(
<motion.div

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Missing Keyboard Navigation for Listbox

The component implements role="listbox" and role="option", but lacks the required keyboard interaction patterns (Up/Down arrow keys to navigate options, Enter/Space to select). This is a significant accessibility regression from the previous DropdownMenu implementation, which typically handles these patterns automatically.

window.removeEventListener('keydown', onKey);
window.removeEventListener('pointerdown', onPointer);
};
}, [open]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Degradation in useLayoutEffect

The useLayoutEffect starting at line 100 is missing a dependency array. Consequently, it executes on every single render, creating and disconnecting a new ResizeObserver instance repeatedly. This will cause significant performance overhead, especially when the parent component re-renders frequently.

Suggested change
}, [open]);
useLayoutEffect(() => {
// ... existing logic
}, []); // Add empty dependency array or specific dependencies

setError(null);
const tid = toast.loading('Saving…');
try {
await api.updateReviewSettings(next);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Missing debounce for review settings updates

The handleConcurrencyChange and handleCommentsChange functions call persistReviewSettings immediately on every slider value change. Since these are linked to UI sliders, this will trigger a high volume of API requests in rapid succession, potentially overloading the server and causing unstable UI behavior. A debounce mechanism (similar to the one implemented for persistGlobalConfig on lines 385-389) should be used here.

Suggested change
await api.updateReviewSettings(next);
// Example: Move state update to a debounced effect
useEffect(() => {
if (!reviewSettings || !reviewSettingsDirty) return;
const handle = setTimeout(() => void persistReviewSettings(reviewSettings, 'Settings updated'), 600);
return () => clearTimeout(handle);
}, [reviewSettings, reviewSettingsDirty]);

Comment thread src/server/core/diff.ts
currentFile.path = nextPath.startsWith('b/') ? nextPath.slice(2) : nextPath;
if (reviewConfig) {
isIgnored = !isReviewableFile(currentFile.path, customMatchers);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Ignored files still included in output array

The 'isIgnored' flag is used to skip parsing the contents of a file (lines 146-148), but the file object itself is still created and subsequently pushed to the 'files' array via 'pushCurrentFile'. This results in the output containing multiple FileDiff objects with empty hunks, which is inefficient and likely incorrect for the consumer of the function.

Suggested change
}
Modify 'pushCurrentFile' to check if 'currentFile' is ignored before adding it to the 'files' array.

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