Skip to content

Feature/UI revamp and referral system#440

Open
madhurMongia wants to merge 29 commits into
testnetsfrom
feature/ui-revamp-and-referral-system
Open

Feature/UI revamp and referral system#440
madhurMongia wants to merge 29 commits into
testnetsfrom
feature/ui-revamp-and-referral-system

Conversation

@madhurMongia

@madhurMongia madhurMongia commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added request search UI, status icons/badges, and enhanced timeline indicators.
    • Added improved inputs (currency field), upload zone, copy button, toggles, and loading/error fallbacks for images/videos.
    • Added subgraph health monitoring (including indexing delay) and cross-chain action trigger UI.
  • Style
    • Refreshed dark theme visuals across navigation, headers/footers, cards, buttons, tooltips, and layout spacing.
    • Improved accessibility for accordion, switch, and external-link behavior.
  • Bug Fixes
    • Corrected active navigation/register highlighting and improved IPFS-related asset handling (SVG imports, gateway validation).
    • Updated manifest/app icon path and transaction link handling.

Tailwind palette aligned to the design-system swatches, globals.css
utility layer (btn-primary, status pills, cards, toasts) with a btn-main
compatibility alias until all pages migrate, dark theme enabled globally,
AppKit dark theme, eslint/next config.
Icon set and social/brand logos from the POH NEW Figma file, normalized
to currentColor; Gift replaced with the design-system filled variant.
Use settleChainQueries so one dead chain does not blank header/me
identity, while keeping lowercased account ids for subgraph matching.
Use the social/notification badge assets in Footer and Header chrome,
point Circles dark logo at gnosis-app.svg with its native 185×48 size,
and make ExternalLink/CheckCircle scale via currentColor and className.
Bring header navigation, wallet section, options, notifications control,
and footer social badges from the chrome layer so the foundation PR owns
the full shell UI and SVG wiring.
Timeline renders every milestone's design glyph (flag-checkered, eye,
challenge, vouch, check/close-circle, hourglass, transfer) tinted with
the status palette. New StatusIcon implements the Figma States set and
feeds the request card chip, the grid status filter and the action-bar
tag (now the dark-pill Tag style). Header uses the poh lockup, footer
the built-by-kleros lockup; kleros + poh-ID marks refreshed from Figma.
Render the new poh.svg lockup at its native 185×48 size and replace the
legacy ethereum-era favicon with the human mark (poh-colored) via App
Router icon assets.
Dark base text color instead of text-black (prevents unreadable flash),
drop the duplicate .divider and redundant dark-mode button override, align
explorerTxLink with explorerLink on SupportedChain, and extract
isRegisterActive into utils/identifier for both navigations.
…ainer

Header/footer inners and page content now share the same max-width and
responsive gutters (kleros-v2 court / scout layout system), so all edges
align at every viewport and content stays centered beyond 1400px.
Removes the per-page viewport-width hacks in favor of .content.
Restyles the shared primitives — buttons, inputs, checkbox/radio/switch,
dropdowns, generic modal, token badges, and the new CurrencyField atom —
with spec hover/pressed states and icon-button treatments, and adopts
them at all non-referral call sites.
Use request-grid breakpoints and wide (5:4) cards so home cells match
feat/ui-revamp-referral-ui layout.
Default ExternalLink to primary/orange text colors and style the help
menu so unstyled anchors no longer use the browser blue link color.
Override @tailwindcss/forms and native accent/focus so checkboxes and
radios use the design palette (peach rest, orange hover/focus).
Grid cards intermittently showed the photo-unavailable fallback when the
IPFS gateway rate-limited the initial request burst. Replace SWR with
react-query (retry 3x, exponential backoff capped at 4s plus jitter) so
cards recover instead of sticking until a refresh. Suspense callers use
the new useSuspenseIPFS since react-query v5 splits suspense into its
own hook.
Production builds failed on Social* SVG imports with "no loaders configured"
because Next's default asset rule still claimed .svg. Follow the SVGR Next
recipe: exclude SVG from the asset pipeline and route component imports
through @svgr/webpack (keep *.svg?url on the asset path).
…styles

- Introduced MediaFallback component for consistent loading and error states across images and videos.
- Removed unused success and warning styles from globals.css.
- Simplified ProfileTimelineHeader and Request/Card components by utilizing getDisplayName utility for name display logic.
- Cleaned up integration components by removing dark mode logic and optimizing logo source selection.
- Updated various components to use MediaFallback for better error handling and loading states.
…logic

- Updated alt text for social media icons in the Footer component for better accessibility.
- Refactored Header component to utilize isRegisterActive function for conditional styling based on registration status.
- Simplified toggle logic in Review component for the "Submit for free" feature.
- Improved aria-labels in SearchBar and Switch components for enhanced screen reader support.
- Adjusted error handling in SignInButton and Previewed components for better user experience.
- Optimized key assignment in RequestsGrid to ensure unique keys for mapped requests.
…rove component structure

- Removed unnecessary props from StatusBadge component, consolidating status color and label logic within the component itself.
- Updated Card component to utilize the simplified StatusBadge, enhancing readability and maintainability.
- Cleaned up imports in both components for better organization.
@netlify

netlify Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploy Preview for proof-of-humanity-v2 ready!

Name Link
🔨 Latest commit 4774e76
🔍 Latest deploy log https://app.netlify.com/projects/proof-of-humanity-v2/deploys/6a57c6415ca60500086f5516
😎 Deploy Preview https://deploy-preview-440--proof-of-humanity-v2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR updates configuration, theming, shared UI components, wallet and transaction workflows, IPFS fetching, media handling, cross-chain views, navigation, and profile/request layouts. It also adds reusable controls and standardizes disabled-state and tooltip handling.

Changes

Application foundation

Layer / File(s) Summary
Configuration, data fetching, and theme foundation
.eslintrc.js, next.config.js, package.json, src/hooks/useIPFS.ts, tailwind.config.cjs, src/app/globals.css
Tightens lint rules, validates the IPFS gateway, updates SVG handling and dependencies, migrates IPFS hooks to React Query, and expands theme/design tokens.
Shared controls and media components
src/components/*
Adds currency fields, upload zones, switches, search/status components, media fallbacks, and loading-aware image/video components while updating fields, links, dropdowns, modals, and tooltips.

Application interface

Layer / File(s) Summary
Header and navigation
src/app/Header/*, src/app/Footer.tsx, src/app/layout.tsx
Reworks the header/footer structure, register navigation state, wallet/settings controls, notification UI, and fixed dark theme configuration.
Request cards and status displays
src/components/Request/*, src/app/[pohid]/[chain]/[request]/Timeline.tsx
Adds reusable status icons/badges, updates request-grid behavior, introduces media loading/parallax handling, and maps timeline kinds to icons.
Request, claim, and revoke workflows
src/app/[pohid]/[chain]/[request]/*, src/app/[pohid]/claim/*, src/app/[pohid]/Revoke*.tsx
Updates evidence upload, funding, appeal, challenge, claim, and revoke controls with shared balance checks, transaction state resolution, and new modal flows.
Cross-chain flows and page surfaces
src/app/[pohid]/cross-chain/*, src/app/*
Updates relay, transfer, update-state, profile, error, loading, and responsive page layouts with new shared presentation components.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: kemuru

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main UI revamp in the diff and is concise, though the referral-system part isn’t evident.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/ui-revamp-and-referral-system

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (9)
src/components/Dropdown/Menu.tsx (1)

13-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Trigger button missing aria-expanded/aria-haspopup.

open is tracked locally but never exposed to assistive tech via ARIA attributes on the trigger button.

Proposed fix
-        <button className="bg-whiteBackground border-stroke text-primaryText flex min-h-12 w-full items-center justify-between rounded-btn border px-4 transition duration-200 ease-premium hover:bg-grey sm:w-64">
+        <button
+          aria-haspopup="listbox"
+          aria-expanded={open}
+          className="bg-whiteBackground border-stroke text-primaryText flex min-h-12 w-full items-center justify-between rounded-btn border px-4 transition duration-200 ease-premium hover:bg-grey sm:w-64"
+        >
🤖 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 `@src/components/Dropdown/Menu.tsx` around lines 13 - 30, Update the trigger
button in the Dropdown component to expose its menu state and relationship to
assistive technology by adding aria-expanded based on open and
aria-haspopup="menu".
src/components/Request/Card.tsx (1)

207-217: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Info tooltip is hover-only, unreachable by keyboard.

The tooltip trigger div has no tabIndex/focus state, so keyboard users can't reveal tooltip content that mouse users see on hover.

Proposed fix
-        <div className="group/info relative flex h-6 w-6 items-center justify-center rounded-full border border-white/[0.08] bg-[`#2F333D`]/85 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] backdrop-blur-sm">
+        <div
+          tabIndex={tooltip ? 0 : undefined}
+          className="group/info relative flex h-6 w-6 items-center justify-center rounded-full border border-white/[0.08] bg-[`#2F333D`]/85 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] backdrop-blur-sm focus-visible:outline-none"
+        >
           <InfoIcon className="h-4 w-4 stroke-current stroke-2 text-white drop-shadow-md" />
           {tooltip && (
-            <span className="tooltip-surface pointer-events-none absolute bottom-full left-1/2 z-50 mb-2 w-max max-w-[200px] -translate-x-1/2 whitespace-normal text-center text-sm opacity-0 transition-opacity group-hover/info:opacity-100">
+            <span className="tooltip-surface pointer-events-none absolute bottom-full left-1/2 z-50 mb-2 w-max max-w-[200px] -translate-x-1/2 whitespace-normal text-center text-sm opacity-0 transition-opacity group-hover/info:opacity-100 group-focus-within/info:opacity-100">
               {tooltip}
             </span>
           )}
🤖 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 `@src/components/Request/Card.tsx` around lines 207 - 217, Update the tooltip
trigger in the request card header, identified by the `group/info` wrapper
around `InfoIcon`, to be keyboard focusable and reveal the tooltip on focus as
well as hover. Add the appropriate focus-visible styling/state while preserving
the existing hover behavior and tooltip content.
src/app/Header/Options.tsx (1)

31-50: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Extract the static array outside the component.

Defining the HELP_LINKS array inline within the component means it is recreated on every render. Extracting it as a static constant outside the component avoids unnecessary memory allocations and keeps the render function clean.

♻️ Proposed refactor
+const HELP_LINKS = [
+  ["https://t.me/proofhumanity", "Get Help (English)"],
+  ["https://t.me/proofofhumanityenespanol", "Get Help (Spanish)"],
+  ["https://gov.proofofhumanity.id/", "Forums"],
+  ["https://t.me/pohDebug", "Report Bugs (Telegram)"],
+  [
+    "https://github.com/Proof-Of-Humanity/proof-of-humanity-web/issues",
+    "Report Bugs (Github)",
+  ],
+  [
+    "https://kleros.gitbook.io/docs/products/proof-of-humanity/proof-of-humanity-tutorial",
+    "Tutorial",
+  ],
+  ["https://ethereum.org/en/wallets", "Crypto Beginner's Guide"],
+  [
+    "https://kleros.gitbook.io/docs/products/proof-of-humanity/poh-faq",
+    "FAQ",
+  ],
+] as const;
+
 const Options: React.FC = () => (
   <div className="mt-[16px] flex flex-row items-center md:mt-0">
     <SettingsPopover />
     <ExternalLink
       href="https://snapshot.org/#/poh.eth/"
       className="icon-btn ml-2 h-9 w-9"
       aria-label="Snapshot"
     >
       <SocialSnapshotIcon />
     </ExternalLink>
 
     <Popover
       trigger={
         <button
           type="button"
           className="icon-btn ml-2 h-9 w-9"
           aria-label="Open help links"
         >
           <SocialHelpIcon />
         </button>
       }
     >
       <div className="grid h-fit grid-cols-1 gap-1 p-2">
-        {(
-          [
-            ["https://t.me/proofhumanity", "Get Help (English)"],
-            ["https://t.me/proofofhumanityenespanol", "Get Help (Spanish)"],
-            ["https://gov.proofofhumanity.id/", "Forums"],
-            ["https://t.me/pohDebug", "Report Bugs (Telegram)"],
-            [
-              "https://github.com/Proof-Of-Humanity/proof-of-humanity-web/issues",
-              "Report Bugs (Github)",
-            ],
-            [
-              "https://kleros.gitbook.io/docs/products/proof-of-humanity/proof-of-humanity-tutorial",
-              "Tutorial",
-            ],
-            ["https://ethereum.org/en/wallets", "Crypto Beginner's Guide"],
-            [
-              "https://kleros.gitbook.io/docs/products/proof-of-humanity/poh-faq",
-              "FAQ",
-            ],
-          ] as const
-        ).map(([href, label]) => (
+        {HELP_LINKS.map(([href, label]) => (
           <ExternalLink
             key={href}
             href={href}
🤖 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 `@src/app/Header/Options.tsx` around lines 31 - 50, Move the inline help-links
array from the Options component into a module-level HELP_LINKS constant,
preserving all existing URLs, labels, and ordering; update the component to
reuse this constant during rendering.
src/app/Header/index.tsx (1)

73-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate pill-CTA class string between Rewards and RegisterLink.

Both branches repeat the same long hover:border-orange rounded-full border ... string, differing only in the active-state ternary. Consider extracting a small helper (similar to navLink in DesktopNavigation.tsx) to avoid divergence.

♻️ Suggested refactor
+const mobileCtaLink = (active: boolean) =>
+  `hover:border-orange rounded-full border border-white/[0.08] bg-[`#2F333D`] px-4 py-2 text-sm font-semibold text-white transition ${
+    active ? "text-orange" : ""
+  }`;
+
 ...
-            className={`hover:border-orange rounded-full border border-white/[0.08] bg-[`#2F333D`] px-4 py-2 text-sm font-semibold text-white transition ${
-              pathname.startsWith("/app") ? "text-orange" : ""
-            }`}
+            className={mobileCtaLink(pathname.startsWith("/app"))}
 ...
-              className={`hover:border-orange rounded-full border border-white/[0.08] bg-[`#2F333D`] px-4 py-2 text-sm font-semibold text-white transition ${
-                isRegisterActive(me?.pohId, pathname) ? "text-orange" : ""
-              }`}
+              className={mobileCtaLink(isRegisterActive(me?.pohId, pathname))}
🤖 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 `@src/app/Header/index.tsx` around lines 73 - 92, Extract the shared pill CTA
class string used by the Rewards Link and RegisterLink branches into a small
helper, following the existing navLink pattern. Have the helper accept the
active-state condition or class and preserve each branch’s current active
styling while removing the duplicated base classes.
src/app/Header/WalletSection.tsx (1)

21-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hardcoded #2F333D duplicates the newly added grey design token. tailwind.config.cjs now defines dark.grey: "#2F333D", but both files still hardcode the raw hex instead of referencing that token, risking future drift.

  • src/app/Header/WalletSection.tsx#L21-L41: replace bg-[#2F333D] on both the connected pill wrapper (L23) and the disconnected "Connect" button (L41) with the theme's grey token/utility.
  • src/app/Header/index.tsx#L76-L94: replace bg-[#2F333D] on the Rewards link (L76), RegisterLink className (L88), and hamburger button (L94) with the same token.
🤖 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 `@src/app/Header/WalletSection.tsx` around lines 21 - 41, Replace the hardcoded
bg-[`#2F333D`] classes with the Tailwind dark.grey utility in
src/app/Header/WalletSection.tsx lines 21-41 for both the connected pill wrapper
and Connect button, and in src/app/Header/index.tsx lines 76-94 for the Rewards
link, RegisterLink, and hamburger button. Use the existing theme token
consistently at every affected site.
src/components/Accordion.tsx (1)

26-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Always fire onToggle even in uncontrolled mode.

When the accordion is used as an uncontrolled component, consumers might still want to pass an onToggle callback to be notified of state changes. Currently, propsOnToggle is only invoked if the component is fully controlled (i.e., isOpen is provided).

♻️ Proposed refactor
   const handleToggle = () => {
-    if (isControlled && propsOnToggle) {
-      propsOnToggle();
-    } else {
-      setInternalOpen((o) => !o);
-    }
+    propsOnToggle?.();
+    if (!isControlled) {
+      setInternalOpen((o) => !o);
+    }
   };
🤖 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 `@src/components/Accordion.tsx` around lines 26 - 32, Update handleToggle so
propsOnToggle is invoked whenever it is provided, regardless of isControlled;
retain the internal state toggle for uncontrolled accordions and preserve
controlled behavior without changing state management.
src/components/Field.tsx (1)

45-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding aria-invalid for better accessibility.

When the field is in an error state, adding aria-invalid="true" helps screen readers announce the input as invalid.

🎨 Proposed refactor
         {textarea ? (
           <textarea
             className={cn(
               "text-primaryText block w-full border-none bg-transparent px-4 py-3 font-medium transition ease-in-out",
               "focus:ring-0",
               className,
             )}
+            aria-invalid={status === "error"}
             {...(props as TextareaHTMLAttributes<HTMLTextAreaElement>)}
           />
         ) : (
           <input
             className={cn(
               "text-primaryText block min-h-12 w-full border-none bg-transparent px-4 py-3 font-medium",
               "focus:ring-0 focus-visible:outline-none",
               className,
             )}
+            aria-invalid={status === "error"}
             {...(props as InputHTMLAttributes<HTMLInputElement>)}
           />
         )}
🤖 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 `@src/components/Field.tsx` around lines 45 - 63, Update the textarea and input
elements in the Field component to expose their error state through
aria-invalid, setting it to true when the field has an error and preserving the
valid-state behavior otherwise. Ensure the attribute is applied consistently in
both branches.
src/app/[pohid]/claim/Review.tsx (1)

334-348: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Enhance text label accessibility.

While the Switch component itself is keyboard accessible, the adjacent text acts as a clickable label but is implemented as a generic span with an onClick handler. This makes the text label inaccessible to keyboard users who might try to activate it directly.

Consider updating the Switch component to accept an id and replacing this span with a <label htmlFor="...">, or add a keyboard event handler and role="button" to the span.

🤖 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 `@src/app/`[pohid]/claim/Review.tsx around lines 334 - 348, Update the
submit-for-free control in Review to use an accessible label: give the Switch a
unique id and replace the clickable span with a label using matching htmlFor,
preserving the existing text, styling, and toggle behavior.
src/components/CurrencyField.tsx (1)

32-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Couple the Label and input for better accessibility.

To ensure the label is programmatically associated with the input for screen readers and allows users to click the label to focus the input, consider passing an id prop and linking it via the htmlFor attribute on the Label (if the Label component supports it).

♻️ Proposed refactor
 function CurrencyField({
   label,
   symbol,
   onTokenClick,
   className,
+  id,
   ...props
 }: CurrencyFieldProps) {
   return (
     <div className="flex w-full flex-col">
-      {label && <Label>{label}</Label>}
+      {label && <Label htmlFor={id}>{label}</Label>}
       <div className="flat-control flex min-h-12 w-full items-center gap-2 rounded-input px-4 transition duration-200 ease-premium">
         <input
+          id={id}
           type="number"
           className={cn(
🤖 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 `@src/components/CurrencyField.tsx` around lines 32 - 41, Update the
CurrencyField label/input markup so Label receives an htmlFor value matching the
input id, using the existing or generated field identifier while preserving any
caller-provided input props. Ensure the association remains valid when a label
is rendered.
🤖 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 `@next.config.js`:
- Line 102: Normalize the value used by the `remotePatterns.hostname`
configuration in `next.config.js`: derive a hostname from the documented
full-URL `REACT_APP_IPFS_GATEWAY` value by removing its scheme before assigning
it to `ipfsGatewayHostname`. Preserve support for the existing configuration
flow while ensuring the remote pattern receives only the host, not `https://` or
another protocol.

In `@package.json`:
- Line 57: Update tsconfig.json’s moduleResolution to an ESM-compatible resolver
such as bundler, node16, or nodenext so the GraphQLClient and RequestOptions
imports in generated/graphql.ts and config/subgraph.ts resolve with
graphql-request v7; alternatively, downgrade graphql-request to v6 while
preserving the existing resolver.

In `@src/app/globals.css`:
- Around line 348-354: Fix the declaration-empty-line-before violations in the
CSS rules .status-pill, .paper, .reward-surface, .pill-tag,
.poh-toast-container, and .poh-toast by inserting the required blank lines
before the flagged declarations, including after custom properties where
specified. Preserve all existing styles and values.
- Around line 41-199: Resolve the stylelint declaration-empty-line-before
violations by inserting a blank line between each `@apply` or custom-property
declaration and the first following plain declaration in .checkbox, .radio,
.btn-secondary, .btn-tertiary, .tooltip-surface, and .icon-btn within
src/app/globals.css lines 41-199; apply the same spacing before background in
.modal-surface at lines 211-216 and before the first plain declarations in
.status-pill, .paper, .reward-surface, .pill-tag, .poh-toast-container, and
.poh-toast at lines 348-457. No declaration values or rule behavior should
change.
- Around line 356-363: Update the CSS color keyword casing from currentColor to
currentcolor in .status-pill::before and the .poh-toast .Toastify__toast-icon
svg rule, including both background and box-shadow declarations, to satisfy
Stylelint value-keyword-case.

In `@src/components/Request/Card.tsx`:
- Around line 58-93: The ErrorFallback component is no longer used for IPFS
media-fetch errors because Content handles the useIPFS error state inline.
Remove the obsolete ErrorFallback UI, unless the intended behavior is to restore
hook-thrown errors; keep the existing inline Content error handling unchanged.

In `@src/components/SubgraphsStatus.tsx`:
- Line 148: Update the visibleStatuses computation in SubgraphsStatus to filter
statuses to only entries where status.ok is false. Preserve the empty-array
result when all chains are healthy, ensuring the banner never includes healthy
chains when any unhealthy chain exists.
- Around line 26-31: Update the subgraph fetch flow in SubgraphsStatus.tsx to
enforce an explicit timeout on each request, using AbortController or the
project’s existing timeout mechanism. Ensure a timed-out fetch rejects and is
handled by the existing polling/error path so probes cannot remain in flight or
overlap across the 60-second polling cycle.

---

Nitpick comments:
In `@src/app/`[pohid]/claim/Review.tsx:
- Around line 334-348: Update the submit-for-free control in Review to use an
accessible label: give the Switch a unique id and replace the clickable span
with a label using matching htmlFor, preserving the existing text, styling, and
toggle behavior.

In `@src/app/Header/index.tsx`:
- Around line 73-92: Extract the shared pill CTA class string used by the
Rewards Link and RegisterLink branches into a small helper, following the
existing navLink pattern. Have the helper accept the active-state condition or
class and preserve each branch’s current active styling while removing the
duplicated base classes.

In `@src/app/Header/Options.tsx`:
- Around line 31-50: Move the inline help-links array from the Options component
into a module-level HELP_LINKS constant, preserving all existing URLs, labels,
and ordering; update the component to reuse this constant during rendering.

In `@src/app/Header/WalletSection.tsx`:
- Around line 21-41: Replace the hardcoded bg-[`#2F333D`] classes with the
Tailwind dark.grey utility in src/app/Header/WalletSection.tsx lines 21-41 for
both the connected pill wrapper and Connect button, and in
src/app/Header/index.tsx lines 76-94 for the Rewards link, RegisterLink, and
hamburger button. Use the existing theme token consistently at every affected
site.

In `@src/components/Accordion.tsx`:
- Around line 26-32: Update handleToggle so propsOnToggle is invoked whenever it
is provided, regardless of isControlled; retain the internal state toggle for
uncontrolled accordions and preserve controlled behavior without changing state
management.

In `@src/components/CurrencyField.tsx`:
- Around line 32-41: Update the CurrencyField label/input markup so Label
receives an htmlFor value matching the input id, using the existing or generated
field identifier while preserving any caller-provided input props. Ensure the
association remains valid when a label is rendered.

In `@src/components/Dropdown/Menu.tsx`:
- Around line 13-30: Update the trigger button in the Dropdown component to
expose its menu state and relationship to assistive technology by adding
aria-expanded based on open and aria-haspopup="menu".

In `@src/components/Field.tsx`:
- Around line 45-63: Update the textarea and input elements in the Field
component to expose their error state through aria-invalid, setting it to true
when the field has an error and preserving the valid-state behavior otherwise.
Ensure the attribute is applied consistently in both branches.

In `@src/components/Request/Card.tsx`:
- Around line 207-217: Update the tooltip trigger in the request card header,
identified by the `group/info` wrapper around `InfoIcon`, to be keyboard
focusable and reveal the tooltip on focus as well as hover. Add the appropriate
focus-visible styling/state while preserving the existing hover behavior and
tooltip content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7517fb96-2fcd-46be-abb2-af2e92cf2edd

📥 Commits

Reviewing files that changed from the base of the PR and between 3b0192a and 287222b.

⛔ Files ignored due to path filters (49)
  • public/logo/built-by-kleros.svg is excluded by !**/*.svg
  • public/logo/gnosis-app.svg is excluded by !**/*.svg
  • public/logo/human-connector.svg is excluded by !**/*.svg
  • public/logo/kleros.svg is excluded by !**/*.svg
  • public/logo/notifications.svg is excluded by !**/*.svg
  • public/logo/poh.svg is excluded by !**/*.svg
  • public/logo/pohid.svg is excluded by !**/*.svg
  • public/logo/social-github.svg is excluded by !**/*.svg
  • public/logo/social-help.svg is excluded by !**/*.svg
  • public/logo/social-snapshot.svg is excluded by !**/*.svg
  • public/logo/social-telegram.svg is excluded by !**/*.svg
  • public/logo/social-x.svg is excluded by !**/*.svg
  • src/app/apple-icon.png is excluded by !**/*.png
  • src/app/favicon.ico is excluded by !**/*.ico
  • src/app/icon.png is excluded by !**/*.png
  • src/app/icon.svg is excluded by !**/*.svg
  • src/generated/graphql.ts is excluded by !**/generated/**
  • src/icons/CaretDown.svg is excluded by !**/*.svg
  • src/icons/Challenge.svg is excluded by !**/*.svg
  • src/icons/CheckCircle.svg is excluded by !**/*.svg
  • src/icons/CheckCircleOutline.svg is excluded by !**/*.svg
  • src/icons/ChevronDown.svg is excluded by !**/*.svg
  • src/icons/CloseCircleOutline.svg is excluded by !**/*.svg
  • src/icons/Copy.svg is excluded by !**/*.svg
  • src/icons/Download.svg is excluded by !**/*.svg
  • src/icons/EthToken.svg is excluded by !**/*.svg
  • src/icons/ExternalLink.svg is excluded by !**/*.svg
  • src/icons/Eye.svg is excluded by !**/*.svg
  • src/icons/FlagCheckered.svg is excluded by !**/*.svg
  • src/icons/Gift.svg is excluded by !**/*.svg
  • src/icons/GnosisToken.svg is excluded by !**/*.svg
  • src/icons/Hourglass.svg is excluded by !**/*.svg
  • src/icons/Loading.svg is excluded by !**/*.svg
  • src/icons/NeedsVouch.svg is excluded by !**/*.svg
  • src/icons/Notifications.svg is excluded by !**/*.svg
  • src/icons/Referral.svg is excluded by !**/*.svg
  • src/icons/SocialGitHub.svg is excluded by !**/*.svg
  • src/icons/SocialHelp.svg is excluded by !**/*.svg
  • src/icons/SocialSnapshot.svg is excluded by !**/*.svg
  • src/icons/SocialTelegram.svg is excluded by !**/*.svg
  • src/icons/SocialX.svg is excluded by !**/*.svg
  • src/icons/Stamp.svg is excluded by !**/*.svg
  • src/icons/TelegramLogo.svg is excluded by !**/*.svg
  • src/icons/Transfer.svg is excluded by !**/*.svg
  • src/icons/Vouch.svg is excluded by !**/*.svg
  • src/icons/WhatsAppLogo.svg is excluded by !**/*.svg
  • src/icons/XLogo.svg is excluded by !**/*.svg
  • src/icons/XdaiToken.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (74)
  • .eslintrc.js
  • next.config.js
  • package.json
  • public/manifest.json
  • src/app/Footer.tsx
  • src/app/Header/DesktopNavigation.tsx
  • src/app/Header/EmailField.tsx
  • src/app/Header/MobileMenu.tsx
  • src/app/Header/Options.tsx
  • src/app/Header/SettingsPopover.tsx
  • src/app/Header/WalletSection.tsx
  • src/app/Header/index.tsx
  • src/app/Toastify.tsx
  • src/app/[pohid]/ProfileSummarySection.tsx
  • src/app/[pohid]/ProfileTimelineHeader.tsx
  • src/app/[pohid]/[chain]/[request]/ActionBar.tsx
  • src/app/[pohid]/[chain]/[request]/Appeal.tsx
  • src/app/[pohid]/[chain]/[request]/DegradedRequestPage.tsx
  • src/app/[pohid]/[chain]/[request]/Evidence.tsx
  • src/app/[pohid]/[chain]/[request]/Funding.tsx
  • src/app/[pohid]/[chain]/[request]/RequestErrorState.tsx
  • src/app/[pohid]/[chain]/[request]/RequestLoadingSkeleton.tsx
  • src/app/[pohid]/[chain]/[request]/Timeline.tsx
  • src/app/[pohid]/[chain]/[request]/error.tsx
  • src/app/[pohid]/[chain]/[request]/page.tsx
  • src/app/[pohid]/claim/Info.tsx
  • src/app/[pohid]/claim/Review.tsx
  • src/app/[pohid]/cross-chain/TransferSection.tsx
  • src/app/[pohid]/cross-chain/UpdateStateSection.tsx
  • src/app/attachment/page.tsx
  • src/app/error.tsx
  • src/app/globals.css
  • src/app/layout.tsx
  • src/components/Accordion.tsx
  • src/components/ActionButton.tsx
  • src/components/Attachment.tsx
  • src/components/BulletedNumber.tsx
  • src/components/Checklist.tsx
  • src/components/CurrencyField.tsx
  • src/components/Dropdown/Item.tsx
  • src/components/Dropdown/Menu.tsx
  • src/components/ExternalLink.tsx
  • src/components/Field.tsx
  • src/components/FileUploadZone.tsx
  • src/components/Integrations/IntegrationCard.tsx
  • src/components/Integrations/IntegrationHeader.tsx
  • src/components/LoadableImage.tsx
  • src/components/MediaFallback.tsx
  • src/components/Modal.tsx
  • src/components/Popover.tsx
  • src/components/Previewed.tsx
  • src/components/Request/Card.tsx
  • src/components/Request/Grid.tsx
  • src/components/Request/StatusBadge.tsx
  • src/components/SearchBar.tsx
  • src/components/SignInButton.tsx
  • src/components/StatusIcon.tsx
  • src/components/SubgraphsStatus.tsx
  • src/components/Switch.tsx
  • src/components/VideoThumbnail.tsx
  • src/components/Webcam.tsx
  • src/components/addLinkToText.tsx
  • src/config/chains.ts
  • src/context/AppKitProvider.tsx
  • src/contracts/apis/APIArbitrator.ts
  • src/contracts/apis/APIPoH.ts
  • src/contracts/config.ts
  • src/data/integrations.ts
  • src/data/user.ts
  • src/hooks/useDarkMode.ts
  • src/hooks/useIPFS.ts
  • src/utils/identifier.ts
  • src/utils/name.ts
  • tailwind.config.cjs
💤 Files with no reviewable changes (1)
  • src/hooks/useDarkMode.ts

Comment thread next.config.js
{
protocol: "https",
hostname: process.env.REACT_APP_IPFS_GATEWAY,
hostname: ipfsGatewayHostname,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for environment variable examples or usages
rg "REACT_APP_IPFS_GATEWAY" --glob "*.env*" --glob "*.md"

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## matches for REACT_APP_IPFS_GATEWAY\n'
rg -n "REACT_APP_IPFS_GATEWAY" . || true

printf '\n## next.config.js context\n'
nl -ba next.config.js | sed -n '85,115p'

printf '\n## remotePatterns usage\n'
rg -n "remotePatterns|ipfsGatewayHostname|new URL\\(" next.config.js . || true

printf '\n## env/docs references around IPFS gateway\n'
rg -n "ipfs gateway|IPFS_GATEWAY|ipfsGateway|gateway" . --glob "*.md" --glob "*.env*" --glob "*.js" --glob "*.ts" --glob "*.tsx" || true

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 782


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## next.config.js\n'
sed -n '1,130p' next.config.js

printf '\n## src/utils/ipfs.ts\n'
sed -n '1,80p' src/utils/ipfs.ts

printf '\n## CONTRIBUTING.md env example\n'
sed -n '30,45p' CONTRIBUTING.md

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 5005


Normalize REACT_APP_IPFS_GATEWAY before using it as remotePatterns.hostname

CONTRIBUTING.md documents this env var as a full URL (https://cdn.kleros.link), but next.config.js passes it straight into hostname. Strip the scheme first (or derive the hostname from new URL(...)) so image matching works with the documented format.

🤖 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 `@next.config.js` at line 102, Normalize the value used by the
`remotePatterns.hostname` configuration in `next.config.js`: derive a hostname
from the documented full-URL `REACT_APP_IPFS_GATEWAY` value by removing its
scheme before assigning it to `ipfsGatewayHostname`. Preserve support for the
existing configuration flow while ensuring the remote pattern receives only the
host, not `https://` or another protocol.

Comment thread package.json
"dotenv": "^16.0.3",
"graphql": "^16.4.0",
"graphql-request": "^4.2.0",
"graphql-request": "^7.1.2",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "moduleResolution|\"type\"" tsconfig.json package.json
rg -n "graphql-request" -A2 -B2 src/generated/graphql.ts src/config/subgraph.ts

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 705


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package.json ==\n'
cat package.json | python3 - <<'PY'
import json,sys
data=json.load(sys.stdin)
print("scripts:")
for k,v in data.get("scripts",{}).items():
    print(f"  {k}: {v}")
print("dependencies:")
for k in ("graphql-request","next","typescript"):
    if k in data.get("dependencies",{}):
        print(f"  {k}: {data['dependencies'][k]}")
    if k in data.get("devDependencies",{}):
        print(f"  {k}: {data['devDependencies'][k]}")
PY

printf '\n== tsconfig.json ==\n'
cat -n tsconfig.json | sed -n '1,120p'

printf '\n== src/config/subgraph.ts ==\n'
cat -n src/config/subgraph.ts | sed -n '1,220p'

printf '\n== src/generated/graphql.ts (top) ==\n'
cat -n src/generated/graphql.ts | sed -n '1,220p'

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 921


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json =="
python3 - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("package.json").read_text())
print("name:", data.get("name"))
print("type:", data.get("type"))
print("scripts:")
for k, v in data.get("scripts", {}).items():
    print(f"  {k}: {v}")
for section in ("dependencies", "devDependencies"):
    deps = data.get(section, {})
    if "graphql-request" in deps or "typescript" in deps or "next" in deps:
        print(section + ":")
        for k in ("graphql-request", "next", "typescript"):
            if k in deps:
                print(f"  {k}: {deps[k]}")
PY

echo
echo "== tsconfig.json =="
cat -n tsconfig.json | sed -n '1,200p'

echo
echo "== src/config/subgraph.ts =="
cat -n src/config/subgraph.ts | sed -n '1,220p'

echo
echo "== src/generated/graphql.ts (top) =="
cat -n src/generated/graphql.ts | sed -n '1,220p'

echo
echo "== all graphql-request references =="
rg -n "graphql-request|generated/graphql|getSdk" src netlify . -g '!node_modules'

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 20904


🌐 Web query:

graphql-request v7 package exports ESM-only RequestOptions GraphQLClient TypeScript moduleResolution node bundler node16 nodenext

💡 Result:

Starting with version 7.x, graphql-request is distributed as a pure ESM (ECMAScript Module) package [1][2]. This means it does not support CommonJS (require) imports [1]. Because the package utilizes modern package.exports, TypeScript users must configure their projects to correctly resolve these exports [3][2]. To successfully use graphql-request v7 in a TypeScript project, you must ensure the following configurations are met: 1. Package Configuration: Your project's package.json must include "type": "module" [4][1][2]. 2. TypeScript Configuration: In your tsconfig.json, the moduleResolution option must be set to "node16", "nodenext", or "bundler" [4][1][2][5]. - "node16" or "nodenext" are recommended for Node.js projects, as they align with Node's native ESM resolution algorithm [5][6]. - "bundler" is suitable if you are using a frontend bundler (e.g., Vite, Webpack), as it supports package.exports without strictly requiring file extensions on relative imports [5]. Regarding the types and exports, graphql-request v7 maintains the GraphQLClient class and various request options (such as RequestOptions and RawRequestOptions) [7][8][1]. Because the package is ESM-only, ensure you are using ES module import syntax (e.g., import { GraphQLClient } from 'graphql-request') [9][1]. If you encounter "Cannot find module" errors, it is almost certainly due to an incompatible moduleResolution setting in your tsconfig.json [4][10].

Citations:


graphql-request v7 needs a compatible TypeScript resolver here

src/generated/graphql.ts and src/config/subgraph.ts import GraphQLClient/RequestOptions from graphql-request, but tsconfig.json still uses moduleResolution: "node". v7 is ESM-only, so this can break tsc --noEmit; switch to bundler/node16/nodenext or keep graphql-request on v6.

🤖 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 `@package.json` at line 57, Update tsconfig.json’s moduleResolution to an
ESM-compatible resolver such as bundler, node16, or nodenext so the
GraphQLClient and RequestOptions imports in generated/graphql.ts and
config/subgraph.ts resolve with graphql-request v7; alternatively, downgrade
graphql-request to v6 while preserving the existing resolver.

Comment thread src/app/globals.css
Comment on lines 41 to +199
.checkbox {
@apply border-orange bg-lightOrange h-6 w-6 rounded-sm border;
@apply h-6 w-6 cursor-pointer rounded-md border;
appearance: none;
background-color: #292d35;
border-color: #ffb08a;
accent-color: #ffb08a;
color: #ffb08a;
}

.checkbox:hover {
border-color: #ff9966;
accent-color: #ff9966;
}

.checkbox:focus,
.checkbox:focus-visible {
outline: 2px solid #ff9966;
outline-offset: 2px;
border-color: #ff9966;
box-shadow: none;
--tw-ring-color: #ff9966;
--tw-ring-shadow: 0 0 #0000;
}

.checkbox:checked {
background-color: #ffb08a;
border-color: #ffb08a;
accent-color: #ffb08a;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.5 8.5L6.5 11.5L12.5 4.5' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}

.checkbox:checked:hover {
background-color: #ff9966;
border-color: #ff9966;
accent-color: #ff9966;
}

.radio {
@apply border-orange bg-lightOrange h-6 w-6 rounded-full border;
@apply h-6 w-6 cursor-pointer rounded-full border;
appearance: none;
background-color: #292d35;
border-color: #3a3e48;
accent-color: #ffb08a;
color: #ffb08a;
}

.radio:hover {
border-color: #ffb08a;
accent-color: #ffb08a;
}

.radio:focus,
.radio:focus-visible {
outline: 2px solid #ff9966;
outline-offset: 2px;
box-shadow: none;
--tw-ring-color: #ff9966;
--tw-ring-shadow: 0 0 #0000;
}

.radio:checked {
background-color: #292d35;
border-color: #ffb08a;
color: #ffb08a;
accent-color: #ffb08a;
background-image: radial-gradient(circle, #ffb08a 0 40%, transparent 42%);
}

.bordered {
@apply gradient rounded p-px;
@apply rounded bg-peach p-px;
}

.btn-primary,
.btn-main {
@apply centered gradient w-full rounded-sm p-2 font-medium uppercase text-white disabled:opacity-40 md:w-auto;
@apply centered min-h-[48px] w-full rounded-btn px-8 py-2.5 font-normal normal-case text-white transition duration-200 ease-premium disabled:opacity-40 md:w-auto;
}

.btn-secondary {
@apply centered min-h-[48px] w-full rounded-btn border px-8 py-2.5 font-normal normal-case transition duration-200 ease-premium disabled:opacity-40 md:w-auto;
background: transparent;
border-color: #ffb08a;
color: #ffb08a;
}

.btn-secondary:hover,
.btn-secondary:active {
background: rgba(255, 153, 102, 0.32);
}

.btn-tertiary {
@apply centered min-h-[48px] w-full rounded-btn border px-8 py-2.5 font-normal normal-case transition duration-200 ease-premium disabled:opacity-40 md:w-auto;
background: #292d35;
border-color: #3a3e48;
color: #f7f3ef;
}

.btn-tertiary:hover,
.btn-tertiary:active {
background: #3a3e48;
}

.tooltip-surface {
position: relative;
@apply rounded-btn border p-4;
background: #2f333d;
border-color: #3a3e48;
color: #f7f3ef;
}

.tooltip-surface::before {
position: absolute;
top: calc(100% - 5px);
left: 50%;
width: 10px;
height: 10px;
content: "";
background: #2f333d;
border-bottom: 1px solid #3a3e48;
border-right: 1px solid #3a3e48;
transform: translateX(-50%) rotate(45deg);
}

/* Circle chrome: rest = dark surface + peach glyph; hover = orange fill + white glyph.
!important so ExternalLink's default hover:text-orange does not reintroduce link blue/orange on the glyph. */
.icon-btn {
@apply flex items-center justify-center rounded-full border transition-colors duration-200;
background: #2f333d;
border-color: #3a3e48;
color: #ffb08a !important;
}

.icon-btn:hover,
.icon-btn:focus-visible {
background: #ff9966;
border-color: #ff9966;
color: #ffffff !important;
}

.icon-btn svg {
@apply h-4 w-4 shrink-0;
}

.btn-primary svg,
.btn-secondary svg,
.btn-tertiary svg {
transition: transform 200ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.btn-sec {
@apply centered border-theme text-orange w-full rounded-sm border-2 p-2 font-semibold uppercase disabled:opacity-40 md:w-auto;
.btn-primary:hover svg,
.btn-secondary:hover svg,
.btn-tertiary:hover svg {
transform: translateX(2px);
}

.backdrop {
@apply fixed left-0 top-0 z-10 flex h-screen w-full items-center justify-center bg-slate-800/30 backdrop-blur;
@apply fixed left-0 top-0 z-10 flex h-screen w-full items-center justify-center bg-black/50 backdrop-blur-sm;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Repeated stylelint declaration-empty-line-before violations across globals.css. Root cause: a plain CSS declaration immediately follows an @apply (or custom-property) line with no blank line in between, throughout the new rules added in this file.

  • src/app/globals.css#L41-L199: add a blank line before the first plain declaration in .checkbox (L43), .radio (L83), .btn-secondary (L123), .btn-tertiary (L135), .tooltip-surface (L148), and .icon-btn (L170).
  • src/app/globals.css#L211-L216: add a blank line before background: in .modal-surface (L213).
  • src/app/globals.css#L348-L457: add a blank line before the first plain declaration in .status-pill (L350), .paper (L391), .reward-surface (L413-417), .pill-tag (L439), .poh-toast-container (L446), and .poh-toast (L452).
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 43-43: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 83-83: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 123-123: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 135-135: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 148-148: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 170-170: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

📍 Affects 1 file
  • src/app/globals.css#L41-L199 (this comment)
  • src/app/globals.css#L211-L216
  • src/app/globals.css#L348-L457
🤖 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 `@src/app/globals.css` around lines 41 - 199, Resolve the stylelint
declaration-empty-line-before violations by inserting a blank line between each
`@apply` or custom-property declaration and the first following plain declaration
in .checkbox, .radio, .btn-secondary, .btn-tertiary, .tooltip-surface, and
.icon-btn within src/app/globals.css lines 41-199; apply the same spacing before
background in .modal-surface at lines 211-216 and before the first plain
declarations in .status-pill, .paper, .reward-surface, .pill-tag,
.poh-toast-container, and .poh-toast at lines 348-457. No declaration values or
rule behavior should change.

Source: Linters/SAST tools

Comment thread src/app/globals.css
Comment on lines +348 to +354
.status-pill {
@apply inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold;
background: rgba(47, 51, 61, 0.82);
border-color: rgba(255, 255, 255, 0.08);
color: #b8b1c2;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stylelint: more missing blank lines before declarations.

Same declaration-empty-line-before violation at Line 350 (.status-pill), Line 391 (.paper), Lines 413-417 (.reward-surface background), Line 439 (.pill-tag), Line 446 (.poh-toast-container, after custom property), Line 452 (.poh-toast, after custom property).

Also applies to: 389-400, 411-421, 437-442, 444-448, 450-457

🧰 Tools
🪛 Stylelint (17.14.0)

[error] 350-350: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 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 `@src/app/globals.css` around lines 348 - 354, Fix the
declaration-empty-line-before violations in the CSS rules .status-pill, .paper,
.reward-surface, .pill-tag, .poh-toast-container, and .poh-toast by inserting
the required blank lines before the flagged declarations, including after custom
properties where specified. Preserve all existing styles and values.

Source: Linters/SAST tools

Comment thread src/app/globals.css
Comment on lines +356 to +363
.status-pill::before {
content: "";
width: 7px;
height: 7px;
border-radius: 999px;
background: currentColor;
box-shadow: 0 0 12px currentColor;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stylelint: currentColor should be currentcolor.

value-keyword-case flags Lines 361, 362 (.status-pill::before) and Line 525 (.poh-toast .Toastify__toast-icon svg).

🎨 Fix
-    background: currentColor;
-    box-shadow: 0 0 12px currentColor;
+    background: currentcolor;
+    box-shadow: 0 0 12px currentcolor;
-    fill: currentColor;
+    fill: currentcolor;

Also applies to: 512-526

🧰 Tools
🪛 Stylelint (17.14.0)

[error] 361-361: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)


[error] 362-362: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 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 `@src/app/globals.css` around lines 356 - 363, Update the CSS color keyword
casing from currentColor to currentcolor in .status-pill::before and the
.poh-toast .Toastify__toast-icon svg rule, including both background and
box-shadow declarations, to satisfy Stylelint value-keyword-case.

Source: Linters/SAST tools

Comment on lines +58 to 93
const updateCardHoverParallax = (event: PointerEvent<HTMLAnchorElement>) => {
const rect = event.currentTarget.getBoundingClientRect();
const x = ((event.clientX - rect.left) / rect.width - 0.5) * -18;
const y = ((event.clientY - rect.top) / rect.height - 0.5) * -8;

event.currentTarget.style.setProperty(
"--request-card-hover-x",
`${x.toFixed(2)}px`,
);
event.currentTarget.style.setProperty(
"--request-card-hover-y",
`${y.toFixed(2)}px`,
);
};

const resetCardHoverParallax = (event: PointerEvent<HTMLAnchorElement>) => {
event.currentTarget.style.removeProperty("--request-card-hover-x");
event.currentTarget.style.removeProperty("--request-card-hover-y");
};

const ErrorFallback: React.FC<{ claimer?: { name?: string | null } }> = ({
claimer,
}) => (
<div className="h-84 flex animate-pulse flex-col items-center bg-white p-2">
<div className="mx-auto mb-2 h-32 w-32 rounded-full bg-slate-200" />
<span className="font-semibold">{claimer?.name}</span>
<span>Some error occurred...</span>
</div>
<>
<div className="bg-grey absolute inset-0" />
<div className="request-card-overlay absolute inset-0" />
<div className="absolute inset-x-0 bottom-0 z-10 px-4 pb-[21px]">
<div className="truncate text-2xl font-semibold text-white">
{claimer?.name || "Unavailable"}
</div>
<div className="mt-1 text-sm text-dark-secondaryText">
Profile media could not be loaded
</div>
</div>
</>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline src/hooks/useIPFS.ts
rg -n "throw" src/hooks/useIPFS.ts -A3 -B3

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== useIPFS.ts outline =="
ast-grep outline src/hooks/useIPFS.ts

echo
echo "== useIPFS.ts (numbered) =="
cat -n src/hooks/useIPFS.ts | sed -n '1,220p'

echo
echo "== Card.tsx outline =="
ast-grep outline src/components/Request/Card.tsx

echo
echo "== Card.tsx relevant lines =="
cat -n src/components/Request/Card.tsx | sed -n '1,220p'

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 11364


ErrorFallback no longer covers IPFS load failures
useIPFS returns [data, error] and Content handles those states inline, so this boundary now only catches unexpected render crashes. If the intent is to show this UI when media fetches fail, the hook needs to throw again or the fallback should be removed.

🤖 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 `@src/components/Request/Card.tsx` around lines 58 - 93, The ErrorFallback
component is no longer used for IPFS media-fetch errors because Content handles
the useIPFS error state inline. Remove the obsolete ErrorFallback UI, unless the
intended behavior is to restore hook-thrown errors; keep the existing inline
Content error handling unchanged.

Comment on lines +26 to +31
async function getLatestBlock(chainId: SupportedChainId) {
const client = createPublicClient({
transport: http(getChainRpc(chainId)),
});
return Number(await client.getBlockNumber());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf '\n## Files\n'
git ls-files | rg '^(src/components/SubgraphsStatus\.tsx|src/app/layout\.tsx|package.json|pnpm-lock\.yaml|yarn.lock|package-lock\.json)$'

printf '\n## Outline: SubgraphsStatus\n'
ast-grep outline src/components/SubgraphsStatus.tsx --view expanded || true

printf '\n## Read relevant slices\n'
sed -n '1,220p' src/components/SubgraphsStatus.tsx

printf '\n## Layout usage\n'
sed -n '1,220p' src/app/layout.tsx

printf '\n## Package versions mentioning viem / fetch wrappers\n'
rg -n '"viem"|getBlockNumber|createPublicClient|http\(' package.json pnpm-lock.yaml yarn.lock package-lock.json src -g '!**/node_modules/**' || true

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 15990


🏁 Script executed:

set -euo pipefail

printf '\n## File sizes\n'
wc -l src/components/SubgraphsStatus.tsx src/app/layout.tsx || true

printf '\n## Exact lines around probe implementation\n'
nl -ba src/components/SubgraphsStatus.tsx | sed -n '1,220p'

printf '\n## Exact lines around layout mount\n'
nl -ba src/app/layout.tsx | sed -n '1,220p'

printf '\n## Search for timeout / abort usage in subgraph status path\n'
rg -n 'AbortController|timeout|setInterval|REFRESH_INTERVAL_MS|probeSubgraph|getLatestBlock|fetch\(' src/components/SubgraphsStatus.tsx src/app/layout.tsx src -g '!**/node_modules/**' || true

printf '\n## Search viem http transport docs-like usage in repo\n'
rg -n 'createPublicClient\(|http\(' src -g '!**/node_modules/**' || true

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 354


🏁 Script executed:

set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('src/components/SubgraphsStatus.tsx')
print(p.read_text())
PY

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 6252


🏁 Script executed:

set -euo pipefail

rg -n 'probeSubgraph|getLatestBlock|AbortController|signal:|timeout' src/components/SubgraphsStatus.tsx src -g '!**/node_modules/**'

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 2169


🌐 Web query:

viem http transport default timeout getBlockNumber timeout fetchOptions

💡 Result:

In Viem, the http transport has a default timeout of 10,000 milliseconds (10 seconds) [1][2]. You can configure this timeout when initializing the http transport by providing a timeout option in the transport configuration object [1]: import { http } from 'viem' const transport = http('https://rpc.example.com', { timeout: 60_000, // Timeout in ms }) Regarding fetchOptions, these are separate from the transport's internal timeout mechanism. You can pass fetchOptions to the http transport to customize the underlying Fetch API request (e.g., headers, mode, credentials), but these do not override or directly control the Viem-level timeout [3][4][5]: const transport = http('https://rpc.example.com', { fetchOptions: { headers: { 'x-custom-header': 'value' }, }, }) For actions like getBlockNumber, the request is executed using the transport defined on your client [6]. There is no specific timeout parameter in getBlockNumber itself [7][6]. Instead, the request inherits the timeout configured on the transport [1]. If the request exceeds the transport's timeout, Viem will throw an error [2]. Summary of Key Points: - Default Timeout: 10,000 ms [1][2]. - Configuration: Set via the timeout property in the http transport options [1]. - FetchOptions: Used for customizing the fetch request, not for setting timeouts [3][4][5]. - Action Behavior: Actions like getBlockNumber use the client's transport configuration [6].

Citations:


🏁 Script executed:

set -euo pipefail
sed -n '1,220p' src/components/SubgraphsStatus.tsx

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 6251


🌐 Web query:

viem http transport timeout default getBlockNumber docs

💡 Result:

In Viem, the default timeout for the HTTP transport is 10,000 milliseconds (10 seconds) [1][2][3]. When you use getBlockNumber, it utilizes the transport configured for your client to perform the underlying eth_blockNumber JSON-RPC request [4]. Consequently, any requests made via getBlockNumber are subject to this 10-second timeout by default [1][3]. You can override this timeout when configuring the HTTP transport by providing the timeout option in milliseconds [1]: import { http } from 'viem' const transport = http('https://your-rpc-url.com', { timeout: 60_000, // Sets timeout to 60 seconds }) This default timeout behavior is consistent across other transports in Viem, such as webSocket and ipc, which also default to 10,000 milliseconds [5][6].

Citations:


Add a timeout around the subgraph fetch getBlockNumber() already inherits viem’s HTTP transport timeout, but the subgraph request itself can still hang forever. With the 60s polling loop, a stuck fetch can leave overlapping in-flight probes.

🤖 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 `@src/components/SubgraphsStatus.tsx` around lines 26 - 31, Update the subgraph
fetch flow in SubgraphsStatus.tsx to enforce an explicit timeout on each
request, using AbortController or the project’s existing timeout mechanism.
Ensure a timed-out fetch rejects and is handled by the existing polling/error
path so probes cannot remain in flight or overlap across the 60-second polling
cycle.

}, []);

if (unhealthy.length === 0) return null;
const visibleStatuses = statuses.some((status) => !status.ok) ? statuses : [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

visibleStatuses shows all chains, not just unhealthy ones.

statuses.some(...) ? statuses : [] only checks whether any chain is unhealthy — it then returns the full statuses array (including healthy chains) instead of filtering to the unhealthy subset. The banner will rotate through healthy chains too whenever one chain is degraded.

Proposed fix
-  const visibleStatuses = statuses.some((status) => !status.ok) ? statuses : [];
+  const visibleStatuses = statuses.filter((status) => !status.ok);
📝 Committable suggestion

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

Suggested change
const visibleStatuses = statuses.some((status) => !status.ok) ? statuses : [];
const visibleStatuses = statuses.filter((status) => !status.ok);
🤖 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 `@src/components/SubgraphsStatus.tsx` at line 148, Update the visibleStatuses
computation in SubgraphsStatus to filter statuses to only entries where
status.ok is false. Preserve the empty-array result when all chains are healthy,
ensuring the banner never includes healthy chains when any unhealthy chain
exists.

- Introduced a new SVG logo for the POH ID badge, replacing the previous logo in various components.
- Created the ActionWalletGate component to manage wallet connection and chain switching for profile action buttons.
- Updated ProfileActionsSection to utilize ActionWalletGate for improved user experience.
- Refactored error handling and loading states in multiple components for consistency and better UX.
- Enhanced styling and accessibility across components, including updates to button classes and layout adjustments.
…ansaction handling

- Introduced useEnoughFunds hook to check wallet balance against transaction amounts, enhancing user feedback on insufficient funds.
- Implemented resolveTxState utility to streamline button state management across various components, providing consistent tooltip messages and disabled states.
- Updated multiple components (RevokeModal, ActionBar, Appeal, Challenge, Funding, etc.) to utilize the new hooks for better user experience and error handling.
- Refactored tooltip and disabled logic in buttons to improve clarity and responsiveness during transactions.
- Updated ActionWalletGate to improve wallet connection logic and chain switching.
- Refactored RevokeModal to ensure loading states are properly managed during revoke actions.
- Enhanced ActionBar, Appeal, and Challenge components to provide clearer feedback on connection status and balance checks.
- Improved Funding component to validate input amounts and provide appropriate error messages.
- Streamlined Timeline and Review components for better accessibility and user experience.
- Added tooltip support for better guidance during cross-chain actions.
Add POH ID badge SVG and implement ActionWalletGate component

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (5)
src/utils/txState.ts (1)

1-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests for resolveTxState.

This pure function now centralizes disable/tooltip logic for transaction gating across RemoveVouch, Challenge, ActionBar, Funding, and Appeal. It's trivial to unit test (priority ordering, message-less checks, empty array) and a regression here would silently break disabled/tooltip behavior everywhere it's used.

🤖 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 `@src/utils/txState.ts` around lines 1 - 18, Add focused unit tests for
resolveTxState covering an empty checks array, active and inactive checks,
priority ordering among multiple active checks, and active message-less checks
that disable without producing a tooltip. Verify the returned disabled and
tooltip values for each case without changing the implementation.
src/hooks/useEnoughFunds.ts (1)

19-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider reserving a small buffer for gas.

The hook only checks whether balance >= amount, explicitly excluding gas as documented. Since this gates payable actions in Challenge.tsx, Funding.tsx, and Appeal.tsx, a wallet with exactly the required balance will be reported as "sufficient," then fail on-chain (wasting gas) because nothing is left to pay the transaction fee.

Consider adding an optional gas-reserve margin (even a rough static buffer per chain) so insufficient reflects the realistic minimum needed to complete the transaction.

🤖 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 `@src/hooks/useEnoughFunds.ts` around lines 19 - 57, Update useEnoughFunds to
include a small gas-reserve margin when determining insufficient, rather than
comparing balanceValue only with amount. Add an optional or chain-aware reserve
using the existing chainId context, and ensure both the insufficient result and
displayed balance message reflect the total required amount while preserving
current behavior when balance data or amount is unavailable.
src/app/[pohid]/[chain]/[request]/ActionBar.tsx (1)

240-272: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract a shared "wrong chain" check helper.

Five files independently rebuild the identical chain-mismatch check object ({ active: userChainId !== chainId, message: Switch your chain above to ${idToChain(chainId)?.name || "the correct chain"} }). ActionBar.tsx already dedups this locally into a single wrongChainCheck reused across its three triggers, showing the pattern is easy to share — it just isn't shared across files.

  • src/app/[pohid]/[chain]/[request]/ActionBar.tsx#L240-L272: extract the local wrongChainCheck object into a shared helper (e.g. wrongChainCheck(userChainId, chain.id)) exported from utils/txState.ts, and use it here.
  • src/app/[pohid]/[chain]/[request]/RemoveVouch.tsx#L99-L108: replace the inline chain-mismatch check in the trigger array with the shared helper.
  • src/app/[pohid]/[chain]/[request]/Challenge.tsx#L249-L273: replace both inline chain-mismatch checks (in submitDisabled and trigger) with the shared helper.
  • src/app/[pohid]/[chain]/[request]/Funding.tsx#L122-L157: replace both inline chain-mismatch checks (in isDisabled and trigger) with the shared helper.
  • src/app/[pohid]/[chain]/[request]/Appeal.tsx#L89-L115: replace the inline chain-mismatch check in SideFunding's resolveTxState call with the shared helper.
♻️ Proposed shared helper
 export type TxCheck = { active: boolean; message?: string };

+export function wrongChainCheck(userChainId: number, targetChainId: number): TxCheck {
+  return {
+    active: userChainId !== targetChainId,
+    message: `Switch your chain above to ${idToChain(targetChainId)?.name || "the correct chain"}`,
+  };
+}
+
 export function resolveTxState(checks: TxCheck[]): {
🤖 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 `@src/app/`[pohid]/[chain]/[request]/ActionBar.tsx around lines 240 - 272,
Extract the duplicated chain-mismatch object into an exported helper in
utils/txState.ts, such as wrongChainCheck(userChainId, chainId), preserving the
existing active comparison and switch-chain message. Update
src/app/[pohid]/[chain]/[request]/ActionBar.tsx lines 240-272 to use the helper
for all three triggers; update RemoveVouch.tsx lines 99-108, both checks in
Challenge.tsx lines 249-273, both checks in Funding.tsx lines 122-157, and the
SideFunding resolveTxState check in Appeal.tsx lines 89-115 to use it instead of
inline objects.
src/app/[pohid]/ProfileSummarySection.tsx (1)

226-241: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

External-link icon on an internal navigation link without target="_blank".

View source request uses ExternalLinkIcon and group/external-link styling (mirroring the truly-external "Claimed by" link), but this Link navigates to another internal profile route in the same tab, with no target="_blank". Consider either dropping the external-link affordance or opening it in a new tab to match the icon's implied behavior.

🤖 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 `@src/app/`[pohid]/ProfileSummarySection.tsx around lines 226 - 241, The “View
source request” link in ProfileSummarySection uses external-link affordance
while navigating internally in the same tab. Update the Link for
punishedVouchSourceHref to open the destination in a new tab, or remove the
ExternalLinkIcon and external-link styling so its behavior matches the indicated
navigation.
src/app/[pohid]/ActionWalletGate.tsx (1)

40-48: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider surfacing switch-chain pending/error feedback.

switchChain?.({ chainId: homeChain.id }) is fire-and-forget: if the user rejects the request or the connector can't switch programmatically, the button just sits there with no feedback. Since this PR already introduces action-feedback patterns elsewhere, consider surfacing isPending/error from useSwitchChain() here too.

🤖 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 `@src/app/`[pohid]/ActionWalletGate.tsx around lines 40 - 48, Update the
chain-switching flow in ActionWalletGate to consume isPending and error from
useSwitchChain, and surface the pending or failure state through the existing
action-feedback UI patterns. Keep the current home-chain mismatch condition and
switch request behavior, while making rejection or connector failures visible to
the user.
🤖 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 `@src/app/`[pohid]/claim/Review.tsx:
- Around line 73-81: The self-funded amount parsing in Review should not convert
parse failures into the empty-input state. Track the parse error separately from
selfFundedWei, pass undefined only when no amount was entered, and update the
submit validation/UI near the existing submission logic to block submission and
show an error when parsing fails.

In `@src/app/`[pohid]/ProfileSummarySection.tsx:
- Line 232: Update the profile removal message in the punishedVouchReason
rendering to include the article “a” before “Sybil Attack,” while leaving the
“Identity Theft” wording unchanged.

In `@src/app/`[pohid]/RevokeConsequences.tsx:
- Around line 18-42: Update the STEPS-driven rendering in RevokeConsequences so
“If unchallenged” and “If challenged” are displayed as sibling alternative
branches after the challenge window, rather than consecutive items in one
connected <ol>. Preserve their existing content, icons, and styling while
adjusting the relevant rendering around the outcome cards.

---

Nitpick comments:
In `@src/app/`[pohid]/[chain]/[request]/ActionBar.tsx:
- Around line 240-272: Extract the duplicated chain-mismatch object into an
exported helper in utils/txState.ts, such as wrongChainCheck(userChainId,
chainId), preserving the existing active comparison and switch-chain message.
Update src/app/[pohid]/[chain]/[request]/ActionBar.tsx lines 240-272 to use the
helper for all three triggers; update RemoveVouch.tsx lines 99-108, both checks
in Challenge.tsx lines 249-273, both checks in Funding.tsx lines 122-157, and
the SideFunding resolveTxState check in Appeal.tsx lines 89-115 to use it
instead of inline objects.

In `@src/app/`[pohid]/ActionWalletGate.tsx:
- Around line 40-48: Update the chain-switching flow in ActionWalletGate to
consume isPending and error from useSwitchChain, and surface the pending or
failure state through the existing action-feedback UI patterns. Keep the current
home-chain mismatch condition and switch request behavior, while making
rejection or connector failures visible to the user.

In `@src/app/`[pohid]/ProfileSummarySection.tsx:
- Around line 226-241: The “View source request” link in ProfileSummarySection
uses external-link affordance while navigating internally in the same tab.
Update the Link for punishedVouchSourceHref to open the destination in a new
tab, or remove the ExternalLinkIcon and external-link styling so its behavior
matches the indicated navigation.

In `@src/hooks/useEnoughFunds.ts`:
- Around line 19-57: Update useEnoughFunds to include a small gas-reserve margin
when determining insufficient, rather than comparing balanceValue only with
amount. Add an optional or chain-aware reserve using the existing chainId
context, and ensure both the insufficient result and displayed balance message
reflect the total required amount while preserving current behavior when balance
data or amount is unavailable.

In `@src/utils/txState.ts`:
- Around line 1-18: Add focused unit tests for resolveTxState covering an empty
checks array, active and inactive checks, priority ordering among multiple
active checks, and active message-less checks that disable without producing a
tooltip. Verify the returned disabled and tooltip values for each case without
changing the implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 680f0ff6-df30-4a49-893e-f03063193e75

📥 Commits

Reviewing files that changed from the base of the PR and between 287222b and 4774e76.

⛔ Files ignored due to path filters (1)
  • public/logo/pohid-badge.svg is excluded by !**/*.svg
📒 Files selected for processing (41)
  • src/app/[pohid]/(profile)/loading.tsx
  • src/app/[pohid]/(profile)/page.tsx
  • src/app/[pohid]/ActionWalletGate.tsx
  • src/app/[pohid]/ProfileActionsSection.tsx
  • src/app/[pohid]/ProfileErrorCard.tsx
  • src/app/[pohid]/ProfileSectionErrorCard.tsx
  • src/app/[pohid]/ProfileSummarySection.tsx
  • src/app/[pohid]/ProfileTimelineError.tsx
  • src/app/[pohid]/ProfileTimelineSection.tsx
  • src/app/[pohid]/Renew.tsx
  • src/app/[pohid]/RevokeClient.tsx
  • src/app/[pohid]/RevokeConsequences.tsx
  • src/app/[pohid]/RevokeModal.tsx
  • src/app/[pohid]/[chain]/[request]/ActionBar.tsx
  • src/app/[pohid]/[chain]/[request]/Appeal.tsx
  • src/app/[pohid]/[chain]/[request]/Challenge.tsx
  • src/app/[pohid]/[chain]/[request]/Evidence.tsx
  • src/app/[pohid]/[chain]/[request]/Funding.tsx
  • src/app/[pohid]/[chain]/[request]/RemoveVouch.tsx
  • src/app/[pohid]/[chain]/[request]/Timeline.tsx
  • src/app/[pohid]/[chain]/[request]/Vouch.tsx
  • src/app/[pohid]/claim/Review.tsx
  • src/app/[pohid]/cross-chain/CrossChain.tsx
  • src/app/[pohid]/cross-chain/CrossChainError.tsx
  • src/app/[pohid]/cross-chain/CrossChainLoading.tsx
  • src/app/[pohid]/cross-chain/PendingRelaySection.tsx
  • src/app/[pohid]/cross-chain/TransferSection.tsx
  • src/app/[pohid]/cross-chain/UpdateStateSection.tsx
  • src/app/[pohid]/cross-chain/crossChainUi.tsx
  • src/app/[pohid]/error.tsx
  • src/components/ChainLogo.tsx
  • src/components/CopyButton.tsx
  • src/components/ExternalLinkIcon.tsx
  • src/components/Field.tsx
  • src/components/Modal.tsx
  • src/components/NewTabIcon.tsx
  • src/components/RequestPunishedVouchNotice.tsx
  • src/data/uploadEvidence.ts
  • src/hooks/useActionFeedback.ts
  • src/hooks/useEnoughFunds.ts
  • src/utils/txState.ts
💤 Files with no reviewable changes (1)
  • src/components/NewTabIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/Modal.tsx
  • src/app/[pohid]/[chain]/[request]/Evidence.tsx
  • src/components/Field.tsx

Comment on lines +73 to +81
let selfFundedWei: bigint | undefined;
if (!submitForFree && selfFunded) {
try {
selfFundedWei = parseEther(selfFunded.toString());
} catch {
selfFundedWei = undefined;
}
}
const funds = useEnoughFunds({ chainId, amount: selfFundedWei });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Review.tsx context ==\n'
sed -n '1,220p' src/app/'[pohid]'/claim/Review.tsx

printf '\n== Search for useEnoughFunds ==\n'
rg -n "function useEnoughFunds|const useEnoughFunds|useEnoughFunds\\(" src -g '!**/node_modules/**'

printf '\n== Candidate hook/file outline ==\n'
fd -a "useEnoughFunds.*|EnoughFunds.*" src || true

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 9490


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map likely files containing the hook implementation, then read the relevant slice.
files=$(rg -l "useEnoughFunds" src -g '!**/node_modules/**' || true)
printf '%s\n' "$files"

for f in $files; do
  printf '\n== %s ==\n' "$f"
  wc -l "$f"
  sed -n '1,240p' "$f"
done

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 39141


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read only the relevant parts of Review.tsx with line numbers.
nl -ba src/app/'[pohid]'/claim/Review.tsx | sed -n '1,180p'

printf '\n== useEnoughFunds references ==\n'
rg -n "useEnoughFunds" src -g '!**/node_modules/**' -A 12 -B 12

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Review.tsx around funding + submit ==\n'
python3 - <<'PY'
from pathlib import Path
p = Path("src/app/[pohid]/claim/Review.tsx")
for i, line in enumerate(p.read_text().splitlines(), 1):
    if 70 <= i <= 220 or 220 <= i <= 420:
        # print all if within ranges, but avoid too much? We'll filter below.
        pass
PY

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path("src/app/[pohid]/claim/Review.tsx")
lines = p.read_text().splitlines()
for start, end, title in [(70, 120, "funding"), (300, 430, "submit area")]:
    print(f"\n== {title} ({start}-{end}) ==")
    for i in range(start, min(end, len(lines)) + 1):
        print(f"{i:4d}: {lines[i-1]}")
PY

printf '\n== Disabled/submit occurrences ==\n'
rg -n "disabled=|onClick=|funds\\.|selfFunded|submitForFree" src/app/'[pohid]'/claim/Review.tsx

Repository: Proof-Of-Humanity/proof-of-humanity-v2-web

Length of output: 10571


Do not treat a parse failure as empty input. parseEther(selfFunded.toString()) falls back to undefined, and useEnoughFunds skips the balance check for undefined, so the Submit button can stay enabled on invalid deposit values. Keep parse errors separate and block submission/show an error. src/app/[pohid]/claim/Review.tsx:73-81, 424-435

🤖 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 `@src/app/`[pohid]/claim/Review.tsx around lines 73 - 81, The self-funded
amount parsing in Review should not convert parse failures into the empty-input
state. Track the parse error separately from selfFundedWei, pass undefined only
when no amount was entered, and update the submit validation/UI near the
existing submission logic to block submission and show an error when parsing
fails.

Punished vouch
</span>
<p className="text-secondaryText text-sm font-normal leading-6">
This profile was removed for aiding {punishedVouchReason}.{" "}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Grammar: missing article before "Sybil Attack".

"aiding Sybil Attack" should read "aiding a Sybil Attack" for grammatical correctness (the "Identity Theft" branch reads fine without an article).

✏️ Proposed fix
-                  This profile was removed for aiding {punishedVouchReason}.{" "}
+                  This profile was removed for aiding{" "}
+                  {punishedVouchReason === "Sybil Attack" ? "a " : ""}
+                  {punishedVouchReason}.{" "}
📝 Committable suggestion

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

Suggested change
This profile was removed for aiding {punishedVouchReason}.{" "}
This profile was removed for aiding{" "}
{punishedVouchReason === "Sybil Attack" ? "a " : ""}
{punishedVouchReason}.{" "}
🤖 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 `@src/app/`[pohid]/ProfileSummarySection.tsx at line 232, Update the profile
removal message in the punishedVouchReason rendering to include the article “a”
before “Sybil Attack,” while leaving the “Identity Theft” wording unchanged.

Comment on lines +18 to +42
const STEPS: OutcomeStep[] = [
{
icon: FlagCheckeredIcon,
title: "Submit request",
description: "Your deposit is locked while the request is open.",
accent: true,
},
{
icon: HourglassIcon,
title: "Challenge window",
description: "Anyone can contest the removal during this period.",
},
{
icon: CheckCircleOutlineIcon,
title: "If unchallenged",
description: "The profile is removed and your deposit is refunded.",
iconClassName: "text-status-registered",
},
{
icon: ChallengeIcon,
title: "If challenged",
description: "The dispute is resolved in Kleros Court.",
iconClassName: "text-peach",
},
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Render the two possible outcomes as branches, not sequential steps.

“If unchallenged” and “If challenged” are mutually exclusive, but the connected <ol> visually presents both as consecutive events. Split them into sibling outcome cards or branches after the challenge window.

Also applies to: 61-89

🤖 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 `@src/app/`[pohid]/RevokeConsequences.tsx around lines 18 - 42, Update the
STEPS-driven rendering in RevokeConsequences so “If unchallenged” and “If
challenged” are displayed as sibling alternative branches after the challenge
window, rather than consecutive items in one connected <ol>. Preserve their
existing content, icons, and styling while adjusting the relevant rendering
around the outcome cards.

@coderabbitai coderabbitai Bot mentioned this pull request Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant