Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: jurors page #1840

Merged
merged 21 commits into from
Jan 30, 2025
Merged

feat: jurors page #1840

merged 21 commits into from
Jan 30, 2025

Conversation

kemuru
Copy link
Contributor

@kemuru kemuru commented Jan 20, 2025

PR-Codex overview

This PR focuses on refactoring the Kleros application to improve the user experience and code structure, specifically by renaming the Dashboard to Profile, updating routes, and enhancing components related to jurors and coherence metrics.

Detailed summary

  • Renamed Dashboard component to Profile.
  • Updated routing paths to reflect the new Profile structure.
  • Introduced JurorsLeaderboardButton component.
  • Added getCoherencePercent utility function.
  • Modified JurorInfo components to utilize new coherence calculations.
  • Adjusted styles in various components for better UI consistency.
  • Enhanced juror-related data queries to improve performance.
  • Simplified user level calculations based on coherence percentage.
  • Updated SVG icons and component imports for better organization.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new Jurors page with search functionality.
    • Added a Profile page, replacing the previous Dashboard.
    • Implemented a new component for displaying jurors based on coherence scores.
    • Added a new component for displaying juror statistics and filters.
    • Introduced a new component for displaying user profile information.
    • Added a new component for accessing the jurors leaderboard.
  • Navigation Changes

    • Replaced Dashboard route with Jurors route.
    • Updated navigation links to point to the new Profile page.
    • Removed Dashboard from the navigation menu.
  • UI Improvements

    • Refined styling for search results and card components.
    • Updated page titles and messaging to reflect the Profile context.
    • Enhanced user interface for profile information display.
    • Introduced new styled components for improved layout and interaction.
  • Performance

    • Maintained lazy loading for components.
    • Preserved existing routing structure with minimal changes.

Copy link
Contributor

coderabbitai bot commented Jan 20, 2025

Walkthrough

The pull request introduces significant changes to the web application's routing and navigation structure. The primary modification involves replacing the Dashboard component with a new Jurors component and creating a Profile component. This change affects multiple files across the project, updating import paths, route configurations, and navigation links. Additionally, new components for juror statistics and search functionality have been added, while existing components have been refactored to align with the new structure. The modifications enhance the user interface and provide a more focused navigation experience.

Changes

File Change Summary
web/src/app.tsx Replaced Dashboard route with Jurors route, added Profile route
web/src/components/EvidenceCard.tsx Updated navigation link from dashboard to profile
web/src/components/Popup/MiniGuides/JurorLevels.tsx Updated import paths for Coherence and PixelArt components
web/src/layout/Header/navbar/Explore.tsx Replaced "Dashboard" link with "Jurors" link
web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx Updated link from dashboard to profile
web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx Updated import path for PixelArt, changed property types in IJurorLevel interface
web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx Updated link from dashboard to profile
web/src/pages/Home/TopJurors/index.tsx Updated ListContainer and StyledLabel to be exported
web/src/pages/Jurors/* Added new components: Search.tsx, Stats.tsx, StatsAndFilters.tsx, index.tsx
web/src/pages/Profile/* Renamed Dashboard to Profile, updated component and text references
web/src/hooks/queries/useTopUsersByCoherenceScore.ts Hook removed, related to previous dashboard functionality
web/src/pages/Home/CourtOverview/Stats.tsx Exported getLastOrZero function
web/src/pages/Home/TopJurors/JurorCard/* Updated property types in interfaces from number to string
subgraph/core/schema.graphql Added new field totalLeaderboardJurors to Counter entity
subgraph/core/src/datapoint.ts Introduced new function updateTotalLeaderboardJurors
web/src/hooks/queries/useJurorsByCoherenceScore.ts Added new hook to fetch jurors by coherence score
web/src/hooks/queries/useTotalLeaderboardJurors.ts Introduced new hook for total leaderboard jurors
web/src/layout/Header/navbar/Menu/Settings/General/WalletAndProfile.tsx Added new component for user profile display

Possibly related PRs

Suggested labels

Type: Enhancement :sparkles:, Type: UX

Suggested reviewers

  • alcercu
  • Harman-singh-waraich

Poem

🐰 Routing rabbits hop and dance,
Dashboards fade, new paths advance!
Jurors shine, profiles gleam bright,
Navigation takes a playful flight!
Code transforms with bunny delight! 🌟


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6875eb and 2bb0a19.

📒 Files selected for processing (1)
  • web/README.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • web/README.md
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Analyze (javascript)
  • GitHub Check: contracts-testing
  • GitHub Check: SonarCloud

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kemuru kemuru changed the title feat: initial jurors page setup, jurors explore navlink, small stylin… feat: jurors page Jan 20, 2025
Copy link

netlify bot commented Jan 20, 2025

Deploy Preview for kleros-v2-neo ready!

Name Link
🔨 Latest commit 2bb0a19
🔍 Latest deploy log https://app.netlify.com/sites/kleros-v2-neo/deploys/679b65ad9d5f100007d0a6fb
😎 Deploy Preview https://deploy-preview-1840--kleros-v2-neo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jan 20, 2025

Deploy Preview for kleros-v2-university failed. Why did it fail? →

Name Link
🔨 Latest commit 2bb0a19
🔍 Latest deploy log https://app.netlify.com/sites/kleros-v2-university/deploys/679b65adc7c0230008fe539d

Copy link

netlify bot commented Jan 20, 2025

Deploy Preview for kleros-v2-testnet-devtools ready!

Name Link
🔨 Latest commit 2bb0a19
🔍 Latest deploy log https://app.netlify.com/sites/kleros-v2-testnet-devtools/deploys/679b65ad2d83a00008de97cc
😎 Deploy Preview https://deploy-preview-1840--kleros-v2-testnet-devtools.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jan 20, 2025

Deploy Preview for kleros-v2-testnet ready!

Name Link
🔨 Latest commit 2bb0a19
🔍 Latest deploy log https://app.netlify.com/sites/kleros-v2-testnet/deploys/679b65ad1d17f600083f43d1
😎 Deploy Preview https://deploy-preview-1840--kleros-v2-testnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

@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 (10)
web/src/pages/Jurors/StatsAndFilters.tsx (2)

7-14: Consider using semantic HTML for the container.

While the styling is good, consider using <section> instead of div for better semantic meaning since this represents a distinct section of the page.

-const Container = styled.div`
+const Container = styled.section`
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
   margin-top: ${responsiveSize(4, 8)};
   margin-bottom: ${responsiveSize(16, 32)};
   justify-content: space-between;
`;

16-20: Consider making props spreading more explicit.

While props spreading works, it's better to be explicit about which props are being passed for better maintainability and type safety.

-const StatsAndFilters: React.FC<IStats> = ({ totalJurors }) => (
+const StatsAndFilters: React.FC<IStats> = ({ totalJurors }) => (
   <Container>
-    <Stats {...{ totalJurors }} />
+    <Stats totalJurors={totalJurors} />
   </Container>
 );
web/src/pages/Jurors/Stats.tsx (2)

13-18: Enhance Field component reusability and accessibility.

The Field component could be more reusable and accessible with these improvements:

  • Add htmlFor attribute to label
  • Add ARIA attributes
  • Make the value styling more flexible
-const Field: React.FC<{ label: string; value: string }> = ({ label, value }) => (
+interface FieldProps {
+  label: string;
+  value: string;
+  id?: string;
+  valueClassName?: string;
+}
+
+const Field: React.FC<FieldProps> = ({ label, value, id = label.toLowerCase(), valueClassName }) => (
   <FieldWrapper>
-    <StyledLabel>{label}</StyledLabel>
-    <small>{value}</small>
+    <StyledLabel htmlFor={id}>{label}</StyledLabel>
+    <small id={id} className={valueClassName} aria-label={`${label}: ${value}`}>
+      {value}
+    </small>
   </FieldWrapper>
 );

24-26: Consider adding prop types validation.

Add runtime validation for the totalJurors prop to ensure it's a positive number.

 const Stats: React.FC<IStats> = ({ totalJurors }) => {
+  if (totalJurors < 0) {
+    console.warn('totalJurors should be a positive number');
+    return null;
+  }
   return <Field label="Total" value={`${totalJurors} Jurors`} />;
 };
web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (1)

37-37: Consider extracting route constants.

The profile link structure should be defined in a central location for better maintainability.

+const ROUTES = {
+  PROFILE: (address: string) => `/profile/1/desc/all?address=${address}`,
+} as const;

-  const profileLink = `/profile/1/desc/all?address=${address}`;
+  const profileLink = ROUTES.PROFILE(address);
web/src/pages/Home/TopJurors/index.tsx (2)

Line range hint 25-37: Consider adding media query breakpoints for grid columns.

The grid layout could be more responsive with multiple column breakpoints.

 export const ListContainer = styled.div`
   display: flex;
   flex-direction: column;
   justify-content: center;

   ${landscapeStyle(
     () => css`
       display: grid;
-      grid-template-columns: 1fr;
+      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+      gap: ${responsiveSize(16, 24)};
     `
   )}
 `;

38-40: Consider adding text ellipsis for long labels.

Add text overflow handling for better UI consistency.

 export const StyledLabel = styled.label`
   font-size: 16px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 `;
web/src/pages/Jurors/index.tsx (2)

38-38: Consider making the jurors limit configurable.

The hard-coded limit of 1000 jurors should be moved to a configuration constant for better maintainability and flexibility.

-  const { data: queryJurors } = useTopUsersByCoherenceScore(1000);
+  const JURORS_LIMIT = 1000; // Move to config file
+  const { data: queryJurors } = useTopUsersByCoherenceScore(JURORS_LIMIT);

40-43: Extract rank calculation to a utility function.

The rank calculation logic should be moved to a separate utility function for reusability and better separation of concerns.

+const addRankToJurors = (jurors) => 
+  jurors?.map((juror, index) => ({
+    ...juror,
+    rank: index + 1,
+  }));

-  const topJurors = queryJurors?.users?.map((juror, index) => ({
-    ...juror,
-    rank: index + 1,
-  }));
+  const topJurors = addRankToJurors(queryJurors?.users);
web/src/pages/Profile/index.tsx (1)

100-102: Consider improving the wallet connection message.

The current message is quite basic. Consider providing more context about the benefits of connecting.

-          To see your profile, connect first
+          Connect your wallet to view your profile and manage your juror activities
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3110e46 and ed63810.

📒 Files selected for processing (15)
  • web/src/app.tsx (2 hunks)
  • web/src/components/EvidenceCard.tsx (2 hunks)
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx (1 hunks)
  • web/src/layout/Header/navbar/Explore.tsx (1 hunks)
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx (1 hunks)
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/index.tsx (2 hunks)
  • web/src/pages/Jurors/Search.tsx (1 hunks)
  • web/src/pages/Jurors/Stats.tsx (1 hunks)
  • web/src/pages/Jurors/StatsAndFilters.tsx (1 hunks)
  • web/src/pages/Jurors/index.tsx (1 hunks)
  • web/src/pages/Profile/JurorInfo/Header.tsx (1 hunks)
  • web/src/pages/Profile/index.tsx (3 hunks)
✅ Files skipped from review due to trivial changes (4)
  • web/src/pages/Profile/JurorInfo/Header.tsx
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
🔇 Additional comments (6)
web/src/layout/Header/navbar/Explore.tsx (1)

57-57: LGTM! Navigation link addition aligns with new jurors page.

The addition of the jurors link is well-placed in the navigation structure and correctly points to the new jurors page route.

web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx (1)

91-91: LGTM! Profile link update aligns with navigation restructuring.

The replacement of dashboard link with profile link is consistent with the broader architectural changes in the PR.

Also applies to: 97-97

web/src/pages/Profile/index.tsx (1)

Line range hint 53-111: LGTM! Component transition from Dashboard to Profile is well-implemented.

The component maintains its core functionality while appropriately reflecting the new Profile context. The implementation correctly handles:

  • Wallet connection states
  • URL parameters and navigation
  • Data fetching with proper pagination
  • Conditional rendering based on user state
web/src/app.tsx (2)

17-19: LGTM! Proper lazy loading implementation.

The lazy loading of new components follows the established pattern and maintains consistency with other route components.


76-81: LGTM! Profile route parameters are well-defined.

The route pattern profile/:page/:order/:filter maintains consistency with the component's expectations and provides necessary navigation flexibility.

web/src/components/EvidenceCard.tsx (1)

226-226: LGTM! Profile link implementation is consistent.

The transition from dashboard to profile link maintains the expected URL structure and properly handles address parameters.

However, let's verify that all dashboard links have been updated across the codebase:

Also applies to: 251-253

✅ Verification successful

Dashboard to profile link transition is complete

The codebase shows no remaining dashboard navigation links or routes, confirming that the transition to profile links is consistent. The only dashboard references found are in image asset paths which don't affect navigation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining dashboard references in routes or links
rg -i "dashboard.*link|/dashboard" src/

Length of output: 86


Script:

#!/bin/bash
# Search for dashboard references in the web source directory
rg -i "dashboard.*link|/dashboard|route.*dashboard" web/src/

# Also search specifically for route configurations
ast-grep --pattern 'path = "/dashboard"' web/src/
ast-grep --pattern 'path: "/dashboard"' web/src/

Length of output: 714

Copy link
Contributor

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

🧹 Nitpick comments (6)
web/src/pages/Home/CourtOverview/Stats.tsx (3)

40-41: Consider adding input validation.

The function export is a good change for reusability. However, consider adding input validation for undefined/null values to make it more robust.

 export const getLastOrZero = (src: HomePageQuery["counters"], stat: HomePageQueryDataPoints) =>
-  src.length > 0 ? src.at(-1)?.[stat] : 0n.toString();
+  !src ? "0" : src.length > 0 ? src.at(-1)?.[stat] ?? "0" : "0";

Line range hint 52-89: Consider performance optimizations.

The stats configuration array could be memoized since it's static. Additionally, price calculations could benefit from memoization to prevent unnecessary recalculations on re-renders.

+import { useMemo } from "react";
+
-const stats: IStat[] = [
+const useStats = (): IStat[] => useMemo(() => [
   {
     title: "PNK Staked",
     coinId: 0,
     getText: (counters) => formatPNK(getLastOrZero(counters, "stakedPNK")),
     getSubtext: (counters, coinPrice) =>
       formatUSD(Number(formatUnitsWei(getLastOrZero(counters, "stakedPNK"))) * (coinPrice ?? 0)),
     color: "purple",
     icon: PNKIcon,
   },
   // ... other stats
-];
+], []);

 const Stats = () => {
+  const stats = useStats();
   // ... rest of the component
 };

Line range hint 90-109: Well-structured implementation with good error handling.

The component handles loading states and responsiveness well. Consider using a more specific type for the map index parameter.

-      {stats.map(({ title, coinId, getText, getSubtext, color, icon }, i) => {
+      {stats.map(({ title, coinId, getText, getSubtext, color, icon }, i: number) => {
web/src/hooks/queries/useTopUsersByCoherenceScore.ts (1)

Line range hint 20-39: Consider making the hook more flexible and review caching strategy.

The implementation is solid, but consider these improvements:

  1. Make orderBy and orderDirection configurable through hook parameters to support different sorting options.
  2. Review if staleTime: Infinity is appropriate - if coherence scores update frequently, you might want to reduce this value.

Example implementation:

export const useTopUsersByCoherenceScore = (
  first = 5,
  orderBy: "coherenceScore" | "totalResolvedVotes" = "coherenceScore",
  orderDirection: "asc" | "desc" = "desc",
  staleTime = Infinity
) => {
  // ... rest of the implementation
}
web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (2)

14-17: Consider adding input validation

The getPercent function assumes valid number conversions. Consider adding validation for the string-to-number conversion.

 const getPercent = (num: number, den: number): string => {
+  if (isNaN(num) || isNaN(den)) return "0%";
   if (den === 0) return "0%";
   return `${Math.floor((num * 100) / den)}%`;
 };

29-29: Consider moving type conversion

Move the Number() conversions closer to the component's input to maintain consistent types throughout the component.

-      <Tooltip text={coherenceRatio}>{getPercent(Number(totalCoherentVotes), Number(totalResolvedVotes))}</Tooltip>
+      const numCoherentVotes = Number(totalCoherentVotes);
+      const numResolvedVotes = Number(totalResolvedVotes);
+      <Tooltip text={coherenceRatio}>{getPercent(numCoherentVotes, numResolvedVotes)}</Tooltip>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed63810 and 1b22294.

📒 Files selected for processing (8)
  • web/src/hooks/queries/useTopUsersByCoherenceScore.ts (1 hunks)
  • web/src/pages/Home/CourtOverview/Stats.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/index.tsx (1 hunks)
  • web/src/pages/Jurors/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/pages/Jurors/index.tsx
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx
🧰 Additional context used
📓 Learnings (1)
web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (1)
Learnt from: Harman-singh-waraich
PR: kleros/kleros-v2#1739
File: web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx:22-26
Timestamp: 2024-11-12T04:49:43.234Z
Learning: In the `Coherency` component (`web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx`), `totalResolvedVotes` is always greater than or equal to `totalCoherentVotes`. When both are zero, `0/0` results in `NaN`, which is acceptable in this context.
⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: Redirect rules - kleros-v2-university
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-university
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-university
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: dependency-review
  • GitHub Check: SonarCloud
  • GitHub Check: contracts-testing
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
web/src/hooks/queries/useTopUsersByCoherenceScore.ts (1)

12-12: LGTM! Good addition of the filter condition.

The addition of where: { totalResolvedVotes_gt: 0 } is a good improvement as it ensures the leaderboard only shows active jurors who have participated in voting. This makes the data more meaningful and relevant for the Jurors page.

Let's verify if there are any other queries that might need similar filtering:

✅ Verification successful

The filter is correctly applied only where needed

The totalResolvedVotes_gt: 0 filter is specific to the jurors leaderboard use case and doesn't need to be applied elsewhere. Other user-related queries in the codebase serve different purposes (authentication, user management, single user fetches) and don't require this filtering.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar GraphQL queries that might benefit from the same filter
ast-grep --pattern 'query $_($first: Int!, $_) {
  users(first: $first, $_) {
    $$$
  }
}'

Length of output: 94


Script:

#!/bin/bash
# Search with a more flexible pattern for GraphQL queries
ast-grep --pattern 'query $_ {
  users($_) {
    $$$
  }
}'

# Backup search with ripgrep
rg -l "query.*users.*\{" --type ts --type graphql

Length of output: 145


Script:

#!/bin/bash
# Search for GraphQL queries in TypeScript files with context
rg -A 5 -B 5 "gql|graphql\`.*users.*\`" --type ts

# Specific search for the query structure
rg -A 5 -B 5 "users\s*\([^)]*first:" --type ts

Length of output: 9163

web/src/pages/Home/TopJurors/JurorCard/index.tsx (1)

9-12: Verify data type conversion point

The change from number to string types suggests these values are received as strings from an external source. Verify where the type conversion occurs (API, GraphQL, etc.) to ensure consistency and prevent multiple conversions.

✅ Verification successful

Type conversion pattern is correct

The string types are appropriate as these metrics originate as BigInt in the subgraph, are transmitted through GraphQL as strings, and are only converted to numbers when numerical operations are needed (e.g., in Profile/JurorInfo components).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for GraphQL queries or API calls fetching these metrics
rg -t typescript -t javascript "query.*\b(coherenceScore|totalCoherentVotes|totalResolvedVotes|totalResolvedDisputes)\b" 

Length of output: 157


Script:

#!/bin/bash
# Search for all occurrences of these metrics without file type restriction
rg "coherenceScore|totalCoherentVotes|totalResolvedVotes|totalResolvedDisputes"

# Look for potential GraphQL query definitions
ast-grep --pattern 'const $_ = gql`
  query $_ {
    $$$
  }
`'

# Look for type definitions related to these metrics
ast-grep --pattern 'type $_ {
  $$$
  coherenceScore
  $$$
}'

Length of output: 9935

web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx (1)

42-45: LGTM!

The type changes are consistent with the parent component and the values are properly passed to child components.

web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (1)

83-86: LGTM!

The type changes are consistent with the parent component and the desktop variant.

Copy link
Contributor

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

♻️ Duplicate comments (1)
web/src/pages/Jurors/Search.tsx (1)

32-45: ⚠️ Potential issue

Add input sanitization before URL encoding.

The search input should be sanitized before being encoded in the URL to prevent potential XSS attacks.

🧹 Nitpick comments (20)
web/src/pages/Jurors/Stats.tsx (3)

8-20: Consider using theme constants for spacing values.

While the styled components are well-structured, consider moving the gap values (8px, 4px) to theme constants for better maintainability and consistency across the application.

+// In your theme file
+const theme = {
+  // ... other theme values
+  spacing: {
+    xs: '4px',
+    sm: '8px',
+    // ... other spacing values
+  }
+}

 const FieldWrapper = styled.div`
   display: inline-flex;
-  gap: 8px;
+  gap: ${({ theme }) => theme.spacing.sm};
 `;

 const ValueAndExtraLabel = styled.div`
   display: flex;
-  gap: 4px;
+  gap: ${({ theme }) => theme.spacing.xs};
 `;

22-30: Extract props interface and skeleton width constant.

Consider these improvements for better maintainability:

  1. Extract the props interface
  2. Move the skeleton width to a constant
+interface FieldProps {
+  label: string;
+  value?: number;
+  extraLabel?: string;
+}
+
+const SKELETON_WIDTH = 16;
+
-const Field: React.FC<{ label: string; value?: number; extraLabel?: string }> = ({ label, value, extraLabel }) => (
+const Field: React.FC<FieldProps> = ({ label, value, extraLabel }) => (
   <FieldWrapper>
     <StyledLabel>{label}</StyledLabel>
     <ValueAndExtraLabel>
-      <small>{!isUndefined(value) ? value : <Skeleton width={16} />}</small>
+      <small>{!isUndefined(value) ? value : <Skeleton width={SKELETON_WIDTH} />}</small>
       {extraLabel ? <small>{extraLabel}</small> : null}
     </ValueAndExtraLabel>
   </FieldWrapper>
 );

32-40: Simplify value handling and add prop documentation.

The component logic can be simplified, and the interface would benefit from JSDoc documentation.

+/**
+ * Interface for Stats component props
+ * @property {number} [totalJurors] - The total number of jurors to display
+ */
 export interface IStats {
   totalJurors?: number;
 }

 const Stats: React.FC<IStats> = ({ totalJurors }) => {
-  const value = !isUndefined(totalJurors) ? totalJurors : undefined;
-  return <Field label="Total" value={value} extraLabel="Jurors" />;
+  return <Field label="Total" value={totalJurors} extraLabel="Jurors" />;
 };
web/src/layout/Header/navbar/Menu/Settings/General/WalletAndProfile.tsx (1)

41-53: Consider adding prop types validation.

The component accepts toggleIsSettingsOpen from ISettings interface but doesn't validate it. Consider adding PropTypes or making the prop required in TypeScript.

-const WalletAndProfile: React.FC<ISettings> = ({ toggleIsSettingsOpen }) => {
+const WalletAndProfile: React.FC<Required<ISettings>> = ({ toggleIsSettingsOpen }) => {
web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (1)

73-73: Consider destructuring props explicitly.

The spread operator {...{ toggleIsSettingsOpen }} is less readable than explicit prop passing.

-<WalletAndProfile {...{ toggleIsSettingsOpen }} />
+<WalletAndProfile toggleIsSettingsOpen={toggleIsSettingsOpen} />
web/src/layout/Header/navbar/Menu/Settings/index.tsx (2)

94-98: Refactor tab content rendering for better maintainability.

The current implementation uses conditional rendering which could become unwieldy with more tabs. Consider using an object map or switch statement.

-      {currentTab === 0 ? (
-        <General {...{ toggleIsSettingsOpen }} />
-      ) : (
-        <NotificationSettings {...{ toggleIsSettingsOpen }} />
-      )}
+      {
+        {
+          0: <General toggleIsSettingsOpen={toggleIsSettingsOpen} />,
+          1: <NotificationSettings toggleIsSettingsOpen={toggleIsSettingsOpen} />
+        }[currentTab]
+      }

Or better yet, map the tabs configuration to components:

const TAB_COMPONENTS = {
  GENERAL: {
    text: "General",
    value: 0,
    component: General
  },
  NOTIFICATIONS: {
    text: "Notifications",
    value: 1,
    component: NotificationSettings
  }
} as const;

const TABS = Object.values(TAB_COMPONENTS).map(({ text, value }) => ({ text, value }));

Then render:

const TabComponent = Object.values(TAB_COMPONENTS).find(
  tab => tab.value === currentTab
)?.component;

return TabComponent && <TabComponent toggleIsSettingsOpen={toggleIsSettingsOpen} />;

Line range hint 65-71: Consider memoizing the click away handler.

The click away handler creates a new function on each render that navigates and calls toggleIsSettingsOpen.

+const handleClickAway = useCallback(() => {
+  toggleIsSettingsOpen();
+  if (location.hash.includes("#notifications")) {
+    navigate("#", { replace: true });
+  }
+}, [toggleIsSettingsOpen, location.hash, navigate]);

-useClickAway(containerRef, () => {
-  toggleIsSettingsOpen();
-  if (location.hash.includes("#notifications")) navigate("#", { replace: true });
-});
+useClickAway(containerRef, handleClickAway);
web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (2)

26-31: Check hover color consistency.
You're overriding color in the label on hover. Verify this styling is consistent with the rest of the app theme and doesn't conflict with other link states (focus, visited, etc.).


53-56: Add alt or accessibility text to the arrow icon.
If feasible, implement accessible text or aria-label for <ArrowIcon />, so screen readers interpret the icon meaningfully.

- <ArrowIcon />
+ <ArrowIcon aria-label="View Profile" role="img" />
web/src/hooks/queries/useTotalLeaderboardJurors.ts (1)

17-32: Ensure robust handling of missing data.
While staleTime is set to Infinity to minimize refetching, ensure there's a strategy for manual invalidation or updates if the data changes. Handling potential fetch failures might also be beneficial.

web/src/utils/userLevelCalculation.ts (1)

24-25: Edge-case handling for boundary conditions.
Ensure that boundary checks for >= minCoherencePercentage and <= maxCoherencePercentage do not accidentally place borderline users in multiple categories. Add tests for exactly 70%, 80%, etc.

web/src/hooks/queries/useJurorsByCoherenceScore.ts (2)

10-26: Consider adding a minimum threshold for coherence score.

The query filters users with totalResolvedVotes_gt: 0 but might benefit from a minimum coherence score threshold to ensure quality data in the leaderboard.

-      where: { totalResolvedVotes_gt: 0 }
+      where: { totalResolvedVotes_gt: 0, coherenceScore_gt: "0.5" }

28-50: Consider caching strategy for real-time updates.

The current implementation uses staleTime: Infinity which means the data will never be considered stale. This might not be ideal for a leaderboard that should reflect real-time changes.

-    staleTime: Infinity,
+    staleTime: 60 * 1000, // Stale after 1 minute
+    cacheTime: 5 * 60 * 1000, // Cache for 5 minutes
web/src/pages/Jurors/DisplayJurors.tsx (2)

44-51: Consider memoizing rank calculation.

The rank calculation could be optimized by using a separate memoized value for the base rank.

+  const baseRank = useMemo(() => jurorSkip + 1, [jurorSkip]);
   const jurors = useMemo(
     () =>
       queryJurors?.users?.map((juror, index) => ({
         ...juror,
-        rank: jurorSkip + index + 1,
+        rank: baseRank + index,
       })),
-    [queryJurors, jurorSkip]
+    [queryJurors, baseRank]
   );

70-72: Add loading state handling.

The component could benefit from a proper loading state indicator rather than just skeleton items.

+  const isLoading = !isUndefined(jurors);
   {!isUndefined(jurors)
     ? jurors.map((juror) => <JurorCard key={juror.rank} address={juror.id} {...juror} />)
-    : [...Array(jurorsPerPage)].map((_, i) => <SkeletonDisputeListItem key={i} />)}
+    : <LoadingState>
+        {[...Array(jurorsPerPage)].map((_, i) => (
+          <SkeletonDisputeListItem key={i} />
+        ))}
+      </LoadingState>}
web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (2)

14-17: Consider using Math.round for more accurate percentage display.

The current implementation uses Math.floor which always rounds down. For percentages, Math.round would provide more accurate representation (e.g., 74.6% → 75% instead of 74%).

-  return `${Math.floor((num * 100) / den)}%`;
+  return `${Math.round((num * 100) / den)}%`;

20-21: Consider using branded types for vote counts.

Using string type for vote counts loses type safety. Consider using branded types to ensure type safety while maintaining string representation.

type VoteCount = string & { readonly __brand: unique symbol };

interface ICoherence {
  totalCoherentVotes: VoteCount;
  totalResolvedVotes: VoteCount;
}
web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx (1)

48-50: Consider moving string to number conversion to a utility function.

The conversion from string to number is done multiple times. Consider extracting it to a utility function to ensure consistent handling across the codebase.

+const toNumber = (value: string) => {
+  const num = Number(value);
+  return isNaN(num) ? 0 : num;
+};
+
 const JurorLevel: React.FC<IJurorLevel> = ({ totalCoherentVotes, totalResolvedVotes, totalResolvedDisputes }) => {
-  const coherencePercentage = getPercent(Number(totalCoherentVotes), Number(totalResolvedVotes));
-  const userLevelData = getUserLevelData(coherencePercentage, Number(totalResolvedDisputes));
+  const coherencePercentage = getPercent(toNumber(totalCoherentVotes), toNumber(totalResolvedVotes));
+  const userLevelData = getUserLevelData(coherencePercentage, toNumber(totalResolvedDisputes));
web/src/pages/Jurors/index.tsx (1)

72-72: Avoid hardcoding skeleton height.

The skeleton height is hardcoded to 1000px which might not match the actual content height. Consider using a dynamic height based on the viewport or expected content size.

-<Skeleton height={1000} />
+<Skeleton height={`${Math.min(window.innerHeight * 0.8, 1000)}px`} count={3} />
web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (1)

104-104: Avoid props spreading for better maintainability.

Props spreading can make it harder to track which props are being used. Consider explicitly passing the required props.

-<JurorLevel {...{ totalCoherentVotes, totalResolvedVotes, totalResolvedDisputes }} />
+<JurorLevel
+  totalCoherentVotes={totalCoherentVotes}
+  totalResolvedVotes={totalResolvedVotes}
+  totalResolvedDisputes={totalResolvedDisputes}
+/>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac069a3 and c95ee8a.

📒 Files selected for processing (25)
  • subgraph/core/schema.graphql (1 hunks)
  • subgraph/core/src/KlerosCore.ts (3 hunks)
  • subgraph/core/src/datapoint.ts (3 hunks)
  • web/src/app.tsx (2 hunks)
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx (2 hunks)
  • web/src/hooks/queries/useJurorsByCoherenceScore.ts (1 hunks)
  • web/src/hooks/queries/useTopUsersByCoherenceScore.ts (0 hunks)
  • web/src/hooks/queries/useTotalLeaderboardJurors.ts (1 hunks)
  • web/src/layout/Header/navbar/Explore.tsx (1 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/General/WalletAndProfile.tsx (1 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (2 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/index.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (3 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/index.tsx (3 hunks)
  • web/src/pages/Jurors/DisplayJurors.tsx (1 hunks)
  • web/src/pages/Jurors/Search.tsx (1 hunks)
  • web/src/pages/Jurors/Stats.tsx (1 hunks)
  • web/src/pages/Jurors/StatsAndFilters.tsx (1 hunks)
  • web/src/pages/Jurors/index.tsx (1 hunks)
  • web/src/pages/Profile/JurorInfo/index.tsx (2 hunks)
  • web/src/utils/userLevelCalculation.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • web/src/hooks/queries/useTopUsersByCoherenceScore.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • web/src/layout/Header/navbar/Explore.tsx
  • web/src/pages/Jurors/StatsAndFilters.tsx
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/pages/Home/TopJurors/index.tsx
  • subgraph/core/src/KlerosCore.ts
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx
🧰 Additional context used
📓 Learnings (1)
web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx (1)
Learnt from: Harman-singh-waraich
PR: kleros/kleros-v2#1739
File: web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx:22-26
Timestamp: 2024-11-12T04:49:43.234Z
Learning: In the `Coherency` component (`web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx`), `totalResolvedVotes` is always greater than or equal to `totalCoherentVotes`. When both are zero, `0/0` results in `NaN`, which is acceptable in this context.
⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Analyze (javascript)
  • GitHub Check: dependency-review
  • GitHub Check: SonarCloud
  • GitHub Check: contracts-testing
🔇 Additional comments (19)
web/src/pages/Jurors/Stats.tsx (1)

1-6: LGTM! Clean and well-organized imports.

The imports are properly organized, following the pattern of external dependencies first, followed by internal utilities.

web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (1)

61-62: Add error handling for disconnected state.

The component uses address from useAccount but doesn't handle the loading or error states from the hook.

web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (3)

4-5: Import for Arrow Icon is appropriate.
No issues here; the introduced SVG icon is consistent with the new link styling.


7-8: Confirm usage of StyledArrowLink and useAccount.
The usage of StyledArrowLink from components/StyledArrowLink and the wagmi hook appears correct, but ensure all related dependencies and their versions are aligned in your package.


44-48: Conditional link logic looks good.
The approach for matching the connected address and adjusting the route accordingly is clear. Consider whether we need additional checks for edge cases (e.g., empty addresses).

web/src/hooks/queries/useTotalLeaderboardJurors.ts (4)

1-2: Importing useQuery from React Query is correct.
This is a good approach for data caching and refetch. Ensure pinned package versions for consistency.


3-4: Use of GraphqlBatcher context.
You are correctly leveraging the GraphQL batcher for composable queries. Confirm that this context is provided at a top-level to avoid runtime errors.


5-8: GraphQL definitions are well-structured.
Exporting the query type ensures type safety. Watch for automatic code generation if you rely on a codegen process.


9-15: Check for error handling in the query.
The query shape looks correct. Consider verifying if counter can be null or if totalLeaderboardJurors can be missing, and gracefully handle that if needed.

web/src/utils/userLevelCalculation.ts (2)

5-6: Coherence-based thresholds.
Switching to minCoherencePercentage and maxCoherencePercentage makes sense but double-check boundary overlaps across multiple levels.


17-20: Parsing string percentage is fine.
The percentageToNumber approach is straightforward. Confirm that all callers reliably provide the string in a “XX%” format to avoid parsing errors.

web/src/hooks/queries/useJurorsByCoherenceScore.ts (1)

1-8: LGTM! Clean imports and type exports.

The imports are well-organized and the type export is properly defined.

subgraph/core/src/datapoint.ts (1)

18-18: LGTM! Consistent variable handling.

The totalLeaderboardJurors variable is properly integrated into the existing variables array and initialization logic.

Also applies to: 47-47

web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx (1)

8-8: Verify the new import path.

The import path has changed from Dashboard to Profile. Ensure all references to this component have been updated accordingly.

✅ Verification successful

Import path change is correctly implemented

All references to the PixelArt component are using the new path from the Profile directory. No instances of the old Dashboard path remain in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old path
rg -l "pages/Dashboard/JurorInfo/PixelArt"

Length of output: 42


Script:

#!/bin/bash
# Check if the new file exists
fd PixelArt.tsx

# Find all imports of PixelArt
rg -l "import.*PixelArt"

# Search for any variations of the path
rg -l "JurorInfo/PixelArt"

Length of output: 370


Script:

#!/bin/bash
# Check the actual import statements in all files
rg "import.*PixelArt" -A 1 -B 1

Length of output: 849

web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (1)

83-86: LGTM: Type changes align with other components.

The interface changes from number to string types align with the changes in other components, maintaining consistency across the codebase.

web/src/app.tsx (3)

17-17: LGTM! Components are correctly lazy loaded.

The Profile and Jurors components are properly imported using lazy loading, which helps with code splitting and initial load performance.

Also applies to: 19-19


68-74: LGTM! Route parameters are well-structured.

The jurors route with path parameters :page/:order/:filter provides good flexibility for pagination, sorting, and filtering functionality.


75-81: LGTM! Profile route maintains consistency.

The profile route follows the same parameter structure as the jurors route, maintaining consistency in the routing pattern.

subgraph/core/schema.graphql (1)

240-240: LGTM! Field addition is well-defined.

The totalLeaderboardJurors field is appropriately typed as non-nullable BigInt! and follows the existing pattern of counter metrics in the entity.

Let's verify that the field is being properly updated in the codebase:

✅ Verification successful

Field is properly implemented and integrated

The totalLeaderboardJurors field is correctly implemented with:

  • Initialization and update logic in subgraph mappings
  • Proper frontend integration with type safety
  • Complete data flow from contract events to UI
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to totalLeaderboardJurors in the codebase
echo "Searching for field updates in the subgraph mappings..."
rg "totalLeaderboardJurors" subgraph/

echo "Searching for field usage in GraphQL queries..."
rg "totalLeaderboardJurors" web/src/

Length of output: 1839

@kemuru kemuru marked this pull request as ready for review January 27, 2025 09:10
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (9)
web/src/components/BlueIconTextButtonContainer.tsx (1)

8-9: Consider using theme variables for font properties.

The font size and weight are hardcoded. Consider moving these to theme variables for better maintainability and consistency across the application.

-  font-size: 14px;
-  font-weight: 400;
+  font-size: ${({ theme }) => theme.typography.size.small};
+  font-weight: ${({ theme }) => theme.typography.weight.regular};
web/src/pages/Home/TopJurors/index.tsx (1)

50-50: Consider parameterizing the hook values.

The hardcoded values (0, 5, "coherenceScore", "desc") could be made configurable through props or constants to improve reusability.

-  const { data: queryJurors } = useJurorsByCoherenceScore(0, 5, "coherenceScore", "desc");
+  const JURORS_LIMIT = 5;
+  const { data: queryJurors } = useJurorsByCoherenceScore(
+    0,
+    JURORS_LIMIT,
+    "coherenceScore",
+    "desc"
+  );
web/src/components/ExtraStatsDisplay.tsx (1)

31-36: Consider making text alignment configurable.

While centering the text works for the current use case, consider making the alignment configurable through props to support different content layouts in the future.

- const ContentContainer = styled.div`
+ const ContentContainer = styled.div<{ textAlign?: string }>`
   display: flex;
   align-items: center;
   gap: 8px;
   flex-wrap: wrap;
-  text-align: center;
+  text-align: ${({ textAlign }) => textAlign || 'center'};
 `;
web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (2)

11-11: Consider optimizing the import path for ISettings.

The relative import path ../../../index seems unnecessarily deep. Consider using a path alias or moving the interface to a more accessible location.


72-72: Consider more explicit prop passing.

Instead of using the spread operator with an object literal, consider passing the prop directly for better readability:

-<WalletAndProfile {...{ toggleIsSettingsOpen }} />
+<WalletAndProfile toggleIsSettingsOpen={toggleIsSettingsOpen} />
web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (1)

26-36: Consider renaming and refactoring the styled component.

A few suggestions to improve the styled component:

  1. The prefix "Re" in ReStyledArrowLink is unconventional. Consider a more descriptive name like StyledJurorLink or EnhancedArrowLink.
  2. The color values could be moved to theme variables for better maintainability.
-export const ReStyledArrowLink = styled(StyledArrowLink)`
+export const EnhancedArrowLink = styled(StyledArrowLink)`
   label {
     cursor: pointer;
-    color: ${({ theme }) => theme.primaryBlue};
+    color: ${({ theme }) => theme.colors.jurorLink.default};
   }

   :hover {
     label {
-      color: ${({ theme }) => theme.secondaryBlue};
+      color: ${({ theme }) => theme.colors.jurorLink.hover};
     }
   }
`;
web/src/pages/Jurors/StatsAndFilters.tsx (1)

30-33: Add error handling for navigation failures.

The handleOrderChange function doesn't handle potential navigation failures.

 const handleOrderChange = (value: string | number) => {
   const encodedFilter = encodeURIFilter({ ...filterObject });
-  navigate(`${location}/1/${value}/${encodedFilter}?${searchParams.toString()}`);
+  navigate(`${location}/1/${value}/${encodedFilter}?${searchParams.toString()}`).catch((error) => {
+    console.error("Navigation failed:", error);
+    // Handle navigation failure (e.g., show error message)
+  });
 };
web/src/pages/Jurors/DisplayJurors.tsx (2)

45-57: Optimize rank calculation performance.

The rank calculation in useMemo could be optimized by avoiding unnecessary array operations.

 const jurors = useMemo(() => {
   const baseJurors = queryJurors?.users?.map((juror, index) => ({
     ...juror,
-    rank: searchValue ? undefined : jurorSkip + index + 1,
+    rank: searchValue ? undefined : (order === "asc" ? totalLeaderboardJurors - jurorSkip - index : jurorSkip + index + 1),
   }));
-  if (!searchValue && order === "asc") {
-    return baseJurors?.map((juror) => ({
-      ...juror,
-      rank: totalLeaderboardJurors - (juror.rank || 0) + 1,
-    }));
-  }
   return baseJurors;
 }, [queryJurors, jurorSkip, order, totalLeaderboardJurors, searchValue]);

64-66: Add error handling for navigation failures.

The handlePageChange function doesn't handle potential navigation failures.

 const handlePageChange = (newPage: number) => {
-  navigate(`/jurors/${newPage}/${order}/${filter}`);
+  navigate(`/jurors/${newPage}/${order}/${filter}`).catch((error) => {
+    console.error("Navigation failed:", error);
+    // Handle navigation failure (e.g., show error message)
+  });
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c95ee8a and f2867a2.

⛔ Files ignored due to path filters (1)
  • web/src/assets/svgs/icons/ranking.svg is excluded by !**/*.svg
📒 Files selected for processing (21)
  • subgraph/core/schema.graphql (2 hunks)
  • subgraph/core/src/entities/User.ts (1 hunks)
  • web/src/components/BlueIconTextButtonContainer.tsx (1 hunks)
  • web/src/components/ExtraStatsDisplay.tsx (3 hunks)
  • web/src/components/HowItWorks.tsx (2 hunks)
  • web/src/components/JurorsLeaderboardButton.tsx (1 hunks)
  • web/src/hooks/queries/useJurorsByCoherenceScore.ts (1 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/General/WalletAndProfile.tsx (1 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (2 hunks)
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx (1 hunks)
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx (2 hunks)
  • web/src/pages/Home/Community/index.tsx (1 hunks)
  • web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx (3 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (2 hunks)
  • web/src/pages/Home/TopJurors/index.tsx (5 hunks)
  • web/src/pages/Jurors/DisplayJurors.tsx (1 hunks)
  • web/src/pages/Jurors/Search.tsx (1 hunks)
  • web/src/pages/Jurors/StatsAndFilters.tsx (1 hunks)
  • web/src/pages/Jurors/index.tsx (1 hunks)
  • web/src/pages/Profile/JurorInfo/Header.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • web/src/components/JurorsLeaderboardButton.tsx
  • web/src/pages/Home/Community/index.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • web/src/pages/Profile/JurorInfo/Header.tsx
  • subgraph/core/schema.graphql
  • web/src/layout/Header/navbar/Menu/Settings/General/WalletAndProfile.tsx
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx
⏰ Context from checks skipped due to timeout of 90000ms (17)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-university
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-university
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-university
  • GitHub Check: dependency-review
  • GitHub Check: SonarCloud
  • GitHub Check: contracts-testing
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Mend Security Check
🔇 Additional comments (23)
web/src/components/BlueIconTextButtonContainer.tsx (1)

4-24: Well-structured styled component with consistent theming.

The component follows good practices:

  • Uses theme colors for consistent styling
  • Implements smooth hover transitions
  • Handles both text and SVG colors consistently
  • Maintains proper spacing with flex layout
web/src/components/HowItWorks.tsx (1)

16-19: Clean implementation with improved UI consistency.

The replacement of the custom Container with BlueIconTextButtonContainer maintains functionality while improving UI consistency across the application.

web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx (1)

9-10: LGTM! Clean icon implementation.

The changes effectively replace the hash symbol with a themed ranking icon, improving visual consistency while maintaining the existing layout structure.

Also applies to: 39-43, 59-59

web/src/pages/Home/TopJurors/index.tsx (1)

Line range hint 26-47: LGTM! Clean component structure.

The styled components are well-organized and the JurorsLeaderboardButton implementation maintains a consistent layout.

Also applies to: 70-72

web/src/components/ExtraStatsDisplay.tsx (3)

11-12: LGTM! Improved responsive layout.

The addition of justify-content: center and flex-wrap: wrap enhances the component's responsiveness and visual balance.


15-17: LGTM! Good component structure.

The new TitleContainer component properly encapsulates the icon and title elements with consistent spacing.


58-70: LGTM! Improved component structure.

The new JSX structure with separate TitleContainer and ContentContainer components improves readability and maintainability while maintaining proper TypeScript type safety.

web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx (3)

101-101: LGTM! Good defensive programming.

The null check for rank prevents rendering issues when the rank is undefined or null.


104-104: Verify the coherence calculation changes.

The props spreading has been updated to align with the new juror level calculation requirements. Please ensure:

  1. The removal of coherenceScore is intentional
  2. The new props are being used correctly in the JurorLevel component

Let's verify the changes in the JurorLevel component:

✅ Verification successful

The coherence calculation changes are correct and intentional

The removal of coherenceScore prop is proper as the JurorLevel component calculates coherence directly from the raw vote counts (totalCoherentVotes, totalResolvedVotes). The new props are being used correctly for both coherence calculation and level determination.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the JurorLevel component implementation
rg -t typescript "const JurorLevel|interface.*JurorLevel" -A 20 "JurorLevel.tsx"

# Find all usages of coherence-related calculations
rg "coherenceScore|coherencePercentage|getPercent"

Length of output: 3724


83-86: Verify the rationale for changing number types to string.

The change from number to string types for statistics could be for handling large numbers or decimal precision. Please clarify:

  • Are these changes due to BigInt requirements?
  • Is this for consistent decimal point formatting?
  • Does this align with the GraphQL schema changes?

Let's verify the GraphQL schema changes and usage:

✅ Verification successful

Type changes from number to string are correct for statistics handling

The change aligns with:

  • GraphQL schema defining these fields as BigInt
  • Codebase's pattern of handling large numbers as strings in UI components
  • Presence of number formatting utilities for proper display
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for GraphQL type definitions and queries using these fields
rg -t graphql "type.*Juror|fragment.*Juror|query.*Juror" -A 10

# Search for any BigInt or decimal formatting utilities
rg "BigInt|Decimal|toFixed|toString"

Length of output: 72041

subgraph/core/src/entities/User.ts (1)

30-30: Consider potential data duplication with entity ID.

The new userAddress field appears to store the same value as the entity's id field. While storing addresses in lowercase is good practice, consider if this warrants a separate field or if the normalization could be handled differently.

Potential alternatives:

  1. If the goal is case normalization, consider normalizing the id parameter before creating the entity:
-export function createUserFromAddress(id: string): User {
-  const user = new User(id);
-  user.userAddress = id.toLowerCase();
+export function createUserFromAddress(id: string): User {
+  const normalizedId = id.toLowerCase();
+  const user = new User(normalizedId);
  1. If the separate field is needed for specific GraphQL queries, consider adding a computed field in the GraphQL schema instead.

Could you clarify the specific use case that requires this additional field? This would help evaluate if the current implementation is the best approach.

web/src/layout/Header/navbar/Menu/Settings/General/index.tsx (3)

39-39: Styling adjustments look good!

The spacing changes improve the visual hierarchy and maintain consistency with the new profile layout.

Also applies to: 45-46, 52-52


60-61: Clean component signature update!

The component now correctly accepts the ISettings interface, improving type safety.


10-10: Verify WalletAndProfile component implementation.

Let's ensure the imported component is properly implemented and handles profile navigation correctly.

✅ Verification successful

WalletAndProfile component implementation verified successfully

The component exists in the correct location and is properly implemented with wallet display and profile navigation functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify WalletAndProfile component implementation

# Check if the component file exists and contains necessary profile navigation logic
rg -A 10 "const WalletAndProfile" "web/src/layout/Header/navbar/Menu/Settings/WalletAndProfile.tsx"

# Check for profile route configuration
rg "path.*profile" "web/src/routes"

Length of output: 294


Script:

#!/bin/bash
# First, let's understand the directory structure
fd -t f . web/src/layout/Header/navbar/Menu/Settings/General

# Then search for WalletAndProfile component across the codebase
ast-grep --pattern 'const WalletAndProfile'

# Also search for any file containing WalletAndProfile
rg -l "WalletAndProfile"

Length of output: 1773

web/src/pages/Home/TopJurors/JurorCard/JurorTitle.tsx (4)

4-8: LGTM! Imports are properly organized.

The new imports support the updated navigation structure and follow React best practices.


38-44: LGTM! Well-structured interface and component definition.

The changes follow TypeScript best practices with:

  • Proper interface definition
  • Optional prop with default value
  • Clear type annotations

53-57: LGTM! Clean component rendering logic.

The component properly:

  • Uses the new EnhancedArrowLink component
  • Conditionally renders the arrow icon
  • Maintains proper component composition

45-49: Verify hardcoded route parameters and consider using constants.

The profile link contains hardcoded values (/1/desc/all) that might need verification:

  1. What does "1" represent in the route?
  2. Are "desc" and "all" the correct default values?

Consider extracting these values to route constants for better maintainability.

+const PROFILE_ROUTES = {
+  DEFAULT_PAGE: '1',
+  DEFAULT_SORT: 'desc',
+  DEFAULT_FILTER: 'all',
+} as const;
+
 const profileLink =
   isConnected && connectedAddress?.toLowerCase() === address.toLowerCase()
-    ? "/profile/1/desc/all"
-    ? `/profile/1/desc/all?address=${address}`;
+    ? `/profile/${PROFILE_ROUTES.DEFAULT_PAGE}/${PROFILE_ROUTES.DEFAULT_SORT}/${PROFILE_ROUTES.DEFAULT_FILTER}`
+    : `/profile/${PROFILE_ROUTES.DEFAULT_PAGE}/${PROFILE_ROUTES.DEFAULT_SORT}/${PROFILE_ROUTES.DEFAULT_FILTER}?address=${address}`;
✅ Verification successful

Hardcoded route parameters are used consistently, but could benefit from constants.

The values /1/desc/all are used consistently across multiple components in the codebase, confirming they are the correct default values. However, the suggestion to extract these to constants remains valid for better maintainability.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for profile route definitions and usages
echo "=== Searching for profile route definitions ==="
rg -g '*.{ts,tsx}' 'profile/[^"'\'']*desc.*all' -A 2 -B 2

echo -e "\n=== Looking for route constant definitions ==="
rg -g '*.{ts,tsx}' 'const.*PROFILE|ROUTE' --type-add 'tsx:*.{ts,tsx}'

echo -e "\n=== Finding profile page components ==="
fd -e ts -e tsx -g '*Profile*.{ts,tsx}'

echo -e "\n=== Checking pagination related code ==="
rg -g '*.{ts,tsx}' 'page.*=.*1.*desc.*all' -A 2 -B 2

Length of output: 3025

web/src/hooks/queries/useJurorsByCoherenceScore.ts (2)

41-41: Consider caching implications of staleTime: Infinity.

Setting staleTime to Infinity means the data will never be considered stale. This might lead to outdated data being displayed if jurors' coherence scores change.

Consider implementing a more appropriate stale time based on how frequently the data changes.


19-20: Validate search input for potential SQL injection.

The where clause in the GraphQL query uses the search parameter directly. While GraphQL typically handles escaping, it's good practice to validate and sanitize user input.

web/src/pages/Jurors/Search.tsx (1)

32-45: Consider using AbortController with useDebounce.

The current debounce implementation doesn't handle component unmounting or rapid navigation changes.

web/src/pages/Jurors/index.tsx (1)

68-72: Improve loading state handling.

The current implementation shows a skeleton only when totalLeaderboardJurors is undefined or zero.

web/src/pages/Jurors/DisplayJurors.tsx (1)

35-35: Add validation for page parameter.

The current implementation assumes the page parameter is always a valid number.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 27, 2025
Copy link
Contributor

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

🔭 Outside diff range comments (1)
web/src/pages/Home/TopJurors/index.tsx (1)

Consolidation of styled components is indeed necessary

The codebase shows multiple duplicate implementations of these components:

  • ListContainer is independently defined in at least 3 places
  • StyledLabel appears in 15+ locations with similar styling patterns

Recommended actions:

  • Move StyledLabel to a shared styled components file (e.g., components/common/styled) as it's the most duplicated
  • Consider consolidating ListContainer implementations if their styles are similar
🔗 Analysis chain

Line range hint 26-47: Consider consolidating shared styled components.

Since ListContainer and StyledLabel are now exported, they might be shared across multiple components. Consider moving them to a shared styled components file if they're used in multiple places.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential reuse of these styled components

# Search for ListContainer usage
echo "Checking ListContainer usage:"
rg "ListContainer" --type ts --type tsx

# Search for StyledLabel usage
echo "Checking StyledLabel usage:"
rg "StyledLabel" --type ts --type tsx

Length of output: 267


Script:

#!/bin/bash
# Search for ListContainer and StyledLabel usage with proper TypeScript/React file handling

echo "Checking ListContainer usage and imports:"
rg "ListContainer" -t typescript -t javascriptreact -t typescriptreact -C 2

echo -e "\nChecking StyledLabel usage and imports:"
rg "StyledLabel" -t typescript -t javascriptreact -t typescriptreact -C 2

echo -e "\nChecking for imports from this file:"
rg "from ['\"].*TopJurors" -t typescript -t javascriptreact -t typescriptreact

Length of output: 614


Script:

#!/bin/bash
echo "Checking ListContainer usage and imports:"
rg "ListContainer" --glob "*.{ts,tsx}"

echo -e "\nChecking StyledLabel usage and imports:"
rg "StyledLabel" --glob "*.{ts,tsx}"

echo -e "\nChecking for imports from TopJurors:"
rg "from.*TopJurors" --glob "*.{ts,tsx}"

Length of output: 10056

🧹 Nitpick comments (5)
web/src/pages/Home/TopJurors/index.tsx (1)

61-61: Enhance the empty state UX.

The current empty state only shows "No jurors found" without providing context or next steps. Consider:

  1. Adding more context about why no jurors are found
  2. Suggesting actions users can take
-        <StyledLabel>No jurors found</StyledLabel>
+        <div>
+          <StyledLabel>No jurors found</StyledLabel>
+          <p>This could be because no jurors have staked yet or the data is still loading.</p>
+          <p>Check back later or visit the leaderboard for more information.</p>
+        </div>

Also applies to: 70-72

web/src/pages/Jurors/DisplayJurors.tsx (4)

14-16: Consider reorganizing component structure.

The relative imports from "../Home/TopJurors" suggest these components might be more general-purpose than their current location implies. Consider moving shared components to a common location.


41-41: Enhance type safety for order parameter.

The order parameter could benefit from stronger typing to prevent potential runtime errors.

+type OrderType = 'asc' | 'desc';
+const orderDirection = (order?: string): OrderDirection => {
+  return (order as OrderType === 'asc') ? OrderDirection.Asc : OrderDirection.Desc;
+};
-    order === "asc" ? OrderDirection.Asc : OrderDirection.Desc,
+    orderDirection(order),

45-57: Simplify rank calculation logic.

The rank calculation logic, especially for ascending order, could be made more readable.

 const jurors = useMemo(() => {
-    const baseJurors = queryJurors?.users?.map((juror, index) => ({
+    if (!queryJurors?.users) return undefined;
+    
+    const calculateRank = (index: number) => {
+      if (searchValue) return undefined;
+      const baseRank = jurorSkip + index + 1;
+      return order === "asc" ? totalLeaderboardJurors - baseRank + 1 : baseRank;
+    };
+    
+    return queryJurors.users.map((juror, index) => ({
       ...juror,
-      rank: searchValue ? undefined : jurorSkip + index + 1,
+      rank: calculateRank(index),
     }));
-    if (!searchValue && order === "asc") {
-      return baseJurors?.map((juror) => ({
-        ...juror,
-        rank: totalLeaderboardJurors - (juror.rank || 0) + 1,
-      }));
-    }
-    return baseJurors;
   }, [queryJurors, jurorSkip, order, totalLeaderboardJurors, searchValue]);

76-79: Extract loading state check for better readability.

Consider extracting the loading state check into a separate variable or component.

+  const renderJurorsList = () => {
+    if (isUndefined(jurors)) {
+      return [...Array(jurorsPerPage)].map((_, i) => (
+        <SkeletonDisputeListItem key={i} />
+      ));
+    }
+    return jurors.map((juror) => (
+      <JurorCard key={juror.id} address={juror.id} {...juror} />
+    ));
+  };

   <ListContainer>
     <Header />
-    {!isUndefined(jurors)
-      ? jurors.map((juror) => <JurorCard key={juror.id} address={juror.id} {...juror} />)
-      : [...Array(jurorsPerPage)].map((_, i) => <SkeletonDisputeListItem key={i} />)}
+    {renderJurorsList()}
   </ListContainer>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2867a2 and c749b2d.

📒 Files selected for processing (2)
  • web/src/pages/Home/TopJurors/index.tsx (6 hunks)
  • web/src/pages/Jurors/DisplayJurors.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (13)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-university
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-university
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-university
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Analyze (javascript)
  • GitHub Check: contracts-testing
  • GitHub Check: dependency-review
  • GitHub Check: SonarCloud
🔇 Additional comments (5)
web/src/pages/Home/TopJurors/index.tsx (2)

6-6: Verify the hook import path.

The import path queries/useJurorsByCoherenceScore seems to be relative. Consider using the absolute import path with the @ alias or verify that the path resolution is correctly configured.

Also applies to: 15-15


Line range hint 50-55: Verify the hook parameters and data structure.

The hook parameters are hardcoded (0, 5, "coherenceScore", "desc"). Consider extracting these as constants or making them configurable props if they need to be adjusted in different contexts.

✅ Verification successful

Hardcoded parameters are appropriate in this context

The current implementation in TopJurors/index.tsx is correct as it serves a specific purpose - displaying the top 5 jurors. The hook is properly designed to accept configurable parameters when needed, as evidenced by its usage in DisplayJurors.tsx, but in this case, the fixed values align with the component's singular purpose.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the hook implementation and usage patterns

# Check for other instances of the hook to understand parameter patterns
rg "useJurorsByCoherenceScore" -A 3 --type ts --type tsx

Length of output: 84


Script:

#!/bin/bash
# Search with correct TypeScript/React file handling
# Look for both hook implementation and usages
rg "useJurorsByCoherenceScore|coherenceScore" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' --type tsx --type ts -A 5

# Also check for any constant definitions that might be related
rg "const.*coherenceScore|const.*COHERENCE" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' --type tsx --type ts

Length of output: 7860

web/src/pages/Jurors/DisplayJurors.tsx (3)

23-27: LGTM! Clean styling implementation.

The styled component follows good practices with clear margin definitions and proper auto-centering.


35-35: Add validation for page parameter.

The current implementation assumes the page parameter is always a valid number.


64-66: Add safety check for filter parameter in navigation.

The navigation could fail if the filter parameter is undefined.

   const handlePageChange = (newPage: number) => {
-    navigate(`/jurors/${newPage}/${order}/${filter}`);
+    navigate(`/jurors/${newPage}/${order}/${filter ?? 'all'}`);
   };

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 27, 2025
Copy link
Contributor

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

🧹 Nitpick comments (4)
subgraph/core/src/KlerosCore.ts (1)

174-174: Optimize coherenceScore calculations to avoid duplication.

The coherenceScore is calculated twice when a vote exists:

  1. First at line 161 (if vote doesn't exist)
  2. Again at line 174 (unconditionally)

The second calculation makes the first one redundant.

Consider this optimization:

        if (!vote) {
-         // Recalculate coherenceScore
-         juror.coherenceScore = computeCoherenceScore(juror.totalCoherentVotes, juror.totalResolvedVotes);
-         juror.save();
          continue;
        }

        if (vote.choice === null) continue;

        // Check if the vote choice matches the final ruling
        if (vote.choice!.equals(dispute.currentRuling)) {
          juror.totalCoherentVotes = juror.totalCoherentVotes.plus(ONE);
        }

        // Recalculate coherenceScore
        juror.coherenceScore = computeCoherenceScore(juror.totalCoherentVotes, juror.totalResolvedVotes);
web/src/pages/Jurors/DisplayJurors.tsx (3)

19-21: Add JSDoc documentation for the interface.

Consider adding JSDoc documentation to describe the purpose of the interface and its property.

+/**
+ * Props for the DisplayJurors component
+ * @interface IDisplayJurors
+ * @property {number} totalLeaderboardJurors - The total number of jurors in the leaderboard
+ */
 interface IDisplayJurors {
   totalLeaderboardJurors: number;
 }

45-57: Simplify rank calculation logic.

The rank calculation for ascending order can be simplified using a single expression.

   const jurors = useMemo(() => {
     const baseJurors = queryJurors?.users?.map((juror, index) => ({
       ...juror,
-      rank: searchValue ? undefined : jurorSkip + index + 1,
+      rank: searchValue ? undefined : order === "asc" 
+        ? totalLeaderboardJurors - jurorSkip - index 
+        : jurorSkip + index + 1,
     }));
-    if (!searchValue && order === "asc" && baseJurors) {
-      return baseJurors.map((juror) => ({
-        ...juror,
-        rank: totalLeaderboardJurors - (juror.rank || 0) + 1,
-      }));
-    }
     return baseJurors;
   }, [queryJurors, jurorSkip, order, totalLeaderboardJurors, searchValue]);

68-101: Simplify render logic and remove duplication.

The component has duplicated pagination logic and complex conditional rendering.

Consider this simplified structure:

   return (
     <>
-      {isUndefined(totalLeaderboardJurors) ? (
-        <>
-          <ListContainer>
-            <Header />
-            {[...Array(jurorsPerPage)].map((_, i) => (
-              <SkeletonDisputeListItem key={i} />
-            ))}
-          </ListContainer>
-          {!searchValue && (
-            <StyledPagination currentPage={currentPage} numPages={totalPages} callback={handlePageChange} />
-          )}
-        </>
-      ) : (
-        <ListContainer>
-          {!isUndefined(jurors) && jurors.length === 0 ? (
-            <StyledLabel>No jurors found</StyledLabel>
-          ) : (
-            <>
-              <Header />
-              {!isUndefined(jurors)
-                ? jurors.map((juror) => <JurorCard key={juror.id} address={juror.id} {...juror} />)
-                : [...Array(jurorsPerPage)].map((_, i) => <SkeletonDisputeListItem key={i} />)}
-              {!searchValue && (
-                <StyledPagination currentPage={currentPage} numPages={totalPages} callback={handlePageChange} />
-              )}
-            </>
-          )}
-        </ListContainer>
-      )}
+      <ListContainer>
+        <Header />
+        {!isUndefined(jurors) ? (
+          jurors.length === 0 ? (
+            <StyledLabel>No jurors found</StyledLabel>
+          ) : (
+            jurors.map((juror) => <JurorCard key={juror.id} address={juror.id} {...juror} />)
+          )
+        ) : (
+          [...Array(jurorsPerPage)].map((_, i) => <SkeletonDisputeListItem key={i} />)
+        )}
+        {!searchValue && (
+          <StyledPagination currentPage={currentPage} numPages={totalPages} callback={handlePageChange} />
+        )}
+      </ListContainer>
     </>
   );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5d969b and 535508a.

📒 Files selected for processing (3)
  • subgraph/core/src/KlerosCore.ts (3 hunks)
  • web/src/components/JurorsLeaderboardButton.tsx (1 hunks)
  • web/src/pages/Jurors/DisplayJurors.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/components/JurorsLeaderboardButton.tsx
⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: Redirect rules - kleros-v2-university
  • GitHub Check: Header rules - kleros-v2-university
  • GitHub Check: Pages changed - kleros-v2-university
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: SonarCloud
  • GitHub Check: contracts-testing
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
subgraph/core/src/KlerosCore.ts (2)

21-27: LGTM! Import changes are well-organized.

The new import updateTotalLeaderboardJurors is correctly grouped with related datapoint functions.


148-164: Verify the impact of unconditional coherenceScore calculation.

The coherenceScore is now recalculated even when no vote exists, which is a behavioral change from the previous implementation. While this might be intentional for the leaderboard feature, we should verify that this doesn't affect historical data consistency.

Run this script to analyze the impact:

Additionally, consider adding comments to explain:

  1. Why coherenceScore is calculated even when no vote exists
  2. The relationship between totalResolvedVotes and leaderboard updates
web/src/pages/Jurors/DisplayJurors.tsx (4)

23-27: LGTM! Clean styling implementation.

The styled component is well-structured and uses appropriate margin properties for centering.


35-35: Add validation for page parameter.

The current implementation assumes the page parameter is always a valid number.


64-66: LGTM! Clean navigation implementation.

The page change handler correctly updates the URL while preserving the order and filter parameters.


31-31: Improve search value handling.

The search value is used directly in the GraphQL query without any sanitization or validation.

Consider adding validation and sanitization:

-  const { id: searchValue } = decodeURIFilter(filter ?? "all");
+  const { id: rawSearchValue } = decodeURIFilter(filter ?? "all");
+  const searchValue = rawSearchValue?.trim().toLowerCase();

Let's verify the decodeURIFilter implementation:

Also applies to: 42-42

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 28, 2025
Copy link
Contributor

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

🧹 Nitpick comments (1)
web/src/pages/Home/TopJurors/Header/MobileHeader.tsx (1)

40-41: Consider adding error handling for the filter decoding.

While the current implementation works, it might be good to add error handling for cases where the filter value is malformed.

 const { filter } = useParams();
-const { id: searchValue } = decodeURIFilter(filter ?? "all");
+const { id: searchValue } = React.useMemo(() => {
+  try {
+    return decodeURIFilter(filter ?? "all");
+  } catch (error) {
+    console.error("Failed to decode filter:", error);
+    return { id: "" };
+  }
+}, [filter]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 535508a and d04f0e1.

📒 Files selected for processing (1)
  • web/src/pages/Home/TopJurors/Header/MobileHeader.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Analyze (javascript)
  • GitHub Check: SonarCloud
  • GitHub Check: contracts-testing
🔇 Additional comments (2)
web/src/pages/Home/TopJurors/Header/MobileHeader.tsx (2)

5-5: LGTM! Import statements are properly organized.

The new imports are correctly placed and necessary for the URL parameter handling functionality.

Also applies to: 9-9


45-45: LGTM! Clear and concise conditional rendering.

The label logic effectively switches between "Ranking" and "Jurors" based on the search context.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 28, 2025
alcercu
alcercu previously approved these changes Jan 29, 2025
Copy link
Contributor

@alcercu alcercu left a comment

Choose a reason for hiding this comment

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

lgtm

@alcercu alcercu enabled auto-merge January 29, 2025 15:44
@jaybuidl jaybuidl dismissed stale reviews from alcercu and coderabbitai[bot] via e6875eb January 30, 2025 10:40
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 30, 2025
@alcercu alcercu self-requested a review January 30, 2025 11:08
alcercu
alcercu previously approved these changes Jan 30, 2025
@alcercu alcercu disabled auto-merge January 30, 2025 11:09
@alcercu alcercu enabled auto-merge January 30, 2025 11:09
@kemuru kemuru dismissed stale reviews from alcercu and coderabbitai[bot] via 2bb0a19 January 30, 2025 11:42
Copy link
Contributor

@alcercu alcercu left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link

codeclimate bot commented Jan 30, 2025

Code Climate has analyzed commit 2bb0a19 and detected 56 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 6
Duplication 14
Style 36

View more on Code Climate.

@alcercu alcercu merged commit 7af5a24 into dev Jan 30, 2025
21 of 26 checks passed
@alcercu alcercu deleted the feat(web)/jurors-page branch January 30, 2025 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants