Skip to content

feat: pre-commit type checking and lint - web#2279

Merged
kyrers merged 15 commits into
feat/pre-commit-type-checkfrom
feat/pre-commit-type-check-web
May 22, 2026
Merged

feat: pre-commit type checking and lint - web#2279
kyrers merged 15 commits into
feat/pre-commit-type-checkfrom
feat/pre-commit-type-check-web

Conversation

@kyrers
Copy link
Copy Markdown
Contributor

@kyrers kyrers commented May 6, 2026

Part of #2277, but isolates the web workspace changes for easier review.


PR-Codex overview

This PR focuses on various code improvements, including type adjustments, error handling enhancements, and updates to the styling and structure of components. It aims to improve type safety, maintainability, and overall code quality.

Detailed summary

  • Updated stake type from string to bigint in several components.
  • Enhanced error handling by removing unnecessary error variable in catch statements.
  • Added TypeScript type declarations for various modules.
  • Adjusted transport type in client configuration.
  • Improved consistency in handling Address types across components.
  • Refactored useCoinPrice to accept an array of coin IDs.
  • Simplified JSX structures and improved readability in multiple components.
  • Updated the way numberOfJurors is handled to avoid type issues.
  • Enhanced linting rules and configurations for better code quality.
  • Made various styling adjustments for consistency and clarity in UI components.

The following files were skipped due to too many changes: web/src/pages/Jurors/index.tsx, web/src/pages/Profile/Votes/VoteCard/index.tsx, web/src/pages/Profile/JurorCard/BottomContent/index.tsx, web/src/hooks/useHomePageContext.tsx, web/src/components/JurorLink.tsx, web/src/utils/getDrawnJurorsWithCount.ts, web/src/utils/crypto/hashJustification.ts, web/src/tests/e2e/utils/contracts.ts, web/src/components/Verdict/FinalDecision.tsx, web/src/hooks/queries/useAppealCost.ts, web/src/components/TxnHash.tsx, web/src/layout/Footer/index.tsx, web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/index.tsx, web/src/pages/Profile/Cases/index.tsx, web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx, web/src/pages/Cases/CaseDetails/Timeline.tsx, web/src/pages/Resolver/Parameters/Jurors.tsx, web/src/pages/Profile/Stakes/CurrentStakes/index.tsx, web/src/pages/Cases/CaseDetails/Voting/VotesDetails/index.tsx, web/src/components/DisputeView/DisputeInfo/DisputeInfoList.tsx, web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx, web/src/utils/getDisputeRequestParamsFromTxn.ts, web/src/hooks/queries/useDisputeKitClassicMultipliers.ts, web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx, web/src/components/DisputeView/DisputeListView.tsx, web/src/components/DisputeView/DisputeCardView.tsx, web/src/components/DisputePreview/DisputeContext.tsx, web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/index.tsx, web/src/components/GradientTokenIcons.tsx, web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx, web/src/pages/Jurors/DisplayJurors.tsx, web/src/pages/Home/CourtOverview/Chart.tsx, web/src/components/ClaimPnkButton.tsx, web/src/components/DisputeView/CardLabels/index.tsx, web/src/hooks/queries/usePolicyRegistryEvent.ts, web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx, web/src/pages/Courts/CourtDetails/TopSearch.tsx, web/src/pages/Profile/JurorCard/index.tsx, web/src/hooks/queries/usePopulatedDisputeData.ts, web/src/pages/Profile/Votes/index.tsx, web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx, web/src/pages/Home/CourtOverview/Stats.tsx, web/src/pages/Profile/JurorCard/StakingRewards.tsx, eslint-config/README.md

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

Summary by CodeRabbit

  • New Features

    • Two new Home Court Overview charts: Cases by Courts and Staked PNK by Courts.
    • Product menu icons show a circular skeleton placeholder while image assets load.
    • Swap success popup subtitle standardized for non-claim swaps.
  • Chores

    • Added pre-commit type checks for the web workspace.
    • Enabled linting and Prettier formatting on staged JS/TS files.
    • Tightened TypeScript settings to enforce stricter checks and include source files.

@kyrers kyrers self-assigned this May 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds web pre-commit typechecks and lint-staged, tightens web TypeScript and package scripts, converts many runtime/prop types to viem Address/Hash and bigint, replaces untyped chain indexing with typed chainKey lookups, moves contract construction into query functions, and applies numerous import/order/ESLint/logging tweaks across web/.

Changes

Web Development Tooling

Layer / File(s) Summary
Pre-commit hook
.husky/pre-commit
Runs Yarn workspace @kleros/kleros-v2-web check-types when staged files under web/ are present.
Lint-staged config
web/.lintstagedrc.json
New config mapping *.{js,jsx,ts,tsx} to eslint --fix and prettier --write.
TypeScript config
web/tsconfig.json, web/package.json, web/eslint.config.mjs
tsconfig enables noImplicitAny and adds include: ["src/**/*","global.d.ts"]; web/package.json check-types uses tsc --noEmit --declaration false; ESLint ignores added path.

Chain/address typing and builders

Layer / File(s) Summary
Typed chainKey lookups
web/src/actions/*/builders/*, web/src/actions/*/vote/*, web/src/actions/*/reveal/*
Replaces direct [...] [chain.id] lookups with typed chainKey (as keyof typeof ...) and uses disputeKit*Address[chainKey] across many builders and action creators.
Tests/config
web/src/tests/e2e/utils/contracts.ts
Derives chainId typed key from DEFAULT_CHAIN.id and indexes address maps via that key.

Types: viem Address/Hash, bigint, and hook typing

Layer / File(s) Summary
viem typing updates
web/src/**/* (many files)
Prop and param types migrated to Address and Hash where appropriate (TxnHash, StakeEventCard, ClaimPnkButton, many components/pages/hooks), aligning signatures to viem types.
bigint stake types
web/src/pages/Profile/Stakes/*
Stake props and interfaces use bigint (ICourtCard.stake, IStake.stake, CurrentStakes props) and caller sites convert with BigInt(...).
Hook typings & return types
web/src/hooks/queries/*, web/src/hooks/*
Added exported UserDraw alias and GroupedDraw, typed useHomePageExtraStats return, made various hook parameters optional (e.g., courtId?: string) and typed query.enabled guards. Moved contract construction into queryFn and added publicClient gating.

UI / import / lint / logging edits

Layer / File(s) Summary
UI components and pages
web/src/components/*, web/src/pages/*, web/src/layout/*
Many import reorderings, small formatting/whitespace edits, added image Skeleton placeholder in Product, OverlayPortal wrapped with styled PortalContainer, Popup payload simplified, and two BarChart wrapper components added (CasesByCourtsChart, StakedPNKByCourtsChart).
Lint and suppression changes
web/src/**
Added inline ESLint disables for exhaustive-deps and max-len in multiple files. Replaced many console.log error paths with console.error.
Styles / utils
web/src/styles/*, web/src/utils/*
Refactors to responsiveSize, scrollbar transition formatting, small API-safe guards (optional chaining), and safe truncation/fallbacks in display helpers.

Sequence Diagram(s)

(Conditions for sequence diagrams not met for a single coherent multi-component new feature beyond tooling and typing churn; none generated.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related Issues

Possibly Related PRs

Suggested labels

Type: Maintenance :construction:

Suggested reviewers

  • alcercu

"I hopped through git with lint in paw,
Types now tighter, noImplicitAny saw,
Pre-commit guards the web day's start,
ESLint and Prettier play their part,
A tidy branch from a rabbit's heart."

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pre-commit-type-check-web

Copy link
Copy Markdown
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.

Caution

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

⚠️ Outside diff range comments (2)
web/src/layout/Header/navbar/Product.tsx (2)

61-61: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid exposing the raw URL as alt text.

At Line 61, alt={Icon} reads the image URL to assistive tech. Use alt="" (decorative image) or alt={text} instead.

Proposed fix
-          <StyledImg alt={Icon} src={Icon} isLoaded={isImgLoaded} onLoad={() => setIsImgLoaded(true)} />
+          <StyledImg alt="" src={Icon} isLoaded={isImgLoaded} onLoad={() => setIsImgLoaded(true)} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/layout/Header/navbar/Product.tsx` at line 61, The alt prop for the
image currently passes the raw URL (alt={Icon}) which exposes the image URL to
assistive tech; update the <StyledImg> usage in Product.tsx to use a meaningful
accessible label or mark it decorative: replace alt={Icon} with either a
descriptive string prop (e.g., alt={productName} or alt={label}) if the image
conveys content, or alt="" if purely decorative, and keep the existing
src={Icon}, isLoaded={isImgLoaded} and onLoad={() => setIsImgLoaded(true)}
unchanged.

54-61: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Loading state can get stuck on broken URLs and won't reset on source changes.

At line 61, the skeleton is cleared only by onLoad. A failed image request leaves the placeholder forever, and changing the Icon prop won't reset isImgLoaded, so the skeleton won't show during loading of a new image.

Proposed fix
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
...
 const Product: React.FC<IProduct> = ({ text, url, Icon }) => {
   const [isImgLoaded, setIsImgLoaded] = useState(false);
+
+  useEffect(() => {
+    if (typeof Icon === "string") setIsImgLoaded(false);
+  }, [Icon]);

   return (
...
-          <StyledImg alt={Icon} src={Icon} isLoaded={isImgLoaded} onLoad={() => setIsImgLoaded(true)} />
+          <StyledImg
+            alt={Icon}
+            src={Icon}
+            isLoaded={isImgLoaded}
+            onLoad={() => setIsImgLoaded(true)}
+            onError={() => setIsImgLoaded(true)}
+          />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/layout/Header/navbar/Product.tsx` around lines 54 - 61, The image
loading state can get stuck because isImgLoaded is only set true in StyledImg's
onLoad and never reset on failures or when Icon changes; update the component to
handle both onError (call setIsImgLoaded(false) and optionally mark a failed
state) and reset isImgLoaded when the Icon prop changes (use a useEffect that
calls setIsImgLoaded(false) when Icon changes) so the Skeleton shows for new
sources and broken URLs don't leave the placeholder permanently; update
references to isImgLoaded, setIsImgLoaded, StyledImg and Icon accordingly.
🧹 Nitpick comments (1)
web/src/hooks/queries/useUser.ts (1)

61-70: ⚡ Quick win

Conditionally pass where variable only when using the filtered query.

Currently, variables always includes where: sanitizedWhere regardless of whether userQuery or userQueryDisputeFilter is selected. When using userQuery (which only declares $address: ID!), the where field is unnecessary. Passing it is harmless at runtime since GraphQL ignores undeclared variables, but it's inefficient and unclear.

More importantly, address?.toLowerCase() is computed twice (lines 62 and 69). Extract it to a variable to avoid redundant evaluation.

Suggested refactor
+  const lowerAddress = address?.toLowerCase();
   return useQuery<UserQuery | UserDisputeFilterQuery>({
-    queryKey: ["userQuery", address?.toLowerCase(), sanitizedWhere],
+    queryKey: ["userQuery", lowerAddress, sanitizedWhere],
     enabled: isEnabled,
     staleTime: STALE_TIME,
     queryFn: async () =>
       await graphqlBatcher.fetch({
         id: crypto.randomUUID(),
         document: query,
-        variables: { address: address?.toLowerCase(), where: sanitizedWhere },
+        variables: sanitizedWhere
+          ? { address: lowerAddress, where: sanitizedWhere }
+          : { address: lowerAddress },
       }),
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/hooks/queries/useUser.ts` around lines 61 - 70, Compute
address.toLowerCase() once into a local const (e.g., lowerAddress) and use that
in queryKey and in graphqlBatcher.fetch variables; then build the variables
object starting with { address: lowerAddress } and only add the where:
sanitizedWhere property when the filtered document is being used (refer to the
query document identifier userQueryDisputeFilter or another flag you already use
to pick the filtered query). Update the useQuery call to use lowerAddress
instead of address?.toLowerCase() twice and pass the conditionally constructed
variables object to graphqlBatcher.fetch in the queryFn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/src/layout/Header/navbar/Product.tsx`:
- Line 61: The alt prop for the image currently passes the raw URL (alt={Icon})
which exposes the image URL to assistive tech; update the <StyledImg> usage in
Product.tsx to use a meaningful accessible label or mark it decorative: replace
alt={Icon} with either a descriptive string prop (e.g., alt={productName} or
alt={label}) if the image conveys content, or alt="" if purely decorative, and
keep the existing src={Icon}, isLoaded={isImgLoaded} and onLoad={() =>
setIsImgLoaded(true)} unchanged.
- Around line 54-61: The image loading state can get stuck because isImgLoaded
is only set true in StyledImg's onLoad and never reset on failures or when Icon
changes; update the component to handle both onError (call setIsImgLoaded(false)
and optionally mark a failed state) and reset isImgLoaded when the Icon prop
changes (use a useEffect that calls setIsImgLoaded(false) when Icon changes) so
the Skeleton shows for new sources and broken URLs don't leave the placeholder
permanently; update references to isImgLoaded, setIsImgLoaded, StyledImg and
Icon accordingly.

---

Nitpick comments:
In `@web/src/hooks/queries/useUser.ts`:
- Around line 61-70: Compute address.toLowerCase() once into a local const
(e.g., lowerAddress) and use that in queryKey and in graphqlBatcher.fetch
variables; then build the variables object starting with { address: lowerAddress
} and only add the where: sanitizedWhere property when the filtered document is
being used (refer to the query document identifier userQueryDisputeFilter or
another flag you already use to pick the filtered query). Update the useQuery
call to use lowerAddress instead of address?.toLowerCase() twice and pass the
conditionally constructed variables object to graphqlBatcher.fetch in the
queryFn.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3b45ab75-7268-49b4-853a-0f3f2b1f3b76

📥 Commits

Reviewing files that changed from the base of the PR and between 29bbceb and 9d03392.

📒 Files selected for processing (38)
  • web/src/components/BlueIconTextButtonContainer.tsx
  • web/src/components/DottedMenuButton.tsx
  • web/src/components/ExtraStatsDisplay.tsx
  • web/src/components/InternalLink.tsx
  • web/src/components/LightButton.tsx
  • web/src/components/OverlayPortal.tsx
  • web/src/components/ScrollTop.tsx
  • web/src/components/StyledIcons/ClosedCircleIcon.tsx
  • web/src/components/Verdict/index.tsx
  • web/src/consts/socialmedia.ts
  • web/src/hooks/queries/useHomePageBlockQuery.ts
  • web/src/hooks/queries/useHomePageExtraStats.ts
  • web/src/hooks/queries/useJurorsByCoherenceScore.ts
  • web/src/hooks/queries/useTopStakedJurorsByCourt.ts
  • web/src/hooks/queries/useUser.ts
  • web/src/hooks/useFundAppeal.ts
  • web/src/hooks/useNavigateAndScrollTop.ts
  • web/src/hooks/useScrollTop.ts
  • web/src/hooks/useSortitionModule.ts
  • web/src/hooks/useToggleThemeContext.tsx
  • web/src/layout/Footer/index.tsx
  • web/src/layout/Header/navbar/Product.tsx
  • web/src/pages/Cases/CaseDetails/index.tsx
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/StakeEventCard/DesktopCard.tsx
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/StakeEventCard/MobileCard.tsx
  • web/src/pages/Courts/index.tsx
  • web/src/pages/GetPnk/index.tsx
  • web/src/pages/Home/CourtOverview/CasesByCourtsChart.tsx
  • web/src/pages/Home/CourtOverview/StakedPNKByCourtsChart.tsx
  • web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx
  • web/src/pages/Home/TopJurors/JurorCard/Score.tsx
  • web/src/pages/Home/index.tsx
  • web/src/pages/Profile/JurorCard/BottomContent/index.tsx
  • web/src/pages/Settings/index.tsx
  • web/src/styles/customScrollbar.ts
  • web/src/utils/getVoteChoice.ts
💤 Files with no reviewable changes (2)
  • web/src/pages/Home/TopJurors/JurorCard/Score.tsx
  • web/src/hooks/useSortitionModule.ts
✅ Files skipped from review due to trivial changes (30)
  • web/src/pages/Cases/CaseDetails/index.tsx
  • web/src/components/BlueIconTextButtonContainer.tsx
  • web/src/components/Verdict/index.tsx
  • web/src/pages/GetPnk/index.tsx
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/StakeEventCard/MobileCard.tsx
  • web/src/pages/Courts/index.tsx
  • web/src/components/InternalLink.tsx
  • web/src/components/StyledIcons/ClosedCircleIcon.tsx
  • web/src/pages/Home/TopJurors/JurorCard/JurorLevel.tsx
  • web/src/pages/Home/index.tsx
  • web/src/layout/Footer/index.tsx
  • web/src/utils/getVoteChoice.ts
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx
  • web/src/hooks/useToggleThemeContext.tsx
  • web/src/consts/socialmedia.ts
  • web/src/hooks/useFundAppeal.ts
  • web/src/hooks/useNavigateAndScrollTop.ts
  • web/src/components/LightButton.tsx
  • web/src/components/ExtraStatsDisplay.tsx
  • web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx
  • web/src/styles/customScrollbar.ts
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/StakeEventCard/DesktopCard.tsx
  • web/src/pages/Profile/JurorCard/BottomContent/index.tsx
  • web/src/pages/Settings/index.tsx
  • web/src/hooks/queries/useTopStakedJurorsByCourt.ts
  • web/src/components/OverlayPortal.tsx
  • web/src/hooks/queries/useJurorsByCoherenceScore.ts
  • web/src/components/ScrollTop.tsx
  • web/src/components/DottedMenuButton.tsx
  • web/src/hooks/queries/useHomePageExtraStats.ts

Copy link
Copy Markdown
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.

Caution

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

⚠️ Outside diff range comments (4)
web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx (1)

61-79: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid suppressing exhaustive-deps for initialization logic

The effect reads disputeData.disputeKitId but has an empty dependency array with the lint rule suppressed at line 78. Since disputeKitId is assigned after mount (e.g., when dispute data is fetched in Landing.tsx or when the court selection changes), the empty deps array with suppression can skip initialization if disputeKitId becomes available after the initial render. Use a one-time guard with useRef instead to preserve the initialization intent while handling post-mount updates:

Suggested refactor pattern
-import React, { Fragment, useEffect, useMemo } from "react";
+import React, { Fragment, useEffect, useMemo, useRef } from "react";
...
+  const initializedFromKitRef = useRef(false);
+
   // DEV: initial feature selection logic, included hardcoded logic
   useEffect(() => {
-    if (!isUndefined(disputeData?.disputeKitId)) {
-      const defaultKit = disputeKitsForDeployment.find((dk) => dk.id === disputeData.disputeKitId);
-      if (!defaultKit) return;
+    if (initializedFromKitRef.current) return;
+    if (isUndefined(disputeData?.disputeKitId)) return;
+
+    const defaultKit = disputeKitsForDeployment.find((dk) => dk.id === disputeData.disputeKitId);
+    if (!defaultKit) return;
 
-      // some kits like gated can have two feature sets, one for gatedERC20 and other for ERC1155
-      if (defaultKit?.featureSets.length > 1) {
-        if ((disputeData?.disputeKitData as IGatedDisputeData)?.isERC1155) {
-          // defaultKit.featureSets[0][0] - is either Classic or Shutter
-          setSelected([defaultKit.featureSets[0][0], Features.GatedErc1155]);
-        } else {
-          setSelected([defaultKit.featureSets[0][0], Features.GatedErc20]);
-        }
+    // some kits like gated can have two feature sets, one for gatedERC20 and other for ERC1155
+    if (defaultKit.featureSets.length > 1) {
+      if ((disputeData?.disputeKitData as IGatedDisputeData)?.isERC1155) {
+        setSelected([defaultKit.featureSets[0][0], Features.GatedErc1155]);
+      } else {
+        setSelected([defaultKit.featureSets[0][0], Features.GatedErc20]);
       }
-      } else if (defaultKit.featureSets.length === 1) {
-        setSelected(defaultKit.featureSets[0]);
-      }
+    } else if (defaultKit.featureSets.length === 1) {
+      setSelected(defaultKit.featureSets[0]);
     }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
+    initializedFromKitRef.current = true;
+  }, [disputeData?.disputeKitId, disputeData?.disputeKitData, disputeKitsForDeployment, setSelected]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx` around
lines 61 - 79, The effect currently suppresses exhaustive-deps and only runs on
mount, so if disputeData.disputeKitId is set after mount the initialization will
be skipped; update the useEffect in FeatureSelection to remove the
eslint-disable and instead depend on disputeData?.disputeKitId and
disputeKitsForDeployment, but guard execution with a useRef "initialized" flag
so the initialization logic (finding defaultKit and calling setSelected) runs
only once when disputeKitId first becomes defined; reference the existing
symbols useEffect, disputeData.disputeKitId, disputeKitsForDeployment,
setSelected and the new useRef flag to implement this one-time, post-mount
initialization.
web/src/pages/Home/CourtOverview/Chart.tsx (1)

136-149: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard empty tooltip elements before coordinate math.

When elements is empty, elements[0]?.element.x and elements[0]?.element.y are undefined. Lines 142–143 then compute arithmetic with undefined values (e.g., undefined + this.height), producing NaN and causing incorrect conditional logic. Per Chart.js 3.9.1 API documentation, custom positioners should return false when no valid elements exist.

Add an early guard to return false when no active element is present.

Proposed fix
 Tooltip.positioners.custom = function (this: TooltipModel<ChartType>, elements: readonly ActiveElement[]) {
+  const first = elements[0];
+  if (!first) return false;
+
   const height = this.chart.chartArea.height;
   const width = this.chart.chartArea.width;
 
-  const x = elements[0]?.element.x;
-  const y = elements[0]?.element.y;
+  const x = first.element.x;
+  const y = first.element.y;
   const isAtTop = height > y + this.height;
   const isAtEnd = width < x + this.width;
 
   return {
-    x: elements[0]?.element.x,
-    y: elements[0]?.element.y,
+    x,
+    y,
     xAlign: isAtTop ? (isAtEnd ? "right" : "left") : "center",
     yAlign: isAtTop ? "center" : "bottom",
   };
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Home/CourtOverview/Chart.tsx` around lines 136 - 149, The
custom tooltip positioner (Tooltip.positioners.custom) does not guard for an
empty elements array and then performs arithmetic on undefined (e.g.,
elements[0]?.element.x + this.height), causing NaN; add an early check in
Tooltip.positioners.custom to return false when elements is empty or
elements[0]?.element is falsy, before computing height/width/x/y and the
isAtTop/isAtEnd logic, so the positioner returns false per Chart.js expectations
when no active element exists.
web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx (1)

68-80: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove eslint-disable-next-line react-hooks/exhaustive-deps and add proper dependencies.

This effect reads publicClient and disputeData.aliasesArray but with an empty dependency array, it runs only once at mount. If these values are populated after the initial render, validation never runs, breaking the pre-populated alias scenario described in the comment. Additionally, line 70 uses .map(async ...) without awaiting, creating fire-and-forget async callbacks that can cause stale state issues.

Suggested fix for exhaustive-deps suppression
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
+  }, [disputeData.aliasesArray, publicClient]);

Also consider wrapping the map with Promise.all() to ensure all validations complete before moving on.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx` around
lines 68 - 80, Remove the eslint-disable comment and update the useEffect that
references useEffect, publicClient, disputeData.aliasesArray, validateAddress
and setDisputeData so it properly lists publicClient and
disputeData.aliasesArray in its dependency array; replace the .map(async ...)
fire-and-forget pattern with a Promise.all over disputeData.aliasesArray.map to
await all validateAddress(alias.address, publicClient) calls, build a new
aliases array immutably (do not mutate disputeData.aliasesArray in place)
updating each .isValid based on the awaited results, then call setDisputeData
once with the new disputeData object; ensure you handle the case where
publicClient or aliasesArray is undefined to avoid unnecessary runs.
web/src/components/DisputeFeatures/Features/GatedErc1155.tsx (1)

56-71: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Line 70: Dependency list is missing disputeData, risking stale-state overwrites when both ERC20 and ERC1155 validations update concurrently.

The effect spreads disputeData (line 65) but omits it from dependencies. If the context is updated elsewhere before this effect executes, the spread will use a stale snapshot and overwrite newer values. The comment on line 57 acknowledges potential conflicts with the parallel ERC20 validation effect.

Suggested fix
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isValid, setDisputeData, props.checked]);
+  }, [isValid, props.checked, disputeData, setDisputeData]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/DisputeFeatures/Features/GatedErc1155.tsx` around lines 56
- 71, The effect currently spreads disputeData directly and can overwrite
concurrent updates; instead use a functional state updater inside the useEffect
so you always operate on the latest disputeData: in the useEffect that
references isValid, props.checked and calls setDisputeData, replace the
setDisputeData({ ...disputeData, disputeKitData: { ...currentData,
isTokenGateValid: isValid } }) call with setDisputeData(prev => ({ ...prev,
disputeKitData: { ...(prev.disputeKitData as IGatedDisputeData),
isTokenGateValid: isValid } })), and keep the dependency list to [isValid,
setDisputeData, props.checked] (or if you prefer to keep the original pattern
instead, add disputeData to the deps); this ensures useEffect, currentData,
disputeData and setDisputeData are updated safely without stale overwrites.
🧹 Nitpick comments (1)
web/src/components/Popup/Description/SwapSuccess.tsx (1)

60-68: ⚡ Quick win

Align ISwapSuccess with the props actually used.

from/to remain in the interface but are no longer consumed after Line 68. Either remove them from the type or wire them back into rendering to keep the component contract unambiguous.

Suggested cleanup
-import { Token } from "pages/GetPnk/Swap/TokenSelect";
-
 interface ISwapSuccess {
   hash: string;
   amount: string;
-  from?: Token;
-  to?: Token;
   isClaim?: boolean;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/Popup/Description/SwapSuccess.tsx` around lines 60 - 68,
The ISwapSuccess interface declares optional from and to Token props that the
SwapSuccess component no longer uses; either remove these unused properties from
ISwapSuccess to match the actual props consumed by the SwapSuccess component, or
reintroduce them into the component’s JSX/logic (e.g., display token icons/names
or include them in the success message) and update the SwapSuccess prop
destructuring to accept from and to; make the change in the ISwapSuccess
declaration and the SwapSuccess component signature so the type and
implementation stay consistent (references: ISwapSuccess, SwapSuccess).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/src/components/DisputeFeatures/Features/GatedErc1155.tsx`:
- Around line 56-71: The effect currently spreads disputeData directly and can
overwrite concurrent updates; instead use a functional state updater inside the
useEffect so you always operate on the latest disputeData: in the useEffect that
references isValid, props.checked and calls setDisputeData, replace the
setDisputeData({ ...disputeData, disputeKitData: { ...currentData,
isTokenGateValid: isValid } }) call with setDisputeData(prev => ({ ...prev,
disputeKitData: { ...(prev.disputeKitData as IGatedDisputeData),
isTokenGateValid: isValid } })), and keep the dependency list to [isValid,
setDisputeData, props.checked] (or if you prefer to keep the original pattern
instead, add disputeData to the deps); this ensures useEffect, currentData,
disputeData and setDisputeData are updated safely without stale overwrites.

In `@web/src/pages/Home/CourtOverview/Chart.tsx`:
- Around line 136-149: The custom tooltip positioner
(Tooltip.positioners.custom) does not guard for an empty elements array and then
performs arithmetic on undefined (e.g., elements[0]?.element.x + this.height),
causing NaN; add an early check in Tooltip.positioners.custom to return false
when elements is empty or elements[0]?.element is falsy, before computing
height/width/x/y and the isAtTop/isAtEnd logic, so the positioner returns false
per Chart.js expectations when no active element exists.

In `@web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx`:
- Around line 61-79: The effect currently suppresses exhaustive-deps and only
runs on mount, so if disputeData.disputeKitId is set after mount the
initialization will be skipped; update the useEffect in FeatureSelection to
remove the eslint-disable and instead depend on disputeData?.disputeKitId and
disputeKitsForDeployment, but guard execution with a useRef "initialized" flag
so the initialization logic (finding defaultKit and calling setSelected) runs
only once when disputeKitId first becomes defined; reference the existing
symbols useEffect, disputeData.disputeKitId, disputeKitsForDeployment,
setSelected and the new useRef flag to implement this one-time, post-mount
initialization.

In `@web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx`:
- Around line 68-80: Remove the eslint-disable comment and update the useEffect
that references useEffect, publicClient, disputeData.aliasesArray,
validateAddress and setDisputeData so it properly lists publicClient and
disputeData.aliasesArray in its dependency array; replace the .map(async ...)
fire-and-forget pattern with a Promise.all over disputeData.aliasesArray.map to
await all validateAddress(alias.address, publicClient) calls, build a new
aliases array immutably (do not mutate disputeData.aliasesArray in place)
updating each .isValid based on the awaited results, then call setDisputeData
once with the new disputeData object; ensure you handle the case where
publicClient or aliasesArray is undefined to avoid unnecessary runs.

---

Nitpick comments:
In `@web/src/components/Popup/Description/SwapSuccess.tsx`:
- Around line 60-68: The ISwapSuccess interface declares optional from and to
Token props that the SwapSuccess component no longer uses; either remove these
unused properties from ISwapSuccess to match the actual props consumed by the
SwapSuccess component, or reintroduce them into the component’s JSX/logic (e.g.,
display token icons/names or include them in the success message) and update the
SwapSuccess prop destructuring to accept from and to; make the change in the
ISwapSuccess declaration and the SwapSuccess component signature so the type and
implementation stay consistent (references: ISwapSuccess, SwapSuccess).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7bac7b8-a921-4190-9425-ee18e7516241

📥 Commits

Reviewing files that changed from the base of the PR and between 709866a and 0cd2660.

📒 Files selected for processing (35)
  • web/eslint.config.mjs
  • web/src/components/ClaimPnkButton.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc1155.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc20.tsx
  • web/src/components/DisputePreview/DisputeContext.tsx
  • web/src/components/EnsureAuth.tsx
  • web/src/components/GradientTokenIcons.tsx
  • web/src/components/Popup/Description/SwapSuccess.tsx
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/components/Popup/index.tsx
  • web/src/components/ScrollTop.tsx
  • web/src/consts/index.ts
  • web/src/context/QueryClientProvider.tsx
  • web/src/hooks/queries/usePopulatedDisputeData.ts
  • web/src/hooks/useLocalStorage.ts
  • web/src/hooks/useSessionStorage.ts
  • web/src/layout/Footer/index.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/EmailVerificationInfo.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/index.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx
  • web/src/pages/Cases/CaseDetails/Voting/Shutter/Reveal.tsx
  • web/src/pages/Home/CourtOverview/Chart.tsx
  • web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx
  • web/src/pages/Profile/JurorCard/StakingRewards.tsx
  • web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx
  • web/src/pages/Resolver/Parameters/Jurors.tsx
  • web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx
  • web/src/pages/Resolver/Policy/index.tsx
  • web/src/styles/responsiveSize.ts
  • web/src/utils/crypto/hashJustification.ts
  • web/src/utils/fetchStakingEventsByCourt.ts
  • web/src/utils/getDisputeRequestParamsFromTxn.ts
💤 Files with no reviewable changes (4)
  • web/src/components/DisputePreview/DisputeContext.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
  • web/src/components/Popup/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx
✅ Files skipped from review due to trivial changes (21)
  • web/src/pages/Cases/CaseDetails/Voting/Shutter/Reveal.tsx
  • web/src/components/EnsureAuth.tsx
  • web/src/pages/Resolver/Policy/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx
  • web/src/utils/getDisputeRequestParamsFromTxn.ts
  • web/src/hooks/useSessionStorage.ts
  • web/src/pages/Resolver/Parameters/Jurors.tsx
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/pages/Profile/JurorCard/StakingRewards.tsx
  • web/src/styles/responsiveSize.ts
  • web/src/context/QueryClientProvider.tsx
  • web/src/consts/index.ts
  • web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx
  • web/src/components/ScrollTop.tsx
  • web/src/utils/crypto/hashJustification.ts
  • web/src/components/GradientTokenIcons.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc20.tsx
  • web/eslint.config.mjs
  • web/src/hooks/queries/usePopulatedDisputeData.ts
  • web/src/hooks/useLocalStorage.ts

@kyrers
Copy link
Copy Markdown
Contributor Author

kyrers commented May 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
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: 12

Caution

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

⚠️ Outside diff range comments (5)
web/src/pages/Cases/CaseDetails/MaintenanceButtons/ExecuteRuling.tsx (1)

45-50: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Disable the button when publicClient is unavailable.

Line 50 now no-ops without publicClient, but the button can still appear enabled. Include !publicClient in isDisabled so users don’t hit a silent click.

Proposed fix
   const isDisabled = useMemo(
-    () => isUndefined(id) || isError || isLoading || period !== Period.Execution || ruled,
-    [id, isError, isLoading, period, ruled]
+    () => isUndefined(id) || isError || isLoading || period !== Period.Execution || ruled || !publicClient,
+    [id, isError, isLoading, period, ruled, publicClient]
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Cases/CaseDetails/MaintenanceButtons/ExecuteRuling.tsx` around
lines 45 - 50, The isDisabled memo currently omits publicClient so the button
can be clickable while publicClient is missing; update the isDisabled condition
to include !publicClient (e.g. () => isUndefined(id) || isError || isLoading ||
!publicClient || period !== Period.Execution || ruled) and add publicClient to
the dependency array so the memo updates when publicClient becomes available;
this aligns isDisabled with the early return in handleClick.
web/src/components/JurorLink.tsx (1)

55-57: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Type IJurorLink.address as Address and remove local assertions.

IdenticonOrAvatar and AddressOrName both expect 0x${string} (Address type), but JurorLink accepts string and casts at use sites. Move the type constraint to the prop interface and make the import type-only since Address is only needed for type annotations.

Suggested refactor
-import { Address } from "viem";
+import type { Address } from "viem";
@@
 interface IJurorLink {
-  address: string;
+  address: Address;
   isInternalLink?: boolean;
   smallDisplay?: boolean;
 }
@@
-        <IdenticonOrAvatar address={address as Address} />
+        <IdenticonOrAvatar address={address} />
@@
-          <AddressOrName address={address as Address} smallDisplay={smallDisplay} />
+          <AddressOrName address={address} smallDisplay={smallDisplay} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/JurorLink.tsx` around lines 55 - 57, Change
IJurorLink.address from string to the Address type and remove any local "0x"
casts/assertions at usage sites: update the interface declaration IJurorLink {
address: Address; ... } (import Address as a type-only import) and then remove
the runtime casts where IdenticonOrAvatar and AddressOrName are passed the
address so they receive the correctly typed Address directly; ensure the Address
import uses import type { Address } to avoid emitting runtime code.
web/src/components/DisputeFeatures/Features/GatedErc20.tsx (1)

56-70: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The proposed refactor is incompatible with the actual setDisputeData API.

The stale closure concern is valid—the effect reads disputeData without including it in dependencies. However, the suggested refactor uses a functional updater pattern (prev) => { ... } which setDisputeData does not support. The useLocalStorage hook returns a setter with signature (newValue: T) => void, not (prev: T) => T.

To fix the stale closure safely, either:

  1. Add disputeData to the dependency array (verify this doesn't cause loops), or
  2. Use a ref-based approach to access the current value while keeping deps stable.

The current eslint suppression addresses a real issue but needs a compatible solution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/DisputeFeatures/Features/GatedErc20.tsx` around lines 56 -
70, The effect reads disputeData but the setDisputeData setter from
useLocalStorage does not accept a functional updater, so replace the eslint
suppression with a compatible fix: either add disputeData to the dependency list
of the useEffect (so the effect uses the latest disputeData when updating
disputeKitData on isValid change) or keep deps stable and introduce a mutable
ref (e.g., disputeDataRef) that you update whenever disputeData changes and then
read disputeDataRef.current inside the useEffect; ensure you update
disputeKitData only when props.checked is true and when
currentData.isTokenGateValid differs from isValid, and reference
IGatedDisputeData/currentData and setDisputeData exactly as in the existing
block.
web/src/hooks/queries/usePopulatedDisputeData.ts (1)

33-38: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid unsafe non-null assertion on templateId.

The enabled guard only excludes undefined, but Line 50 force-unwraps templateId. If templateId is null, this throws at runtime before the query call.

Suggested fix
-  const isEnabled =
+  const templateId = disputeData?.dispute?.templateId;
+  const isEnabled =
     !isUndefined(disputeID) &&
     !isUndefined(disputeData) &&
     !isUndefined(disputeData?.dispute) &&
     !isUndefined(disputeData.dispute?.arbitrableChainId) &&
-    !isUndefined(disputeData.dispute?.templateId);
+    templateId != null;
...
-            variables: { id: disputeData.dispute!.templateId!.toString() },
+            variables: { id: templateId.toString() },

Also applies to: 50-50

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/hooks/queries/usePopulatedDisputeData.ts` around lines 33 - 38, The
current isEnabled guard only excludes undefined but not null, yet later code
force-unwraps disputeData.dispute.templateId causing a runtime throw if
templateId === null; update the guard for isEnabled to use a nullish check
(e.g., disputeData?.dispute?.templateId != null or !== null && !== undefined) so
templateId is guaranteed non-null before any non-null assertion, and remove any
unnecessary non-null assertions where templateId is used (or use a safe
access/early return) in the query invocation that currently force-unwraps
templateId.
web/src/components/Popup/MiniGuides/PageContentsTemplate.tsx (1)

100-107: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard missing right-page component before rendering.

RightPageComponent can be undefined when page counts diverge, which causes an invalid element render crash.

Suggested fix
 const RightContent: React.FC<{ currentPage: number; rightPageComponents: React.FC[] }> = ({
   currentPage,
   rightPageComponents,
 }) => {
   const RightPageComponent = rightPageComponents[currentPage - 1];
+  if (!RightPageComponent) return null;
 
   return <RightPageComponent />;
 };

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/Popup/MiniGuides/PageContentsTemplate.tsx` around lines
100 - 107, RightContent currently grabs RightPageComponent =
rightPageComponents[currentPage - 1] and renders it directly, which can be
undefined if page counts diverge; update RightContent to check that
RightPageComponent is a valid component (and that currentPage is in range)
before rendering and return a safe fallback (null or a placeholder) when
undefined. Specifically, in the RightContent component, validate
rightPageComponents and index (currentPage - 1) and only render
<RightPageComponent /> when it exists, otherwise render null or an explicit
fallback to avoid invalid element render crashes; apply the same guard for the
analogous usage at the other occurrence mentioned.
🧹 Nitpick comments (7)
web/src/tests/e2e/utils/contracts.ts (1)

18-22: 💤 Low value

Consider consolidating redundant type assertions.

SonarCloud correctly identifies that the type assertions on lines 20-22 may be redundant since chainId is already cast to a compatible key type on line 18. If all contract address maps share the same chain ID keys, a single cast should suffice:

const chainId = DEFAULT_CHAIN.id as keyof typeof disputeResolverAddress;
export const DISPUTE_RESOLVER_ADDRESS = disputeResolverAddress[chainId];
export const KLEROS_CORE_ADDRESS = klerosCoreAddress[chainId];
export const SORTITION_MODULE_ADDRESS = sortitionModuleAddress[chainId];
export const PNK_ADDRESS = pnkAddress[chainId];

That said, the current approach is defensive and ensures type safety even if the maps diverge in the future. Given the PR's focus on stricter TypeScript configuration, this can be left as-is or refactored for cleaner code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/tests/e2e/utils/contracts.ts` around lines 18 - 22, The code
repeatedly casts chainId when indexing each address map even though chainId is
already declared as DEFAULT_CHAIN.id as keyof typeof disputeResolverAddress;
remove the redundant per-access assertions and use the single typed variable
(chainId) for all lookups: update usages of disputeResolverAddress[chainId],
klerosCoreAddress[chainId], sortitionModuleAddress[chainId], and
pnkAddress[chainId] to rely on the initial cast (or if you prefer defensive
typing, change the initial cast to a common union type shared by all maps) so
the extra "as keyof typeof ..." on KLEROS_CORE_ADDRESS,
SORTITION_MODULE_ADDRESS, and PNK_ADDRESS is eliminated.
web/src/hooks/queries/useCourtTree.ts (1)

61-61: ⚖️ Poor tradeoff

Type cast in recursive mapping may hide type incompatibilities.

The cast child as Court assumes each child conforms to the full Court type structure. While functionally this is likely correct (GraphQL returns consistent nested court structures), TypeScript may not be able to prove that deeply nested children have the same type as the root court.

This is a common limitation with recursive types. If the cast becomes problematic, consider:

  1. Using a more specific child type that reflects the actual GraphQL schema
  2. Structuring the query to ensure type uniformity
  3. Using a type guard instead of an assertion

For now, this cast is acceptable given TypeScript's limitations with recursive structures, but it's worth noting for future refactoring.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/hooks/queries/useCourtTree.ts` at line 61, The recursive cast "child
as Court" in the rootCourtToItems call can hide mismatches between nested nodes
and the Court type; replace the assertion with a proper type guard or a specific
child type: implement an isCourt(value): value is Court guard and use
court.children.filter(isCourt).map(child => rootCourtToItems(child, value)) (or
adjust the GraphQL generated type to a recursive CourtNode used by
rootCourtToItems) so TypeScript can verify nested shapes instead of relying on a
blind cast; update references to rootCourtToItems, court.children, and the Court
type accordingly.
web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx (1)

7-8: ⚡ Quick win

Remove the ClassicVoteParams assertion on the vote payload.

The assertion suppresses type checking at the callsite; letting TypeScript infer here is safer and catches drift automatically. The useVote mutation accepts the union type VoteParams, and the object being passed has a dynamic type field that may not narrow to ClassicVoteParams specifically, making the assertion unnecessary and potentially hiding type mismatches.

Suggested refactor
-import { ClassicVoteParams } from "~src/actions/vote/params";
@@
-      await vote({
+      await vote({
         disputeId: parsedDisputeID,
         voteIds: parsedVoteIDs,
         choice: voteOption,
         salt: BigInt(currentRoundIndex),
         justification,
         type: disputeKitName ?? DisputeKits.Classic,
-      } as ClassicVoteParams);
+      });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx` around lines 7 - 8,
In the Vote component, remove the explicit cast to ClassicVoteParams when
passing the payload into the useVote mutation: stop asserting the object as
ClassicVoteParams and let TypeScript infer the union VoteParams instead; locate
the callsite where you construct the vote payload (the variable/inline object
passed to useVote) and delete the "as ClassicVoteParams" cast, or change the
payload's declared type to VoteParams if an explicit annotation is required,
ensuring the useVote(...) call accepts the inferred union without suppressing
type checking.
web/src/pages/Resolver/Preview/index.tsx (1)

83-85: ⚡ Quick win

Consider consolidating types to eliminate the cast.

The comment notes that IDisputeTemplate is a subset of DisputeDetails and that types should be consolidated. Consider creating a shared base type or adjusting the template type to match DisputeDetails to avoid the type assertion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Resolver/Preview/index.tsx` around lines 83 - 85, The cast on
disputeTemplate (used in <DisputeContext disputeDetails={disputeTemplate as
DisputeDetails} />) hides a type mismatch between IDisputeTemplate and
DisputeDetails; remove the assertion by consolidating types: either define a
shared base interface (e.g., BaseDispute) that contains the common fields and
have both IDisputeTemplate and DisputeDetails extend it, or widen the
disputeTemplate/type of DisputeContext.disputeDetails to accept the template
type (or make IDisputeTemplate extend DisputeDetails if appropriate). Update the
relevant type declarations (IDisputeTemplate, DisputeDetails, and the
DisputeContext prop type) so the cast is unnecessary and the compiler verifies
compatibility.
web/src/hooks/useHomePageContext.tsx (1)

22-23: ⚡ Quick win

Memoize the context value to prevent unnecessary re-renders.

The context value object is recreated on every render, causing all consumers to re-render even when data and error haven't changed.

⚡ Proposed fix using useMemo
  const { data, error } = useHomePageQuery(timeframe);
-  return <Context.Provider value={{ data, error }}>{children}</Context.Provider>;
+  const value = useMemo(() => ({ data, error }), [data, error]);
+  return <Context.Provider value={value}>{children}</Context.Provider>;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/hooks/useHomePageContext.tsx` around lines 22 - 23, The
Context.Provider value is recreated on every render because the object literal {
data, error } is not memoized; update useHomePageContext (the component using
useHomePageQuery) to memoize the provider value with useMemo so it only changes
when data or error change and pass that memoized value to Context.Provider to
avoid unnecessary consumer re-renders.
web/src/pages/Profile/JurorCard/StakingRewards.tsx (1)

6-8: ⚡ Quick win

Remove the commented-out implementation block.

Keeping this inactive code in-place makes the file harder to maintain and keeps static-analysis noise around. Please delete it and rely on git history if it needs to be restored.

Also applies to: 20-50

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Profile/JurorCard/StakingRewards.tsx` around lines 6 - 8,
Delete the inactive commented-out import and component code block that includes
the commented imports "Box as _Box, Button" and "EnsureChain" and any other
commented implementation lines in the same file (the commented block around
those imports and the larger commented region spanning the earlier file area),
leaving only live code; rely on git history if recovery is needed and ensure no
other commented implementation remnants remain between the same surrounding
identifiers (e.g., references to _Box, Button, EnsureChain) so static-analysis
and maintenance noise are removed.
web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx (1)

7-8: ⚡ Quick win

Remove the unnecessary ClassicCommitParams cast.

The assertion is redundant here and weakens type feedback; inference should be kept as-is.

♻️ Proposed cleanup
-import { ClassicCommitParams } from "~src/actions/commit/params";
@@
-      await castCommit({
+      await castCommit({
         type: disputeKitName ?? DisputeKits.Classic,
         disputeId: parsedDisputeID,
         choice,
         voteIds: parsedVoteIDs,
         roundIndex: Number(currentRoundIndex),
-      } as ClassicCommitParams);
+      });

Please verify by running the web workspace typecheck after this change.

Also applies to: 53-53

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx` around lines 7 -
8, Remove the unnecessary ClassicCommitParams type assertion used in this file:
drop the explicit cast to ClassicCommitParams wherever it's applied (the import
can remain) — locate usages in Commit.tsx (including the occurrence around line
53) and let TypeScript infer the type instead of using the assertion; after
removing the casts, run the web workspace typecheck to ensure no errors remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/actions/commit/builders/classic.builder.ts`:
- Around line 20-25: The code uses a type assertion for chainKey and directly
indexes disputeKitClassicAddress, which can yield undefined at runtime for
unsupported chain IDs; update the builder (e.g., in classic.builder.ts where
chainKey and disputeKitClassicAddress are used) to first perform a runtime
guard: check that disputeKitClassicAddress hasOwnProperty(chain.id) or that
disputeKitClassicAddress[chain.id] is truthy, and if not reject/throw a clear
error or return a failed Result (consistent with other builders) before
proceeding to build the transaction; apply the same pattern to the other builder
files (commit, vote, reveal, fundAppeal) so they all validate the chain ID and
fail fast with a descriptive error instead of allowing an undefined address to
be returned.

In `@web/src/actions/commit/builders/gatedShutter.builder.ts`:
- Around line 38-43: The code uses a type assertion for chain.id when accessing
disputeKitGatedShutterAddress which can still yield an undefined address at
runtime; add a runtime guard in the builder (around where chain.id is used to
compute chainKey and the returned address) to check that
disputeKitGatedShutterAddress[chainKey] is defined, and if not throw a clear
error or return a safe failure (e.g., throw new Error or return null/undefined)
before proceeding to the write path so the returned object (account, address,
abi) never contains an undefined address.

In `@web/src/actions/fundAppeal/builders/classic.builder.ts`:
- Around line 11-15: The code unsafely casts chain.id to a key for
disputeKitClassicAddress (chainKey) which can yield undefined address at
runtime; update the builder to validate the lookup by checking that
disputeKitClassicAddress hasOwnProperty(chain.id) or that
disputeKitClassicAddress[chain.id] is truthy before returning, throw or return a
clear error if missing, and only set address to
disputeKitClassicAddress[chain.id] when present; reference the variables
chain.id, chainKey, disputeKitClassicAddress, and the returned properties
account/address to locate and modify the lookup logic.

In `@web/src/actions/fundAppeal/builders/classicUniversity.builder.ts`:
- Around line 11-15: The code currently asserts chain.id into chainKey and
directly indexes disputeKitClassicUniversityAddress, which can yield undefined
at runtime; replace that with a runtime guard: compute chainKey from chain.id,
look up const address = disputeKitClassicUniversityAddress[chainKey], and if
address is undefined (i.e., unsupported chain) throw or return an explicit
error/result instead of returning an object with an undefined address; update
the return in the builder (the block that returns account and address) to use
the validated address so invalid contract calls are prevented.

In `@web/src/actions/reveal/builders/classic.builder.ts`:
- Around line 11-15: The code currently casts chain.id to a key (chainKey) for
disputeKitClassicAddress and may return undefined at runtime; update the
function (around chain.id / chainKey in classic.builder) to validate that
disputeKitClassicAddress hasOwnProperty(chain.id) (or key in
disputeKitClassicAddress) before using it, and if missing either throw a clear
error or return a safe fallback instead of allowing address to be undefined;
ensure the returned object (account, address) uses the validated lookup so
transactions never receive an undefined address.

In `@web/src/actions/reveal/builders/classicUniversity.builder.ts`:
- Around line 11-15: The code currently casts chain.id to chainKey and directly
indexes disputeKitClassicUniversityAddress which can yield undefined for
unsupported chains; add a runtime guard in the builder before returning (use the
chainKey variable) that checks whether
disputeKitClassicUniversityAddress[chainKey] is defined and if not throw a clear
Error (include the offending chain.id in the message) so the function fails fast
instead of returning an undefined address in the transaction config.

In `@web/src/actions/vote/builders/argentinaConsumerProtection.builder.ts`:
- Around line 14-18: The code currently casts chain.id to a key (chainKey) and
directly indexes disputeKitGatedArgentinaConsumerProtectionAddress which can
yield undefined at runtime; update the builder to defensively check that
chain.id exists in disputeKitGatedArgentinaConsumerProtectionAddress (following
the pattern in useDisputeKitAddresses) before using it, and throw or return a
clear error if not found; apply this same runtime guard pattern to all dispute
kit builders referenced (functions/variables like chainKey,
disputeKitGatedArgentinaConsumerProtectionAddress, and the other dispute kit
address maps used in classic, gated, shutter, gatedShutter, classicUniversity
builders across vote and commit actions) so no builder dereferences an address
with an unchecked type assertion.

In `@web/src/hooks/queries/usePolicyRegistryEvent.ts`:
- Line 11: The enabled flag and runtime guard are inconsistent: change any
runtime guard in queryFn that uses falsy checks (e.g., if (!courtID ||
!publicClient)) to use isUndefined() consistently (e.g., isUndefined(courtID) ||
isUndefined(publicClient)) so courtID=0 is treated as valid, and replace the
incorrect throw Error; with throwing an actual Error instance (throw new
Error('descriptive message')) so queryFn throws a proper Error; update
references in usePolicyRegistryEvent.ts for isEnabled, queryFn, courtID and
publicClient accordingly.

In `@web/src/pages/Cases/CaseDetails/MaintenanceButtons/DistributeRewards.tsx`:
- Around line 48-52: The klerosCoreAddress lookup uses a cast but may be
undefined at runtime; in DistributeRewards.tsx validate that the resolved chain
key exists in klerosCoreAddress before building baseArgs: compute chainKey from
chainId/DEFAULT_CHAIN.id, check if chainKey is a valid key (e.g., using
Object.prototype.hasOwnProperty.call(klerosCoreAddress, chainKey) or chainKey in
klerosCoreAddress), and only set baseArgs.address = klerosCoreAddress[chainKey]
when defined; if not defined, handle the case (throw or return early, disable
the action UI, or use a safe fallback) so execute/contract config never receives
undefined address. Ensure you update the references to chainKey and baseArgs in
this component accordingly.

In `@web/src/pages/Resolver/Parameters/Jurors.tsx`:
- Around line 70-71: The effect mutates disputeData without including it in deps
causing a stale closure; change the setDisputeData call in the useEffect to use
the functional updater form so it reads the latest state (e.g.
setDisputeData(prev => ({ ...prev, arbitrationCost: data?.toString() }))) and
keep the dependency array as [data] (remove the eslint-disable comment so lint
stays valid).

In `@web/src/utils/getDrawnJurorsWithCount.ts`:
- Line 1: The cast of transactionHash to viem's Hash in getDrawnJurorsWithCount
(and similarly in Evidence/index.tsx and DisputeTimeline.tsx) accepts any
GraphQL string and bypasses type safety; fix by importing and using viem's
isHash to validate the string before casting (e.g., check
isHash(transactionHashString) and only then cast to Hash or handle invalid
values—throw, return null, or skip), updating the logic in the function that
produces transactionHash so invalid hashes don't propagate downstream.

In `@web/src/utils/getGraphqlUrl.ts`:
- Around line 17-19: The return logic for isDisputeTemplate uses an unsafe type
assertion for CHAINID_TO_DISPUTE_TEMPLATE_SUBGRAPH[chainId as keyof typeof
CHAINID_TO_DISPUTE_TEMPLATE_SUBGRAPH], which can yield undefined for unsupported
chainId values; update the getGraphqlUrl logic to first check whether chainId
exists as a key in CHAINID_TO_DISPUTE_TEMPLATE_SUBGRAPH (e.g., using
Object.prototype.hasOwnProperty.call or chainId in
CHAINID_TO_DISPUTE_TEMPLATE_SUBGRAPH) and only return the mapped URL when
present, otherwise fall back to coreUrl or throw a clear error; ensure you
reference the same symbols (chainId, isDisputeTemplate,
CHAINID_TO_DISPUTE_TEMPLATE_SUBGRAPH, coreUrl) so callers get a safe
deterministic URL.

---

Outside diff comments:
In `@web/src/components/DisputeFeatures/Features/GatedErc20.tsx`:
- Around line 56-70: The effect reads disputeData but the setDisputeData setter
from useLocalStorage does not accept a functional updater, so replace the eslint
suppression with a compatible fix: either add disputeData to the dependency list
of the useEffect (so the effect uses the latest disputeData when updating
disputeKitData on isValid change) or keep deps stable and introduce a mutable
ref (e.g., disputeDataRef) that you update whenever disputeData changes and then
read disputeDataRef.current inside the useEffect; ensure you update
disputeKitData only when props.checked is true and when
currentData.isTokenGateValid differs from isValid, and reference
IGatedDisputeData/currentData and setDisputeData exactly as in the existing
block.

In `@web/src/components/JurorLink.tsx`:
- Around line 55-57: Change IJurorLink.address from string to the Address type
and remove any local "0x" casts/assertions at usage sites: update the interface
declaration IJurorLink { address: Address; ... } (import Address as a type-only
import) and then remove the runtime casts where IdenticonOrAvatar and
AddressOrName are passed the address so they receive the correctly typed Address
directly; ensure the Address import uses import type { Address } to avoid
emitting runtime code.

In `@web/src/components/Popup/MiniGuides/PageContentsTemplate.tsx`:
- Around line 100-107: RightContent currently grabs RightPageComponent =
rightPageComponents[currentPage - 1] and renders it directly, which can be
undefined if page counts diverge; update RightContent to check that
RightPageComponent is a valid component (and that currentPage is in range)
before rendering and return a safe fallback (null or a placeholder) when
undefined. Specifically, in the RightContent component, validate
rightPageComponents and index (currentPage - 1) and only render
<RightPageComponent /> when it exists, otherwise render null or an explicit
fallback to avoid invalid element render crashes; apply the same guard for the
analogous usage at the other occurrence mentioned.

In `@web/src/hooks/queries/usePopulatedDisputeData.ts`:
- Around line 33-38: The current isEnabled guard only excludes undefined but not
null, yet later code force-unwraps disputeData.dispute.templateId causing a
runtime throw if templateId === null; update the guard for isEnabled to use a
nullish check (e.g., disputeData?.dispute?.templateId != null or !== null && !==
undefined) so templateId is guaranteed non-null before any non-null assertion,
and remove any unnecessary non-null assertions where templateId is used (or use
a safe access/early return) in the query invocation that currently force-unwraps
templateId.

In `@web/src/pages/Cases/CaseDetails/MaintenanceButtons/ExecuteRuling.tsx`:
- Around line 45-50: The isDisabled memo currently omits publicClient so the
button can be clickable while publicClient is missing; update the isDisabled
condition to include !publicClient (e.g. () => isUndefined(id) || isError ||
isLoading || !publicClient || period !== Period.Execution || ruled) and add
publicClient to the dependency array so the memo updates when publicClient
becomes available; this aligns isDisabled with the early return in handleClick.

---

Nitpick comments:
In `@web/src/hooks/queries/useCourtTree.ts`:
- Line 61: The recursive cast "child as Court" in the rootCourtToItems call can
hide mismatches between nested nodes and the Court type; replace the assertion
with a proper type guard or a specific child type: implement an isCourt(value):
value is Court guard and use court.children.filter(isCourt).map(child =>
rootCourtToItems(child, value)) (or adjust the GraphQL generated type to a
recursive CourtNode used by rootCourtToItems) so TypeScript can verify nested
shapes instead of relying on a blind cast; update references to
rootCourtToItems, court.children, and the Court type accordingly.

In `@web/src/hooks/useHomePageContext.tsx`:
- Around line 22-23: The Context.Provider value is recreated on every render
because the object literal { data, error } is not memoized; update
useHomePageContext (the component using useHomePageQuery) to memoize the
provider value with useMemo so it only changes when data or error change and
pass that memoized value to Context.Provider to avoid unnecessary consumer
re-renders.

In `@web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx`:
- Around line 7-8: Remove the unnecessary ClassicCommitParams type assertion
used in this file: drop the explicit cast to ClassicCommitParams wherever it's
applied (the import can remain) — locate usages in Commit.tsx (including the
occurrence around line 53) and let TypeScript infer the type instead of using
the assertion; after removing the casts, run the web workspace typecheck to
ensure no errors remain.

In `@web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx`:
- Around line 7-8: In the Vote component, remove the explicit cast to
ClassicVoteParams when passing the payload into the useVote mutation: stop
asserting the object as ClassicVoteParams and let TypeScript infer the union
VoteParams instead; locate the callsite where you construct the vote payload
(the variable/inline object passed to useVote) and delete the "as
ClassicVoteParams" cast, or change the payload's declared type to VoteParams if
an explicit annotation is required, ensuring the useVote(...) call accepts the
inferred union without suppressing type checking.

In `@web/src/pages/Profile/JurorCard/StakingRewards.tsx`:
- Around line 6-8: Delete the inactive commented-out import and component code
block that includes the commented imports "Box as _Box, Button" and
"EnsureChain" and any other commented implementation lines in the same file (the
commented block around those imports and the larger commented region spanning
the earlier file area), leaving only live code; rely on git history if recovery
is needed and ensure no other commented implementation remnants remain between
the same surrounding identifiers (e.g., references to _Box, Button, EnsureChain)
so static-analysis and maintenance noise are removed.

In `@web/src/pages/Resolver/Preview/index.tsx`:
- Around line 83-85: The cast on disputeTemplate (used in <DisputeContext
disputeDetails={disputeTemplate as DisputeDetails} />) hides a type mismatch
between IDisputeTemplate and DisputeDetails; remove the assertion by
consolidating types: either define a shared base interface (e.g., BaseDispute)
that contains the common fields and have both IDisputeTemplate and
DisputeDetails extend it, or widen the disputeTemplate/type of
DisputeContext.disputeDetails to accept the template type (or make
IDisputeTemplate extend DisputeDetails if appropriate). Update the relevant type
declarations (IDisputeTemplate, DisputeDetails, and the DisputeContext prop
type) so the cast is unnecessary and the compiler verifies compatibility.

In `@web/src/tests/e2e/utils/contracts.ts`:
- Around line 18-22: The code repeatedly casts chainId when indexing each
address map even though chainId is already declared as DEFAULT_CHAIN.id as keyof
typeof disputeResolverAddress; remove the redundant per-access assertions and
use the single typed variable (chainId) for all lookups: update usages of
disputeResolverAddress[chainId], klerosCoreAddress[chainId],
sortitionModuleAddress[chainId], and pnkAddress[chainId] to rely on the initial
cast (or if you prefer defensive typing, change the initial cast to a common
union type shared by all maps) so the extra "as keyof typeof ..." on
KLEROS_CORE_ADDRESS, SORTITION_MODULE_ADDRESS, and PNK_ADDRESS is eliminated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8f3abb3-08cb-460e-8411-9db9ea314ab4

📥 Commits

Reviewing files that changed from the base of the PR and between 709866a and 7f52cfa.

📒 Files selected for processing (160)
  • kleros-sdk/src/types/reality-eth-lib.d.ts
  • web/eslint.config.mjs
  • web/global.d.ts
  • web/package.json
  • web/src/actions/commit/builders/argentinaConsumerProtection.builder.ts
  • web/src/actions/commit/builders/classic.builder.ts
  • web/src/actions/commit/builders/classicUniversity.builder.ts
  • web/src/actions/commit/builders/gated.builder.ts
  • web/src/actions/commit/builders/gatedShutter.builder.ts
  • web/src/actions/commit/builders/shutter.builder.ts
  • web/src/actions/fundAppeal/builders/argentinaConsumerProtection.builder.ts
  • web/src/actions/fundAppeal/builders/classic.builder.ts
  • web/src/actions/fundAppeal/builders/classicUniversity.builder.ts
  • web/src/actions/fundAppeal/builders/gated.builder.ts
  • web/src/actions/fundAppeal/builders/gatedShutter.builder.ts
  • web/src/actions/fundAppeal/builders/shutter.builder.ts
  • web/src/actions/reveal/builders/argentinaConsumerProtection.builder.ts
  • web/src/actions/reveal/builders/classic.builder.ts
  • web/src/actions/reveal/builders/classicUniversity.builder.ts
  • web/src/actions/reveal/builders/gated.builder.ts
  • web/src/actions/reveal/builders/gatedShutter.builder.ts
  • web/src/actions/reveal/builders/shutter.builder.ts
  • web/src/actions/reveal/helpers/bruteForceChoice.test.ts
  • web/src/actions/vote/builders/argentinaConsumerProtection.builder.ts
  • web/src/actions/vote/builders/classic.builder.ts
  • web/src/actions/vote/builders/classicUniversity.builder.ts
  • web/src/actions/vote/builders/gated.builder.ts
  • web/src/actions/vote/builders/gatedShutter.builder.ts
  • web/src/actions/vote/builders/shutter.builder.ts
  • web/src/components/ClaimPnkButton.tsx
  • web/src/components/ConnectWallet/index.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc1155.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc20.tsx
  • web/src/components/DisputePreview/Alias.tsx
  • web/src/components/DisputePreview/DisputeContext.tsx
  • web/src/components/DisputeView/CardLabels/index.tsx
  • web/src/components/DisputeView/DisputeCardView.tsx
  • web/src/components/DisputeView/DisputeInfo/DisputeInfoCard.tsx
  • web/src/components/DisputeView/DisputeInfo/DisputeInfoList.tsx
  • web/src/components/DisputeView/DisputeListView.tsx
  • web/src/components/EnsureAuth.tsx
  • web/src/components/GradientTokenIcons.tsx
  • web/src/components/JurorLink.tsx
  • web/src/components/MarkdownRenderer.tsx
  • web/src/components/Popup/Description/SwapSuccess.tsx
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/components/Popup/MiniGuides/PageContentsTemplate.tsx
  • web/src/components/Popup/index.tsx
  • web/src/components/ScrollTop.tsx
  • web/src/components/TxnHash.tsx
  • web/src/components/Verdict/DisputeTimeline.tsx
  • web/src/components/Verdict/FinalDecision.tsx
  • web/src/components/Verdict/index.tsx
  • web/src/config/rpc.ts
  • web/src/config/wagmi.ts
  • web/src/consts/index.ts
  • web/src/context/GraphqlBatcher.tsx
  • web/src/context/NewDisputeContext.tsx
  • web/src/context/OverlayScrollContext.tsx
  • web/src/context/QueryClientProvider.tsx
  • web/src/hooks/queries/useAllUserDraws.ts
  • web/src/hooks/queries/useAppealCost.ts
  • web/src/hooks/queries/useCourtTree.ts
  • web/src/hooks/queries/useDisputeKitClassicMultipliers.ts
  • web/src/hooks/queries/useHomePageBlockQuery.ts
  • web/src/hooks/queries/usePolicyRegistryEvent.ts
  • web/src/hooks/queries/usePopulatedDisputeData.ts
  • web/src/hooks/queries/useTopStakedJurorsByCourt.ts
  • web/src/hooks/useCoinPrice.tsx
  • web/src/hooks/useHomePageContext.tsx
  • web/src/hooks/useLocalStorage.ts
  • web/src/hooks/useNavigateAndScrollTop.ts
  • web/src/hooks/usePNKData.tsx
  • web/src/hooks/useScrollTop.ts
  • web/src/hooks/useSessionStorage.ts
  • web/src/hooks/useStakingEventsByCourt.ts
  • web/src/layout/Footer/index.tsx
  • web/src/layout/Header/navbar/Explore.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/EmailVerificationInfo.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/index.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
  • web/src/layout/Header/navbar/Menu/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx
  • web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx
  • web/src/pages/Cases/CaseDetails/MaintenanceButtons/DistributeRewards.tsx
  • web/src/pages/Cases/CaseDetails/MaintenanceButtons/ExecuteRuling.tsx
  • web/src/pages/Cases/CaseDetails/MaintenanceButtons/WithdrawAppealFees.tsx
  • web/src/pages/Cases/CaseDetails/Tabs.tsx
  • web/src/pages/Cases/CaseDetails/Timeline.tsx
  • web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx
  • web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx
  • web/src/pages/Cases/CaseDetails/Voting/Shutter/Reveal.tsx
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/AccordionTitle.tsx
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/index.tsx
  • web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
  • web/src/pages/Cases/CaseDetails/Voting/index.tsx
  • web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/StakeEventCard.tsx
  • web/src/pages/Courts/CourtDetails/StakingHistoryByCourt/DisplayStakes/index.tsx
  • web/src/pages/Courts/CourtDetails/TopSearch.tsx
  • web/src/pages/Courts/CourtDetails/index.tsx
  • web/src/pages/GetPnk/Widget.tsx
  • web/src/pages/Home/CourtOverview/BarChart.tsx
  • web/src/pages/Home/CourtOverview/Chart.tsx
  • web/src/pages/Home/CourtOverview/ExtraStats.tsx
  • web/src/pages/Home/CourtOverview/Stats.tsx
  • web/src/pages/Home/CourtOverview/TimeSeriesChart.tsx
  • web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx
  • web/src/pages/Home/TopJurors/Header/Rewards.tsx
  • web/src/pages/Home/TopJurors/JurorCard/DesktopCard.tsx
  • web/src/pages/Home/TopJurors/JurorCard/MobileCard.tsx
  • web/src/pages/Home/TopJurors/JurorCard/Rewards.tsx
  • web/src/pages/Home/TopJurors/JurorCard/index.tsx
  • web/src/pages/Home/TopJurors/index.tsx
  • web/src/pages/Jurors/DisplayJurors.tsx
  • web/src/pages/Jurors/index.tsx
  • web/src/pages/Profile/Cases/index.tsx
  • web/src/pages/Profile/JurorCard/BottomContent/JurorRewards.tsx
  • web/src/pages/Profile/JurorCard/BottomContent/index.tsx
  • web/src/pages/Profile/JurorCard/Header.tsx
  • web/src/pages/Profile/JurorCard/StakingRewards.tsx
  • web/src/pages/Profile/JurorCard/TopContent/index.tsx
  • web/src/pages/Profile/JurorCard/index.tsx
  • web/src/pages/Profile/Stakes/CourtCard/Stake.tsx
  • web/src/pages/Profile/Stakes/CourtCard/index.tsx
  • web/src/pages/Profile/Stakes/CurrentStakes/Header.tsx
  • web/src/pages/Profile/Stakes/CurrentStakes/index.tsx
  • web/src/pages/Profile/Stakes/StakingHistory.tsx
  • web/src/pages/Profile/Votes/VoteCard/CaseStatus.tsx
  • web/src/pages/Profile/Votes/VoteCard/index.tsx
  • web/src/pages/Profile/Votes/index.tsx
  • web/src/pages/Profile/index.tsx
  • web/src/pages/Resolver/Landing/index.tsx
  • web/src/pages/Resolver/NavigationButtons/SubmitBatchDisputesButton.tsx
  • web/src/pages/Resolver/NavigationButtons/SubmitDisputeButton.tsx
  • web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx
  • web/src/pages/Resolver/Parameters/Jurors.tsx
  • web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx
  • web/src/pages/Resolver/Policy/index.tsx
  • web/src/pages/Resolver/Preview/BatchCreationCard.tsx
  • web/src/pages/Resolver/Preview/index.tsx
  • web/src/pages/Settings/EmailConfirmation/index.tsx
  • web/src/styles/responsiveSize.ts
  • web/src/tests/e2e/utils/contracts.ts
  • web/src/types/react-identicons.d.ts
  • web/src/types/reality-eth-lib.d.ts
  • web/src/utils/crypto/hashJustification.ts
  • web/src/utils/fetchStakingEventsByCourt.ts
  • web/src/utils/findCourtNameById.ts
  • web/src/utils/format.ts
  • web/src/utils/getDisputeRequestParamsFromTxn.ts
  • web/src/utils/getDrawnJurorsWithCount.ts
  • web/src/utils/getGraphqlUrl.ts
  • web/src/utils/index.ts
  • web/src/utils/jurorRewardConfig.ts
  • web/src/utils/retrieveDisputeId.ts
  • web/tsconfig.json
💤 Files with no reviewable changes (3)
  • web/src/components/Popup/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
✅ Files skipped from review due to trivial changes (51)
  • web/global.d.ts
  • web/eslint.config.mjs
  • web/src/actions/commit/builders/gated.builder.ts
  • web/src/components/Popup/MiniGuides/JurorLevels.tsx
  • web/src/components/Verdict/FinalDecision.tsx
  • web/src/components/MarkdownRenderer.tsx
  • kleros-sdk/src/types/reality-eth-lib.d.ts
  • web/src/actions/vote/builders/shutter.builder.ts
  • web/src/actions/fundAppeal/builders/gated.builder.ts
  • web/src/components/ScrollTop.tsx
  • web/src/pages/Profile/JurorCard/TopContent/index.tsx
  • web/src/actions/fundAppeal/builders/argentinaConsumerProtection.builder.ts
  • web/src/pages/Profile/JurorCard/Header.tsx
  • web/src/pages/Home/CourtOverview/BarChart.tsx
  • web/src/components/Verdict/DisputeTimeline.tsx
  • web/src/pages/Profile/Stakes/CourtCard/Stake.tsx
  • web/src/context/GraphqlBatcher.tsx
  • web/src/actions/reveal/builders/shutter.builder.ts
  • web/src/actions/reveal/builders/gatedShutter.builder.ts
  • web/src/actions/reveal/builders/gated.builder.ts
  • web/src/actions/fundAppeal/builders/shutter.builder.ts
  • web/src/types/react-identicons.d.ts
  • web/package.json
  • web/src/utils/format.ts
  • web/src/actions/fundAppeal/builders/gatedShutter.builder.ts
  • web/src/hooks/useLocalStorage.ts
  • web/src/actions/reveal/helpers/bruteForceChoice.test.ts
  • web/src/pages/Home/TopJurors/Header/Rewards.tsx
  • web/src/types/reality-eth-lib.d.ts
  • web/src/pages/Profile/JurorCard/BottomContent/JurorRewards.tsx
  • web/src/actions/vote/builders/gatedShutter.builder.ts
  • web/src/utils/jurorRewardConfig.ts
  • web/src/hooks/useStakingEventsByCourt.ts
  • web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx
  • web/src/pages/Resolver/Parameters/Court/FeatureSelection/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx
  • web/src/components/DisputeFeatures/Features/GatedErc1155.tsx
  • web/src/pages/Cases/CaseDetails/Voting/index.tsx
  • web/src/actions/reveal/builders/argentinaConsumerProtection.builder.ts
  • web/src/styles/responsiveSize.ts
  • web/src/pages/Cases/CaseDetails/Voting/VotesDetails/index.tsx
  • web/src/layout/Header/navbar/Explore.tsx
  • web/src/pages/Settings/EmailConfirmation/index.tsx
  • web/src/components/EnsureAuth.tsx
  • web/src/layout/Footer/index.tsx
  • web/src/pages/Home/TopJurors/Header/DesktopHeader.tsx
  • web/src/hooks/useCoinPrice.tsx
  • web/src/context/QueryClientProvider.tsx
  • web/src/pages/Resolver/Parameters/NotablePersons/PersonFields.tsx
  • web/src/hooks/useSessionStorage.ts
  • web/src/components/GradientTokenIcons.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • web/src/pages/Resolver/Policy/index.tsx
  • web/src/components/DisputePreview/DisputeContext.tsx
  • web/src/hooks/queries/useHomePageBlockQuery.ts
  • web/src/utils/crypto/hashJustification.ts
  • web/src/pages/Home/CourtOverview/Chart.tsx
  • web/tsconfig.json
  • web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/index.tsx
  • web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx
  • web/src/components/Popup/Description/SwapSuccess.tsx

Comment thread web/src/actions/commit/builders/classic.builder.ts
Comment thread web/src/actions/commit/builders/gatedShutter.builder.ts
Comment thread web/src/actions/fundAppeal/builders/classic.builder.ts
Comment thread web/src/actions/fundAppeal/builders/classicUniversity.builder.ts
Comment thread web/src/actions/reveal/builders/classic.builder.ts
Comment thread web/src/hooks/queries/usePolicyRegistryEvent.ts
Comment thread web/src/pages/Resolver/Parameters/Jurors.tsx
Comment thread web/src/utils/getDrawnJurorsWithCount.ts
Comment thread web/src/utils/getGraphqlUrl.ts
@kyrers kyrers marked this pull request as ready for review May 15, 2026 12:07
@kyrers kyrers requested a review from a team as a code owner May 15, 2026 12:07
@kyrers kyrers force-pushed the feat/pre-commit-type-check-web branch from b7879e1 to b431026 Compare May 15, 2026 16:16
Copy link
Copy Markdown
Contributor

@tractorss tractorss left a comment

Choose a reason for hiding this comment

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

looks great, left a couple comments 🙌

Comment thread web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx Outdated
Comment thread web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx Outdated
Comment thread web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx Outdated
Comment thread web/src/pages/Cases/CaseDetails/Voting/VotesDetails/index.tsx
Comment thread web/src/pages/Cases/CaseDetails/Tabs.tsx Outdated
Comment thread web/src/pages/Profile/Votes/VoteCard/CaseStatus.tsx Outdated
Comment thread web/src/pages/Resolver/Landing/index.tsx Outdated
Comment thread web/src/pages/Resolver/Preview/index.tsx
Comment thread web/src/utils/getDrawnJurorsWithCount.ts
Comment thread web/src/hooks/useHomePageContext.tsx Outdated
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@kyrers
Copy link
Copy Markdown
Contributor Author

kyrers commented May 21, 2026

@coderabbitai, also create an issue for this. It was added as a shim to prevent type errors. However, soon we will be migrating the UI to Tailwind, at which point we should also replace this library. Create an issue for replacing this library, adding both why and when it should happen.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@kyrers, I've created a GitHub issue to track replacing the react-identicons library. The issue covers:

  • Why: The library lacks TypeScript types, requiring a temporary shim to satisfy the stricter noImplicitAny configuration introduced in this PR.
  • When: During the upcoming Tailwind CSS UI migration, at which point the consuming component(s) should be refactored to use a well-typed alternative and the shim removed.

I'll share the issue link once the script executes.


🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo kleros/kleros-v2 \
  --title "Replace react-identicons library during Tailwind UI migration" \
  --assignee "kyrers" \
  --body "## Summary

The \`react-identicons\` library is currently used in the \`web\` workspace to render identicon avatars. It does not ship its own TypeScript type definitions, which required adding a manual declaration shim (\`web/src/types/react-identicons.d.ts\`) to prevent \`noImplicitAny\` errors introduced by the stricter TypeScript configuration.

## Why Replace It

- The library has no TypeScript support, necessitating a shim that should be temporary.
- The shim was added as part of `#2279` (feat: pre-commit type checking and lint - web) solely to unblock stricter TS checks — it is not a long-term solution.
- Maintaining a shim adds ongoing maintenance overhead and obscures type-safety guarantees.

## When to Replace It

This library should be replaced during (or alongside) the upcoming migration of the \`web\` workspace UI to **Tailwind CSS**. At that point, the component(s) consuming \`react-identicons\` should be refactored to use a well-typed alternative (or a custom implementation), and the shim at \`web/src/types/react-identicons.d.ts\` should be removed.

## References

- PR introducing the shim: https://github.com/kleros/kleros-v2/pull/2279
- Shim file: \`web/src/types/react-identicons.d.ts\`
- Requested by: `@kyrers`"

Length of output: 48


@kyrers, the issue has been created: #2320 — Replace react-identicons library during Tailwind UI migration.

It captures:

  • Why: The library ships no TypeScript types, requiring a shim (web/src/types/react-identicons.d.ts) to satisfy the stricter noImplicitAny config — a workaround, not a permanent solution.
  • When: During the upcoming Tailwind CSS UI migration, when the consuming components should be refactored to a well-typed alternative and the shim removed.

@kyrers kyrers merged commit cb99623 into feat/pre-commit-type-check May 22, 2026
3 of 4 checks passed
@kyrers kyrers deleted the feat/pre-commit-type-check-web branch May 22, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants