Skip to content

X tdh#1510

Merged
simo6529 merged 291 commits intomainfrom
xTDH
Dec 5, 2025
Merged

X tdh#1510
simo6529 merged 291 commits intomainfrom
xTDH

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Oct 6, 2025

Summary by CodeRabbit

  • New Features

    • Full xTDH experience: /xtdh hub, user xTDH tab, stats panels, and a “Received” explorer.
    • Grant workflow: create grants, review granted items, token-level details, and grant management actions.
    • NFT Picker: contract search, token-range input, selection UI and virtualized token lists.
    • “Network” collected view with xTDH-aware cards and sorting.
  • Improvements

    • Unified user stats (TDH/xTDH/rates/Rep/NIC/Followers) across header, sidebar, and tooltips.
    • Better loading states, error handling, and accessibility.
  • Documentation

    • Added testing and frontend workflow guides.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 6, 2025

Walkthrough

Migrates user tabs to a centralized config (id-based tab objects). Introduces xTDH feature set (pages, stats, grants, received views), a full NFT picker (parsing, ranges, hooks, UI) and virtualized token list, Alchemy-backed API routes, NETWORK mode for collected pages, many new hooks/components, and widespread tests and error-handling hardening.

Changes

Cohort / File(s) Summary
User tabs & routing
components/user/layout/userTabs.config.ts, components/user/layout/UserPageTabs.tsx, components/user/layout/UserPageTab.tsx, components/user/utils/CommonProfileLink.tsx, components/profile-activity/list/items/*, components/header/header-search/*, app/[user]/*/page.tsx, __tests__/components/user/layout/*, __tests__/components/profile-activity/*, __tests__/components/user/utils/*
Replace enum/meta with config-driven tab definitions (ids, map, visibility). Components now accept tab objects and activeTabId. Pages use tab config for route/meta. Call sites updated to USER_PAGE_TAB_IDS and tests adjusted.
xTDH surface & grants
app/xtdh/page.tsx, components/xtdh/**/*, components/user/xtdh/**/*, components/react-query-wrapper/ReactQueryWrapper.tsx, __tests__/components/user/xtdh/*, __tests__/components/xtdh/*
New xTDH overview page and many xTDH features: user xTDH pages, stats header, granted lists, grant creation UI/hooks/validators, received collections/tokens, contributors panel, formatters, skeletons, and tests. React Query keys and invalidation extend for xTDH data.
NFT picker & Alchemy API
components/nft-picker/**/*, app/api/alchemy/*, __tests__/services/alchemy-api.test.ts
Add NftPicker (types, parsing, ranges, utilities, hooks, UI subcomponents), related utils and tests. Add Next.js App routes for Alchemy-backed collection search, contract overview, owner NFTs, and token-metadata endpoints.
Virtualized token list
components/token-list/**/*, __tests__/components/token-list/*
New VirtualizedTokenList and VirtualizedTokenListContent, token rows/grid, thumbnails, metadata windowing hooks, persistent scroll, utils/types, and tests for virtualization and rendering.
Collected page: NETWORK mode
components/user/collected/*, components/user/collected/hooks/useXtdhTokensQuery.ts, entities/IProfile.ts, components/collections-dropdown/CollectionsDropdown.tsx, components/navigation/isNavItemActive.ts, __tests__/components/user/collected/*
Introduce NETWORK subcollection and network-specific cards; add useXtdhTokensQuery, new sorts (XTDH, XTDH_DAY), filters/widgets, UI branching and tests.
Common UI & accessibility
components/utils/select/*, components/utils/select/tabs/*, components/utils/select/dropdown/*, components/utils/button/SecondaryButton.tsx, components/utils/modal/CommonConfirmationModal.tsx, __tests__/components/groups/page/create/*
Enhancements: badges and sizing in CommonSelect, improved Tabs (scroll/fade, keyboard), dropdown portal positioning and close-on-select, SecondaryButton loading, CommonConfirmationModal, and tests adapted to tab roles.
Profile & header stats consolidation
components/common/profile/*, components/user/utils/stats/UserStatsRow.tsx, components/header/*, components/user/utils/profile/*, __tests__/components/user/*
New ProfileAvatar/Handle/Level components; consolidate header/sidebar/tooltip stats into UserStatsRow (adds xtdh/xtdh_rate/cic propagation); update dependent components and tests.
nft-picker internals & parsing/ranges
components/nft-picker/utils/*, components/nft-picker/hooks/*, components/nft-picker/types.ts
Add robust token parsing, ranges, bigint safety, errors, formatting, mappers and hooks (search, selection, token input) with types and utilities.
App API, env & lint
config/alchemyEnv.ts, config/env.schema.ts, eslint.config.mjs, __tests__/config/env.base-endpoint.test.ts, GEMINI.md
Add Alchemy key loader, remove ALCHEMY_API_KEY from public schema, ignore new file in ESLint, update env test, and add documentation (GEMINI.md).
Error-handling hardening & misc
Many components/* and app/* files (fetches refactored to try/catch), minor UI/layout tweaks, new tests and re-exports
Wrap network calls in try/catch with logging and safe resets; many minor refactors, formatting, and new helper tests across the codebase.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Client UI (NftPicker / xTDH)
  participant AppAPI as Next.js App Route (/api/alchemy/*)
  participant Alchemy as Alchemy API

  User->>UI: search collections / select contract
  UI->>AppAPI: GET /api/alchemy/collections?query=...
  AppAPI->>Alchemy: forward search request (query, chain, hideSpam)
  Alchemy-->>AppAPI: collections JSON
  AppAPI-->>UI: collections JSON (no-store)

  User->>UI: request contract overview
  UI->>AppAPI: GET /api/alchemy/contract?address=0x...
  AppAPI->>Alchemy: fetch contract overview
  Alchemy-->>AppAPI: overview JSON
  AppAPI-->>UI: overview JSON (no-store)

  User->>UI: view token metadata
  UI->>AppAPI: POST /api/alchemy/token-metadata { address, tokenIds }
  AppAPI->>Alchemy: batch metadata request
  Alchemy-->>AppAPI: metadata JSON
  AppAPI-->>UI: metadata JSON (no-store)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • High breadth: many new feature areas (xTDH, nft-picker, token list) and new App API routes.
  • High depth: complex parsing/range logic, virtualization math, and hooks requiring focused review.
    Areas to focus during review:
  • nft-picker parsing/ranges/error handling (parsing correctness, MAX_ENUMERATION safeguards).
  • VirtualizedTokenList windowing and metadata window hooks (index math, onEndReached).
  • userTabs.config migration impact on routing and tests (redirects when tabs become invisible).
  • xTDH grant flows (validation, mutations, query invalidation) and new React Query keys.
  • New App API routes for Alchemy (input validation, error mapping, signal handling).

Possibly related PRs

Poem

hop-hop! my tabs got tidy IDs,
new burrows for xTDH seeds.
I sniff NFTs with picker smart,
list tokens swift, a scrolly art.
routes whisper to Alchemy streams —
carrots cached, UI gleams. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.44% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive Title is terse and unclear; it doesn't describe the main changes (xTDH feature additions and user-tab/config refactor). Use a concise descriptive title referencing the main change, e.g. 'Introduce xTDH feature and user-page tab config'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch xTDH

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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
components/user/xtdh/UserPageXtdh.tsx (1)

3-9: Stub component - confirm if implementation is planned.

The component currently returns null. Is this intentional as a placeholder for future development?

If you need help implementing the XTDH tab content, I can help scaffold the component structure. Would you like me to open an issue to track this implementation?

components/user/layout/UserPageTabs.tsx (1)

7-7: Derive activeTab from pathname with useMemo (remove state/effect)

Simplifies logic and avoids an extra render.

-import { useContext, useEffect, useMemo, useRef, useState } from "react";
+import { useContext, useMemo, useRef } from "react";
@@
-  const [activeTab, setActiveTab] = useState<UserPageTabKey>(
-    resolveTabFromPath(pathname)
-  );
-
-  useEffect(() => {
-    setActiveTab(resolveTabFromPath(pathname));
-  }, [pathname]);
+  const activeTab = useMemo<UserPageTabKey>(
+    () => resolveTabFromPath(pathname),
+    [pathname]
+  );

Also applies to: 62-68

components/user/layout/UserPageTab.tsx (1)

5-5: Remove derived state for isActive/classes; compute directly

Reduces re-renders and simplifies logic. Keep effect only for scroll-into-view.

-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef } from "react";
@@
-  const [isActive, setIsActive] = useState<boolean>(tab.id === activeTabId);
-  useEffect(() => {
-    setIsActive(tab.id === activeTabId);
-  }, [activeTabId, tab.id]);
+  const isActive = tab.id === activeTabId;
@@
-  const [classes, setClasses] = useState<string>(
-    isActive ? activeClasses : inActiveClasses
-  );
+  const classes = isActive ? activeClasses : inActiveClasses;
@@
-  useEffect(() => {
-    setClasses(isActive ? activeClasses : inActiveClasses);
-    if (ref.current && isActive && !isVisibleInViewportSide()) {
-      ref.current.scrollIntoView({
-        behavior: "smooth",
-        block: "nearest",
-        inline: "center",
-      });
-    }
-  }, [isActive]);
+  useEffect(() => {
+    if (ref.current && isActive && !isVisibleInViewportSide()) {
+      ref.current.scrollIntoView({
+        behavior: "smooth",
+        block: "nearest",
+        inline: "center",
+      });
+    }
+  }, [isActive]);

Also applies to: 28-31, 38-40, 57-66

components/user/layout/userTabs.config.ts (2)

6-85: Consider consolidating title and metaLabel if they remain identical.

Currently, title and metaLabel have identical values across all tabs. If they serve the same purpose, you could eliminate the duplication by either:

  • Removing one field and using the other consistently
  • Deriving metaLabel from title by default, with an optional override

Only implement this if the fields are confirmed to serve the same purpose long-term.


87-88: UserPageTabType alias may cause confusion.

The UserPageTabType alias duplicates UserPageTabKey. If this alias exists for backward compatibility during migration, consider documenting this or removing it once the migration is complete to avoid inconsistent usage across the codebase.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f53b04 and 94a0516.

📒 Files selected for processing (30)
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx (2 hunks)
  • __tests__/components/user/layout/UserPageTab.test.tsx (2 hunks)
  • __tests__/components/user/layout/UserPageTabs.test.tsx (3 hunks)
  • __tests__/components/user/utils/CommonProfileLink.test.tsx (2 hunks)
  • app/[user]/collected/page.tsx (1 hunks)
  • app/[user]/followers/page.tsx (1 hunks)
  • app/[user]/groups/page.tsx (1 hunks)
  • app/[user]/identity/page.tsx (2 hunks)
  • app/[user]/page.tsx (1 hunks)
  • app/[user]/proxy/page.tsx (1 hunks)
  • app/[user]/rep/page.tsx (2 hunks)
  • app/[user]/stats/page.tsx (1 hunks)
  • app/[user]/subscriptions/page.tsx (1 hunks)
  • app/[user]/waves/page.tsx (1 hunks)
  • app/[user]/xtdh/page.tsx (1 hunks)
  • components/header/header-search/HeaderSearchModal.tsx (2 hunks)
  • components/header/header-search/HeaderSearchModalItem.tsx (2 hunks)
  • components/profile-activity/list/items/ProfileActivityLogProxy.tsx (2 hunks)
  • components/profile-activity/list/items/ProfileActivityLogProxyAction.tsx (2 hunks)
  • components/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsx (2 hunks)
  • components/profile-activity/list/items/ProfileActivityLogProxyActionState.tsx (2 hunks)
  • components/profile-activity/list/items/ProfileActivityLogRate.tsx (2 hunks)
  • components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsx (2 hunks)
  • components/user/layout/UserPageTab.tsx (2 hunks)
  • components/user/layout/UserPageTabs.tsx (1 hunks)
  • components/user/layout/userTabs.config.ts (1 hunks)
  • components/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsx (2 hunks)
  • components/user/utils/CommonProfileLink.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdh.tsx (1 hunks)
  • helpers/Helpers.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props

Use TypeScript for implementation code

Files:

  • components/profile-activity/list/items/ProfileActivityLogRate.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxy.tsx
  • app/[user]/waves/page.tsx
  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsx
  • app/[user]/collected/page.tsx
  • app/[user]/rep/page.tsx
  • app/[user]/groups/page.tsx
  • __tests__/components/user/layout/UserPageTab.test.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyAction.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsx
  • app/[user]/page.tsx
  • app/[user]/proxy/page.tsx
  • __tests__/components/user/layout/UserPageTabs.test.tsx
  • components/user/layout/userTabs.config.ts
  • app/[user]/identity/page.tsx
  • components/header/header-search/HeaderSearchModalItem.tsx
  • app/[user]/stats/page.tsx
  • components/user/utils/CommonProfileLink.tsx
  • components/user/layout/UserPageTab.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyActionState.tsx
  • app/[user]/subscriptions/page.tsx
  • helpers/Helpers.ts
  • components/user/layout/UserPageTabs.tsx
  • app/[user]/followers/page.tsx
  • __tests__/components/user/utils/CommonProfileLink.test.tsx
  • components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsx
  • app/[user]/xtdh/page.tsx
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx
  • components/header/header-search/HeaderSearchModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/profile-activity/list/items/ProfileActivityLogRate.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxy.tsx
  • app/[user]/waves/page.tsx
  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsx
  • app/[user]/collected/page.tsx
  • app/[user]/rep/page.tsx
  • app/[user]/groups/page.tsx
  • __tests__/components/user/layout/UserPageTab.test.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyAction.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsx
  • app/[user]/page.tsx
  • app/[user]/proxy/page.tsx
  • __tests__/components/user/layout/UserPageTabs.test.tsx
  • app/[user]/identity/page.tsx
  • components/header/header-search/HeaderSearchModalItem.tsx
  • app/[user]/stats/page.tsx
  • components/user/utils/CommonProfileLink.tsx
  • components/user/layout/UserPageTab.tsx
  • components/profile-activity/list/items/ProfileActivityLogProxyActionState.tsx
  • app/[user]/subscriptions/page.tsx
  • components/user/layout/UserPageTabs.tsx
  • app/[user]/followers/page.tsx
  • __tests__/components/user/utils/CommonProfileLink.test.tsx
  • components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsx
  • app/[user]/xtdh/page.tsx
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx
  • components/header/header-search/HeaderSearchModal.tsx
{app,pages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use NextJS features that match the current version

Files:

  • app/[user]/waves/page.tsx
  • app/[user]/collected/page.tsx
  • app/[user]/rep/page.tsx
  • app/[user]/groups/page.tsx
  • app/[user]/page.tsx
  • app/[user]/proxy/page.tsx
  • app/[user]/identity/page.tsx
  • app/[user]/stats/page.tsx
  • app/[user]/subscriptions/page.tsx
  • app/[user]/followers/page.tsx
  • app/[user]/xtdh/page.tsx
app/**/page.tsx

📄 CodeRabbit inference engine (AGENTS.md)

All new pages must be created inside the app/ directory

Files:

  • app/[user]/waves/page.tsx
  • app/[user]/collected/page.tsx
  • app/[user]/rep/page.tsx
  • app/[user]/groups/page.tsx
  • app/[user]/page.tsx
  • app/[user]/proxy/page.tsx
  • app/[user]/identity/page.tsx
  • app/[user]/stats/page.tsx
  • app/[user]/subscriptions/page.tsx
  • app/[user]/followers/page.tsx
  • app/[user]/xtdh/page.tsx
app/**/{page,layout}.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Routes in app/ should export a generateMetadata function using getAppMetadata

Files:

  • app/[user]/waves/page.tsx
  • app/[user]/collected/page.tsx
  • app/[user]/rep/page.tsx
  • app/[user]/groups/page.tsx
  • app/[user]/page.tsx
  • app/[user]/proxy/page.tsx
  • app/[user]/identity/page.tsx
  • app/[user]/stats/page.tsx
  • app/[user]/subscriptions/page.tsx
  • app/[user]/followers/page.tsx
  • app/[user]/xtdh/page.tsx
**/{__tests__/**/*.{ts,tsx},*.test.tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/{__tests__/**/*.{ts,tsx},*.test.tsx}: Place tests in tests directories or alongside components as ComponentName.test.tsx
Mock external dependencies and APIs in tests

Files:

  • __tests__/components/user/layout/UserPageTab.test.tsx
  • __tests__/components/user/layout/UserPageTabs.test.tsx
  • __tests__/components/user/utils/CommonProfileLink.test.tsx
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx
__tests__/**

📄 CodeRabbit inference engine (tests/AGENTS.md)

Place Jest test suites under the __tests__ directory mirroring source folders (e.g., components, contexts, hooks, utils)

Files:

  • __tests__/components/user/layout/UserPageTab.test.tsx
  • __tests__/components/user/layout/UserPageTabs.test.tsx
  • __tests__/components/user/utils/CommonProfileLink.test.tsx
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use @testing-library/react and @testing-library/user-event for React component tests

Files:

  • __tests__/components/user/layout/UserPageTab.test.tsx
  • __tests__/components/user/layout/UserPageTabs.test.tsx
  • __tests__/components/user/utils/CommonProfileLink.test.tsx
  • __tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-28T12:32:36.083Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: AGENTS.md:0-0
Timestamp: 2025-09-28T12:32:36.083Z
Learning: Applies to **/{__tests__/**/*.{ts,tsx},*.test.tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/user/layout/UserPageTabs.test.tsx
🧬 Code graph analysis (28)
components/profile-activity/list/items/ProfileActivityLogRate.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
components/profile-activity/list/items/ProfileActivityLogProxy.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/waves/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
components/user/xtdh/UserPageXtdh.tsx (1)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/collected/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
app/[user]/rep/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
app/[user]/groups/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
__tests__/components/user/layout/UserPageTab.test.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
components/user/layout/UserPageTab.tsx (1)
  • UserPageTab (17-83)
components/profile-activity/list/items/ProfileActivityLogProxyAction.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
components/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
app/[user]/proxy/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
__tests__/components/user/layout/UserPageTabs.test.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/identity/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
components/header/header-search/HeaderSearchModalItem.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/stats/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
components/user/layout/UserPageTab.tsx (1)
components/user/layout/userTabs.config.ts (2)
  • UserPageTabConfig (6-12)
  • UserPageTabKey (87-87)
components/profile-activity/list/items/ProfileActivityLogProxyActionState.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/subscriptions/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
helpers/Helpers.ts (1)
components/user/layout/userTabs.config.ts (1)
  • getUserPageTabById (117-119)
components/user/layout/UserPageTabs.tsx (4)
components/user/layout/userTabs.config.ts (6)
  • DEFAULT_USER_PAGE_TAB (109-109)
  • UserPageVisibilityContext (1-4)
  • UserPageTabKey (87-87)
  • getUserPageTabByRoute (111-115)
  • UserPageTabConfig (6-12)
  • USER_PAGE_TABS (90-90)
components/cookies/CookieConsentContext.tsx (1)
  • useCookieConsent (43-50)
components/auth/Auth.tsx (1)
  • AuthContext (83-93)
components/user/layout/UserPageTab.tsx (1)
  • UserPageTab (17-83)
app/[user]/followers/page.tsx (2)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
__tests__/components/user/utils/CommonProfileLink.test.tsx (2)
components/user/utils/CommonProfileLink.tsx (1)
  • CommonProfileLink (8-38)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/xtdh/page.tsx (3)
components/user/layout/userTabs.config.ts (2)
  • USER_PAGE_TAB_MAP (92-99)
  • USER_PAGE_TAB_IDS (101-107)
app/[user]/_lib/userTabPageFactory.tsx (1)
  • createUserTabPage (21-75)
components/user/xtdh/UserPageXtdh.tsx (1)
  • UserPageXtdh (3-9)
__tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
components/header/header-search/HeaderSearchModal.tsx (1)
components/user/layout/userTabs.config.ts (1)
  • USER_PAGE_TAB_IDS (101-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (29)
components/header/header-search/HeaderSearchModalItem.tsx (1)

14-14: LGTM! Centralized config migration.

The change correctly migrates from the old UserPageTabType.IDENTITY to the centralized USER_PAGE_TAB_IDS.IDENTITY, maintaining the same behavior while aligning with the new config-driven approach.

Also applies to: 125-125

components/profile-activity/list/items/ProfileActivityLogProxy.tsx (1)

7-7: LGTM! Consistent migration pattern.

The change correctly adopts the centralized tab configuration for the PROXY tab identifier.

Also applies to: 20-20

components/user/utils/CommonProfileLink.tsx (1)

6-6: LGTM! Type import alignment.

The change correctly updates the type import source to the centralized config. Using type import is a TypeScript best practice that helps with tree-shaking.

app/[user]/rep/page.tsx (1)

10-13: LGTM! Centralized config adoption.

The refactor correctly replaces hardcoded route and label values with config-driven properties from TAB_CONFIG, improving maintainability and consistency across user pages.

Also applies to: 68-72

components/profile-activity/list/items/ProfileActivityLogProxyAction.tsx (1)

8-8: LGTM! Consistent refactor.

The change aligns with the centralized config pattern, matching similar updates in ProfileActivityLogProxy.tsx.

Also applies to: 22-22

components/header/header-search/HeaderSearchModal.tsx (1)

16-16: LGTM! Matches HeaderSearchModalItem pattern.

The change consistently applies the centralized config approach to the search modal, using USER_PAGE_TAB_IDS.IDENTITY as the default path, aligning with the similar change in HeaderSearchModalItem.tsx.

Also applies to: 131-131

app/[user]/xtdh/page.tsx (1)

1-17: Approve XTDH user tab page implementation

Follows established pattern and USER_PAGE_TAB_IDS.XTDH is defined, so no further changes needed.

components/profile-activity/list/items/ProfileActivityLogProxyActionState.tsx (1)

8-8: LGTM! Clean migration to centralized tab configuration.

The import source and tab ID reference have been correctly updated to use the centralized config. The logic remains unchanged.

Also applies to: 30-30

components/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsx (1)

6-6: LGTM! Consistent with the centralized tab configuration.

The changes correctly migrate from the enum to the config-based approach.

Also applies to: 22-22

app/[user]/waves/page.tsx (2)

4-7: LGTM! Good use of config-driven metadata.

The TAB_CONFIG pattern eliminates hard-coded route and metadata values, centralizing tab configuration.

Also applies to: 13-13


16-17: LGTM! Route and metadata now derived from config.

Using TAB_CONFIG.route and TAB_CONFIG.metaLabel ensures consistency with the centralized tab definition.

components/profile-activity/list/items/ProfileActivityLogRate.tsx (1)

4-4: LGTM! Tab targeting logic correctly migrated.

The conditional assignment of tabTarget based on rating_matter now uses the centralized config constants while preserving the original logic.

Also applies to: 82-85

__tests__/components/user/layout/UserPageTabs.test.tsx (2)

2-3: LGTM! Test mock correctly updated for config structure.

The mock now renders p.tab.id to match the new config object structure, and imports are correctly updated.

Also applies to: 19-19


55-57: LGTM! Test assertions correctly use centralized tab IDs.

The test expectations now reference USER_PAGE_TAB_IDS constants, maintaining test validity with the new config system.

Also applies to: 63-64

app/[user]/subscriptions/page.tsx (1)

4-7: LGTM! Consistent config-driven pattern for subscriptions tab.

The implementation follows the same centralized configuration approach as other user tab pages, deriving route and metadata from TAB_CONFIG.

Also applies to: 13-13, 16-17

components/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsx (1)

12-12: LGTM! Profile routing correctly uses centralized tab ID.

The defaultPath parameter now references USER_PAGE_TAB_IDS.STATS, maintaining the intended routing behavior.

Also applies to: 78-78

app/[user]/page.tsx (1)

3-6: LGTM! Main user page correctly adopts centralized config.

The Brain tab page now derives its route and metadata from TAB_CONFIG, completing the consistent migration pattern across all user tab pages.

Also applies to: 8-8, 11-12

app/[user]/collected/page.tsx (1)

3-12: LGTM! Clean refactor to centralized tab configuration.

The migration to centralized tab config eliminates hard-coded route and label strings, improving maintainability. Type-safe lookups via USER_PAGE_TAB_IDS and USER_PAGE_TAB_MAP ensure consistency across the codebase.

components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsx (1)

1-34: LGTM! Tab target logic correctly migrated to centralized config.

The switch from the old enum to USER_PAGE_TAB_IDS maintains the existing conditional logic while aligning with the centralized tab configuration pattern.

app/[user]/followers/page.tsx (1)

3-12: LGTM! Consistent migration to centralized tab config.

Follows the same clean refactor pattern as other user pages, replacing hard-coded values with config-driven metadata.

app/[user]/groups/page.tsx (1)

3-12: LGTM! Tab configuration correctly centralized.

Consistent refactor that eliminates hard-coded "groups" and "Groups" strings in favor of centralized configuration.

app/[user]/stats/page.tsx (1)

4-17: LGTM! Stats page successfully migrated to centralized config.

The refactor maintains the existing page structure while replacing hard-coded tab values with config-driven metadata.

__tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsx (1)

2-40: LGTM! Test correctly updated to match refactored component.

The test now references USER_PAGE_TAB_IDS.REP, aligning with the component's migration to centralized tab configuration.

app/[user]/identity/page.tsx (1)

9-67: LGTM! Identity page configuration centralized correctly.

The refactor follows the established pattern, replacing hard-coded "identity" and "Identity" strings with config-driven values while preserving all existing component logic.

__tests__/components/user/utils/CommonProfileLink.test.tsx (1)

4-23: LGTM! Test updated to use centralized tab identifiers.

Both test cases correctly reference USER_PAGE_TAB_IDS.COLLECTED, matching the refactored component interface.

app/[user]/proxy/page.tsx (1)

13-17: Good switch to config-driven route/metadata

Using USER_PAGE_TAB_MAP/USER_PAGE_TAB_IDS ensures single source of truth for route and metaLabel. Looks correct.

__tests__/components/user/layout/UserPageTab.test.tsx (1)

24-29: Tests updated correctly for id-based active state

Deriving repTab from the config and using activeTabId mirrors the new API. Assertions remain valid.

components/user/layout/userTabs.config.ts (2)

1-119: Well-structured tab configuration system.

The centralized configuration approach is solid:

  • Strong typing with UserPageTabKey derived from TABS ensures compile-time safety
  • Visibility context pattern enables conditional tab rendering
  • Utility functions provide convenient lookup methods
  • Use of as const satisfies maintains type safety while allowing inference

This refactoring successfully consolidates tab metadata into a single source of truth.


14-21: Empty route for Brain tab is intentional (maps to the default user page).

Comment thread helpers/Helpers.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94a0516 and 3395772.

📒 Files selected for processing (1)
  • __tests__/AGENTS.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
__tests__/**

📄 CodeRabbit inference engine (tests/AGENTS.md)

Place Jest test suites under the __tests__ directory mirroring source folders (e.g., components, contexts, hooks, utils)

Files:

  • __tests__/AGENTS.md
🧠 Learnings (2)
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Run `npm run test:cov:changed` for changed-file tests and coverage; use `npm run test` for full suite; ensure `npm run lint` and `npm run type-check` pass

Applied to files:

  • __tests__/AGENTS.md
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Applies to __tests__/components/**/*.{ts,tsx,js,jsx} : Use `testing-library/react` and `testing-library/user-event` for React component tests

Applied to files:

  • __tests__/AGENTS.md
🪛 markdownlint-cli2 (0.18.1)
__tests__/AGENTS.md

61-61: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


69-69: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


84-84: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


92-92: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


99-99: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


106-106: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


112-112: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


118-118: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


124-124: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


130-130: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)

Comment thread __tests__/AGENTS.md
Comment thread __tests__/AGENTS.md
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3395772 and 6723009.

⛔ Files ignored due to path filters (7)
  • generated/models/ApiCreateTdhGrant.ts is excluded by !**/generated/**
  • generated/models/ApiPageSortDirection.ts is excluded by !**/generated/**
  • generated/models/ApiTdhGrant.ts is excluded by !**/generated/**
  • generated/models/ApiTdhGrantStatus.ts is excluded by !**/generated/**
  • generated/models/ApiTdhGrantTargetChain.ts is excluded by !**/generated/**
  • generated/models/ApiTdhGrantsPage.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
📒 Files selected for processing (25)
  • app/api/profiles/[profile]/xtdh/stats/route.ts (1 hunks)
  • components/nft-picker/NftPicker.tsx (1 hunks)
  • components/nft-picker/NftPicker.types.ts (1 hunks)
  • components/nft-picker/useAlchemyClient.ts (1 hunks)
  • components/react-query-wrapper/ReactQueryWrapper.tsx (2 hunks)
  • components/user/xtdh/UserPageXtdh.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrant.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantAmount.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantSelection.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantSubmit.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantSummary.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantValidity.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGranted.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantedList.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhReceived.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhStatsHeader.tsx (1 hunks)
  • components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx (1 hunks)
  • components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx (1 hunks)
  • components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx (1 hunks)
  • components/user/xtdh/utils/xtdhGrantFormatters.ts (1 hunks)
  • hooks/useXtdhStats.ts (1 hunks)
  • openapi.yaml (7 hunks)
  • review-pr.sh (0 hunks)
  • services/6529api.ts (2 hunks)
  • types/xtdh.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • review-pr.sh
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props

Use TypeScript for implementation code

Files:

  • types/xtdh.ts
  • components/user/xtdh/UserPageXtdhReceived.tsx
  • components/user/xtdh/UserPageXtdhGrantSubmit.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantSelection.tsx
  • components/user/xtdh/UserPageXtdhGranted.tsx
  • components/user/xtdh/UserPageXtdhStatsHeader.tsx
  • components/user/xtdh/UserPageXtdhGrantAmount.tsx
  • components/user/xtdh/UserPageXtdh.tsx
  • components/nft-picker/NftPicker.tsx
  • components/user/xtdh/UserPageXtdhGrantSummary.tsx
  • app/api/profiles/[profile]/xtdh/stats/route.ts
  • services/6529api.ts
  • components/user/xtdh/utils/xtdhGrantFormatters.ts
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/nft-picker/useAlchemyClient.ts
  • components/user/xtdh/UserPageXtdhGrant.tsx
  • components/user/xtdh/UserPageXtdhGrantValidity.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx
  • components/nft-picker/NftPicker.types.ts
  • hooks/useXtdhStats.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/user/xtdh/UserPageXtdhReceived.tsx
  • components/user/xtdh/UserPageXtdhGrantSubmit.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantSelection.tsx
  • components/user/xtdh/UserPageXtdhGranted.tsx
  • components/user/xtdh/UserPageXtdhStatsHeader.tsx
  • components/user/xtdh/UserPageXtdhGrantAmount.tsx
  • components/user/xtdh/UserPageXtdh.tsx
  • components/nft-picker/NftPicker.tsx
  • components/user/xtdh/UserPageXtdhGrantSummary.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/user/xtdh/UserPageXtdhGrant.tsx
  • components/user/xtdh/UserPageXtdhGrantValidity.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx
{app,pages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use NextJS features that match the current version

Files:

  • app/api/profiles/[profile]/xtdh/stats/route.ts
app/api/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (app/api/AGENTS.md)

app/api/**/*.{ts,tsx,js,jsx}: Never call fetch directly with user-controlled or scraped URLs in API code; use @/lib/security/urlGuard helpers (parsePublicUrl, assertPublicUrl, fetchPublicUrl, fetchPublicJson) so every hop is validated
When custom headers or timeouts are needed for external requests, pass them via urlGuard helper options instead of rolling a custom wrapper

Files:

  • app/api/profiles/[profile]/xtdh/stats/route.ts
app/api/**/route.{ts,js}

📄 CodeRabbit inference engine (app/api/AGENTS.md)

app/api/**/route.{ts,js}: Catch UrlGuardError explicitly in route handlers if returning a tailored response; otherwise allow it to propagate so the correct status code surfaces
Export HTTP verb handlers (e.g., GET) from route.ts files
Keep route.ts logic in small internal functions when it grows beyond ~200 lines
For edge caching behavior, prefer export const dynamic = "force-dynamic" or revalidate constants rather than inline headers
Follow project default responses (NextResponse.json) and reuse existing util modules instead of duplicating logic

Files:

  • app/api/profiles/[profile]/xtdh/stats/route.ts
app/api/**/*.ts

📄 CodeRabbit inference engine (app/api/AGENTS.md)

Use TypeScript types for request parameters and responses; avoid any unless a third-party payload has no shape guarantees

Files:

  • app/api/profiles/[profile]/xtdh/stats/route.ts
🧬 Code graph analysis (17)
components/user/xtdh/UserPageXtdhGrantSubmit.tsx (1)
components/nft-picker/NftPicker.types.ts (2)
  • ContractOverview (24-27)
  • NftPickerSelection (43-51)
components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx (2)
generated/models/ApiTdhGrantsPage.ts (1)
  • ApiTdhGrantsPage (16-56)
components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx (1)
  • UserPageXtdhGrantListItem (12-34)
components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx (2)
generated/models/ApiTdhGrantsPage.ts (1)
  • ApiTdhGrantsPage (16-56)
components/user/xtdh/utils/xtdhGrantFormatters.ts (3)
  • formatTargetTokens (3-24)
  • formatAmount (45-48)
  • formatDateTime (26-43)
components/user/xtdh/UserPageXtdhGrantSelection.tsx (2)
components/nft-picker/NftPicker.types.ts (2)
  • ContractOverview (24-27)
  • NftPickerSelection (43-51)
components/nft-picker/NftPicker.tsx (1)
  • NftPicker (70-854)
components/user/xtdh/UserPageXtdhGranted.tsx (2)
components/user/xtdh/UserPageXtdhGrantedList.tsx (1)
  • UserPageXtdhGrantedList (17-75)
components/user/xtdh/UserPageXtdhGrant.tsx (1)
  • UserPageXtdhGrant (22-154)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (2)
hooks/useXtdhStats.ts (1)
  • useXtdhStats (34-56)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (98-115)
components/user/xtdh/UserPageXtdh.tsx (6)
components/utils/select/CommonSelect.tsx (2)
  • CommonSelectItem (11-17)
  • CommonSelect (57-66)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-169)
components/auth/Auth.tsx (1)
  • AuthContext (83-93)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (1)
  • UserPageXtdhStatsHeader (10-136)
components/user/xtdh/UserPageXtdhGranted.tsx (1)
  • UserPageXtdhGranted (6-33)
components/user/xtdh/UserPageXtdhReceived.tsx (1)
  • UserPageXtdhReceived (3-9)
components/nft-picker/NftPicker.tsx (9)
components/nft-picker/NftPicker.types.ts (8)
  • SupportedChain (3-3)
  • TokenSelection (9-9)
  • TokenRange (7-7)
  • Suggestion (11-22)
  • ContractOverview (24-27)
  • NftPickerProps (60-76)
  • ParseError (78-84)
  • NftPickerSelection (43-51)
components/nft-picker/NftPicker.utils.ts (10)
  • toCanonicalRanges (366-384)
  • formatCanonical (407-415)
  • parseTokenExpressionToRanges (247-278)
  • formatBigIntWithSeparators (417-436)
  • mergeCanonicalRanges (301-312)
  • removeTokenFromRanges (314-344)
  • fromCanonicalRanges (386-405)
  • isRangeTooLargeError (43-49)
  • tryToNumberArray (438-452)
  • MAX_SAFE (7-7)
components/nft-picker/useAlchemyClient.ts (3)
  • useContractOverviewQuery (164-201)
  • useCollectionSearch (107-162)
  • primeContractCache (264-277)
helpers/Helpers.ts (1)
  • isValidEthAddress (218-219)
components/nft-picker/NftSuggestList.tsx (1)
  • NftSuggestList (39-191)
components/nft-picker/NftContractHeader.tsx (1)
  • NftContractHeader (18-102)
components/nft-picker/AllTokensSelectedCard.tsx (1)
  • AllTokensSelectedCard (12-44)
components/nft-picker/NftEditRanges.tsx (1)
  • NftEditRanges (44-223)
components/nft-picker/NftTokenList.tsx (1)
  • NftTokenList (55-216)
components/user/xtdh/UserPageXtdhGrantSummary.tsx (2)
components/user/xtdh/utils/xtdhGrantFormatters.ts (1)
  • formatDateTime (26-43)
components/nft-picker/NftPicker.types.ts (2)
  • ContractOverview (24-27)
  • NftPickerSelection (43-51)
app/api/profiles/[profile]/xtdh/stats/route.ts (1)
types/xtdh.ts (2)
  • XtdhStatsError (9-13)
  • XtdhStatsResponse (1-7)
services/6529api.ts (3)
entities/IDBResponse.ts (1)
  • DBResponse (1-6)
services/auth/auth.utils.ts (1)
  • getStagingAuth (77-79)
constants.ts (1)
  • API_AUTH_COOKIE (39-39)
components/user/xtdh/utils/xtdhGrantFormatters.ts (1)
components/nft-picker/NftPicker.utils.ts (1)
  • parseTokenExpressionToRanges (247-278)
components/nft-picker/useAlchemyClient.ts (2)
services/alchemy-api.ts (5)
  • SearchContractsResult (571-571)
  • TokenMetadataParams (571-571)
  • searchNftCollections (294-326)
  • getContractOverview (328-376)
  • getTokensMetadata (417-456)
components/nft-picker/NftPicker.types.ts (4)
  • ContractOverview (24-27)
  • TokenMetadata (29-35)
  • SupportedChain (3-3)
  • Suggestion (11-22)
components/user/xtdh/UserPageXtdhGrant.tsx (6)
components/nft-picker/NftPicker.types.ts (2)
  • ContractOverview (24-27)
  • NftPickerSelection (43-51)
components/auth/Auth.tsx (1)
  • AuthContext (83-93)
generated/models/ApiCreateTdhGrant.ts (1)
  • ApiCreateTdhGrant (16-76)
services/api/common-api.ts (1)
  • commonApiPost (166-191)
generated/models/ApiTdhGrant.ts (1)
  • ApiTdhGrant (18-120)
components/user/xtdh/UserPageXtdhGrantSelection.tsx (1)
  • UserPageXtdhGrantSelection (9-27)
components/user/xtdh/UserPageXtdhGrantedList.tsx (3)
services/api/common-api.ts (1)
  • commonApiFetch (18-45)
generated/models/ApiTdhGrantsPage.ts (1)
  • ApiTdhGrantsPage (16-56)
components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx (1)
  • UserPageXtdhGrantedListContent (15-55)
components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx (2)
generated/models/ApiTdhGrantsPage.ts (1)
  • ApiTdhGrantsPage (16-56)
components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx (1)
  • UserPageXtdhGrantList (8-18)
hooks/useXtdhStats.ts (1)
types/xtdh.ts (2)
  • XtdhStatsResponse (1-7)
  • XtdhStatsError (9-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
services/6529api.ts (3)

76-78: LGTM!

The 401 handling has been correctly added, matching the pattern used in fetchUrl and postData.


40-53: Header and method precedence are correct as written. Spreading init before explicit method: "POST" and headers correctly enforces the intended overrides.


10-18: Remove header override warning. In fetch(url, { ...init, headers }), the explicit headers property (placed after ...init) always overrides any init.headers, so the auth header cannot be bypassed.

Likely an incorrect or invalid review comment.

components/user/xtdh/UserPageXtdhGranted.tsx (1)

6-33: LGTM!

The component correctly follows coding guidelines: client component, readonly props, functional component with hooks pattern, and TailwindCSS styling. The conditional rendering logic for the grant creation UI is appropriate.

types/xtdh.ts (1)

1-13: LGTM!

The type definitions are clean and follow best practices with readonly modifiers on all fields. The error interface appropriately includes statusCode for proper error handling.

components/user/xtdh/UserPageXtdhGrantSelection.tsx (1)

9-27: LGTM!

The component correctly follows coding guidelines (client component, readonly props, functional component) and properly configures the NftPicker with appropriate flags for the grant selection use case.

components/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsx (1)

12-34: LGTM!

The component follows all coding guidelines: exported interface with readonly props, proper use of formatting utilities, TailwindCSS styling, and functional component pattern. The null handling for valid_to at line 30 is appropriate.

components/user/xtdh/granted-list/UserPageXtdhGrantList.tsx (1)

8-18: LGTM!

The component correctly follows coding guidelines with readonly props and functional component pattern. The use of grant.id as the key (line 14) is appropriate assuming the API guarantees unique IDs.

components/user/xtdh/UserPageXtdhGrantAmount.tsx (1)

1-37: LGTM!

The component correctly follows coding guidelines (readonly props, functional component, TailwindCSS) and implements proper numeric input handling with appropriate constraints (min="0", step="any", inputMode="decimal"). The null handling and NaN checking in the onChange handler (lines 25-31) are correct.

app/api/profiles/[profile]/xtdh/stats/route.ts (1)

81-83: No edge runtime configured; using Node.js by default
This route doesn’t export a runtime, so it runs on Node.js where setTimeout is supported. If you switch to edge runtime later, ensure delays are acceptable.

Comment thread app/api/profiles/[profile]/xtdh/stats/route.ts Outdated
Comment thread app/api/profiles/[profile]/xtdh/stats/route.ts Outdated
Comment thread components/user/xtdh/UserPageXtdhGrant.tsx Outdated
Comment thread components/user/xtdh/UserPageXtdhGrantValidity.tsx Outdated
Comment thread openapi.yaml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (2)

138-146: Consider using the exponentiation operator.

The logic is correct, but you can use the modern exponentiation operator for brevity.

Apply this diff:

-  const factor = Math.pow(10, decimals);
+  const factor = 10 ** decimals;

207-225: Remove unnecessary arrow function wrapper.

The error component is functional, but the onClick handler can be simplified.

Apply this diff:

-        onClick={() => onRetry()}
+        onClick={onRetry}
components/user/xtdh/UserPageXtdhReceived.tsx (1)

3-3: Add explicit return type annotation.

Consider adding an explicit return type JSX.Element to the function declaration for better type safety and clarity.

Apply this diff:

-export default function UserPageXtdhReceived() {
+export default function UserPageXtdhReceived(): JSX.Element {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6723009 and ac00de1.

📒 Files selected for processing (3)
  • components/user/xtdh/UserPageXtdhReceived.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhStatsHeader.tsx (1 hunks)
  • components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props

Use TypeScript for implementation code

Files:

  • components/user/xtdh/UserPageXtdhStatsHeader.tsx
  • components/user/xtdh/UserPageXtdhReceived.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/user/xtdh/UserPageXtdhStatsHeader.tsx
  • components/user/xtdh/UserPageXtdhReceived.tsx
🧬 Code graph analysis (1)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (2)
hooks/useXtdhStats.ts (1)
  • useXtdhStats (34-56)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (98-115)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (5)

1-8: LGTM!

Clean imports and the interface correctly uses readonly as per coding guidelines.


10-47: LGTM!

The component logic is well-structured with proper error handling, loading states, and defensive calculations. The derived metrics (xtdhRate, xtdhRateAvailable, grantingPercentage, clampedPercentage) are computed correctly with appropriate guards against edge cases like division by zero and negative values.


49-135: Excellent accessibility and semantic HTML!

The component demonstrates strong accessibility practices with proper ARIA attributes, semantic roles, and keyboard navigation support. The progress bar implementation (lines 94-102) is particularly well-done with comprehensive ARIA properties.


148-176: LGTM!

The StatCard component is well-structured with proper props typing and accessibility attributes (tooltip via title and keyboard focus via tabIndex).


178-200: LGTM!

The skeleton loader accurately mirrors the component's structure and provides a good loading experience with the pulse animation.

components/user/xtdh/UserPageXtdhReceived.tsx (1)

6-9: Verify if the hardcoded "no items" message is intentional.

The message states "Currently, there are no items receiving xTDH" as a hardcoded string. If this component is meant to display dynamic data in the future, this could be misleading to users when items do exist. Ensure this placeholder approach aligns with the intended user experience for the xTDH received feature.

Comment thread components/user/xtdh/UserPageXtdhReceived.tsx Outdated
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🧹 Nitpick comments (37)
components/xtdh/stats-overview/ErrorState.tsx (2)

11-17: Consider adding an aria-live region for screen reader accessibility.

The error section should announce itself to screen readers when it appears. Add aria-live="polite" and role="alert" to the section element to improve accessibility.

Apply this diff:

-    <section className="tw-rounded-2xl tw-border tw-border-rose-500/40 tw-bg-rose-900/20 tw-p-6 tw-text-iron-50 tw-space-y-4">
+    <section 
+      className="tw-rounded-2xl tw-border tw-border-rose-500/40 tw-bg-rose-900/20 tw-p-6 tw-text-iron-50 tw-space-y-4"
+      role="alert"
+      aria-live="polite"
+    >

18-24: Consider adding disabled/loading state to the Retry button.

When onRetry is clicked, the button should ideally show a loading state or be disabled to prevent multiple simultaneous retry attempts. However, since the parent component controls the retry logic, this may be handled upstream.

components/xtdh/received/subcomponents/XtdhReceivedCollectionsSkeleton.tsx (1)

1-1: Evaluate if 'use client' directive is necessary.

This component doesn't use any client-side hooks (useState, useEffect), event handlers, or browser APIs. Consider removing the 'use client' directive to allow it to render as a server component, which would improve performance.

If this component is always rendered within a client component boundary, the directive is redundant. Remove it unless there's a specific reason for explicit client-side rendering.

components/xtdh/received/subcomponents/XtdhReceivedGranterRow.tsx (1)

22-27: Prefer next/image for avatars to avoid CLS and get built-in optimization

Switch to Next.js Image and set explicit width/height matching Tailwind sizing.

+import Image from "next/image";
@@
-        <img
-          src={granter.profileImage}
-          alt={`${granter.displayName} avatar`}
-          className="tw-h-9 tw-w-9 tw-rounded-full tw-object-cover tw-border tw-border-iron-700"
-          loading="lazy"
-        />
+        <Image
+          src={granter.profileImage}
+          alt={`${granter.displayName} avatar`}
+          width={36}
+          height={36}
+          className="tw-h-9 tw-w-9 tw-rounded-full tw-object-cover tw-border tw-border-iron-700"
+          loading="lazy"
+        />
components/xtdh/stats-overview/Skeletons.tsx (1)

3-3: Improve skeleton accessibility (announce loading, hide decoration)

Add aria-busy on the container and aria-hidden on decorative skeleton wrappers.

-    <section className="tw-rounded-2xl tw-border tw-border-iron-700 tw-bg-iron-950 tw-p-6 tw-shadow-md tw-shadow-black/30 tw-text-iron-50">
+    <section aria-busy="true" className="tw-rounded-2xl tw-border tw-border-iron-700 tw-bg-iron-950 tw-p-6 tw-shadow-md tw-shadow-black/30 tw-text-iron-50">
@@
-    <div className="tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-5 tw-space-y-5 tw-animate-pulse">
+    <div aria-hidden="true" className="tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-5 tw-space-y-5 tw-animate-pulse">
@@
-    <div className="tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-5 tw-flex tw-flex-col tw-gap-4 tw-animate-pulse">
+    <div aria-hidden="true" className="tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-5 tw-flex tw-flex-col tw-gap-4 tw-animate-pulse">

Also applies to: 21-21, 48-48

components/utils/select/dropdown/CommonDropdown.tsx (1)

26-27: Add aria-expanded; verify new prop typing; consider simplifying label state

  • a11y: expose expanded state on the button.
  • Type: ensure CommonSelectProps includes closeOnSelect.
  • Optional: use computedLabel directly (drop label state) and map items directly if items is already an array.
           type="button"
           aria-haspopup="true"
           aria-label={`${filterLabel}: ${label}`}
+          aria-expanded={isOpen}

Also confirm CommonSelectProps<T, U> declares:

  • closeOnSelect?: boolean

If items is an array, prefer:

  • items.map(...) instead of Object.values(items).map(...)

Also applies to: 40-55, 56-59, 127-131, 176-189

components/user/xtdh/user-page-xtdh-granted-list/constants.ts (1)

21-26: Make item arrays immutable to prevent accidental mutation

Mark BASE_STATUS_ITEMS and SORT_ITEMS as readonly.

-export const BASE_STATUS_ITEMS: CommonSelectItem<GrantedFilterStatus>[] = [
+export const BASE_STATUS_ITEMS = [
   { key: "ALL", label: STATUS_LABELS.ALL, value: "ALL" },
   { key: "PENDING", label: STATUS_LABELS.PENDING, value: "PENDING" },
   { key: "FAILED", label: STATUS_LABELS.FAILED, value: "FAILED" },
   { key: "GRANTED", label: STATUS_LABELS.GRANTED, value: "GRANTED" },
-];
+] as const;
@@
-export const SORT_ITEMS: CommonSelectItem<GrantedSortField>[] = [
+export const SORT_ITEMS = [
   { key: "created_at", label: "Created At", value: "created_at" },
   { key: "valid_from", label: "Valid From", value: "valid_from" },
   { key: "valid_to", label: "Valid To", value: "valid_to" },
   { key: "tdh_rate", label: "TDH Rate", value: "tdh_rate" },
-];
+] as const;

If you rely on mutating these arrays elsewhere, keep as-is. Otherwise, readonly avoids accidental changes. Based on guidelines.

Also applies to: 28-33

services/alchemy-api.ts (1)

510-518: Replace recursion with an iterative loop and validate inputs (safer, avoids stack growth)

  • Iterative pagination avoids unbounded recursion depth.
  • Optionally validate contract/owner before calling Alchemy to fail fast on bad input.
 export async function getNftsForContractAndOwner(
   chainId: number,
   contract: string,
   owner: string,
   nfts?: any[],
-  pageKey?: string,
-  retries = 0,
+  pageKey?: string,
+  retries = 0,
   signal?: AbortSignal
 ) {
-  if (!nfts) {
-    nfts = [];
-  }
+  if (!nfts) nfts = [];
+  // Optional: if owner is always an address, validate both
+  // if (!isValidEthAddress(contract) || !isValidEthAddress(owner)) {
+  //   throw new Error("Invalid address");
+  // }
   let path = "eth-mainnet";
   if (chainId === sepolia.id) {
     path = "eth-sepolia";
   } else if (chainId === goerli.id) {
     path = "eth-goerli";
   }
 
-  let url = `https://${path}.g.alchemy.com/nft/v3/${publicEnv.ALCHEMY_API_KEY}/getNFTsForOwner?owner=${owner}&contractAddresses[]=${contract}`;
-  if (pageKey) {
-    url += `&pageKey=${pageKey}`;
-  }
-  const response = await fetchLegacyUrl(url, signal);
-  if (response.error) {
-    if (retries >= MAX_GET_NFTS_RETRIES) {
-      throw new Error("Failed to fetch NFTs for owner after retries");
-    }
-    await delayWithAbort(250 * (retries + 1), signal);
-    return getNftsForContractAndOwner(
-      chainId,
-      contract,
-      owner,
-      nfts,
-      pageKey,
-      retries + 1,
-      signal
-    );
-  }
-  nfts = [...nfts].concat(response.ownedNfts);
-  if (response.pageKey) {
-    return getNftsForContractAndOwner(
-      chainId,
-      contract,
-      owner,
-      nfts,
-      response.pageKey,
-      retries,
-      signal
-    );
-  }
+  let nextPageKey = pageKey;
+  let attempts = retries;
+  while (true) {
+    let url = `https://${path}.g.alchemy.com/nft/v3/${publicEnv.ALCHEMY_API_KEY}/getNFTsForOwner?owner=${owner}&contractAddresses[]=${contract}`;
+    if (nextPageKey) {
+      url += `&pageKey=${nextPageKey}`;
+    }
+    const response = await fetchLegacyUrl(url, signal);
+    if (response.error) {
+      if (attempts >= MAX_GET_NFTS_RETRIES) {
+        throw new Error("Failed to fetch NFTs for owner after retries");
+      }
+      attempts += 1;
+      await delayWithAbort(250 * attempts, signal);
+      continue;
+    }
+    nfts = nfts.concat(response.ownedNfts ?? []);
+    if (response.pageKey) {
+      nextPageKey = response.pageKey;
+      continue;
+    }
+    break;
+  }
 
   const allNfts = nfts.map((nft) => {
     return {
       tokenId: nft.tokenId,
       tokenType: nft.tokenType,
       name: nft.name,
       tokenUri: nft.tokenUri,
       image: nft.image,
     };
   });
   return allNfts;
 }

If owners can be ENS names, skip the address validation and resolve upstream. Please confirm expected owner format.

Also applies to: 529-572

components/utils/input/collections/CollectionsAutocomplete.tsx (1)

3-15: Replace the “×” glyph with FontAwesome to match icon guidelines

Use FontAwesomeIcon for the remove control instead of a raw character.

As per coding guidelines

@@
 import {
   useCallback,
   useEffect,
   useId,
   useMemo,
   useRef,
   useState,
   type ChangeEvent,
   type KeyboardEvent,
 } from "react";
 import { useClickAway } from "react-use";
 import { classNames } from "@/helpers/Helpers";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faXmark } from "@fortawesome/free-solid-svg-icons";
@@
               <button
                 type="button"
                 className="tw-inline-flex tw-h-4 tw-w-4 tw-items-center tw-justify-center tw-rounded-full tw-border tw-border-transparent tw-bg-transparent tw-text-iron-400 hover:tw-text-error tw-transition tw-duration-200 tw-ease-out"
                 onClick={() => handleRemove(option.id)}
                 aria-label={`Remove ${option.name}`}
               >
-                ×
+                <FontAwesomeIcon icon={faXmark} aria-hidden="true" />
               </button>

Also applies to: 241-249

app/api/xtdh/stats/route.ts (1)

1-20: Consider forcing dynamic for fresh stats responses

Add a dynamic directive to avoid static optimization/caching of this route.

As per coding guidelines

 import { NextResponse } from "next/server";
 import { getOverviewStats } from "../mock-data";
 
+export const dynamic = "force-dynamic";
+
 export async function GET() {
   try {
     const stats = getOverviewStats();
     return NextResponse.json(stats);
components/xtdh/received/subcomponents/XtdhReceivedCollectionCard.tsx (1)

3-8: Use FontAwesome for expand/collapse indicator

Swap text glyphs for FontAwesome icons to follow icon standards.

As per coding guidelines

@@
-import type { XtdhReceivedCollectionSummary, XtdhReceivedNft } from "@/types/xtdh";
+import type { XtdhReceivedCollectionSummary, XtdhReceivedNft } from "@/types/xtdh";
 import { formatXtdhRate, formatXtdhTotal } from "../utils";
 import { XtdhReceivedGranterAvatarGroup } from "./XtdhReceivedGranterAvatarGroup";
 import { XtdhReceivedNftCard } from "./XtdhReceivedNftCard";
 import { XtdhReceivedEmptyState } from "./XtdhReceivedEmptyState";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
@@
             <span
               className="tw-ml-auto tw-inline-flex tw-h-8 tw-w-8 tw-items-center tw-justify-center tw-rounded-full tw-border tw-border-iron-700 tw-bg-iron-850 tw-text-sm tw-text-iron-200"
               aria-hidden
             >
-              {expanded ? "−" : "+"}
+              <FontAwesomeIcon icon={expanded ? faMinus : faPlus} />
             </span>

Also applies to: 66-75

__tests__/components/user/xtdh/UserPageXtdh.test.tsx (1)

60-80: Consider expanding test coverage.

The test suite currently has only one test case. Consider adding tests for:

  • Initial tab state when no tab param is present
  • Rendering the correct view component based on active tab
  • canGrant permission logic for different user scenarios
  • Stats header rendering with correct profile ID
components/xtdh/received/subcomponents/XtdhReceivedEmptyState.tsx (1)

18-24: Consider adding an aria-label to the button for better accessibility.

While the visible text is provided via actionLabel, adding an explicit aria-label would improve screen reader experience, especially when actionLabel is generic (e.g., "Try again").

Apply this diff:

         <button
           type="button"
           onClick={onAction}
+          aria-label={`${actionLabel}: ${message}`}
           className="tw-inline-flex tw-items-center tw-justify-center tw-rounded-lg tw-border tw-border-iron-700 tw-bg-iron-900 tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-text-iron-50 hover:tw-bg-iron-800 tw-transition tw-duration-300 tw-ease-out"
         >
hooks/useXtdhOverview.ts (1)

6-23: Consolidate duplicate fetchJson implementations
fetchJson is defined in both hooks/useXtdhOverview.ts and app/api/pepe/resolve/route.ts; extract a single shared utility to avoid duplication.

__tests__/components/user/xtdh/UserPageXtdhReceived.test.tsx (1)

130-143: Consider using userEvent for consistency.

The test correctly validates filter clearing behavior, but line 141 uses synchronous clearButton.click() instead of userEvent.click() like in the previous test. For consistency and more realistic user interaction simulation, consider using userEvent throughout.

Apply this diff:

-    clearButton.click();
+    await user.click(clearButton);
     expect(mockPush).toHaveBeenCalled();

And add const user = userEvent.setup(); at the beginning of the test.

components/xtdh/received/subcomponents/XtdhReceivedErrorState.tsx (1)

8-26: Consider adding aria-live for better accessibility.

The component is well-implemented, but the error message could benefit from an aria-live="polite" region to announce errors to screen reader users.

Apply this diff:

-    <div className="tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-3 tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-950 tw-p-8 tw-text-center">
-      <p className="tw-text-sm tw-text-rose-200 tw-m-0">
+    <div 
+      className="tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-3 tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-950 tw-p-8 tw-text-center"
+      role="alert"
+      aria-live="polite"
+    >
+      <p className="tw-text-sm tw-text-rose-200 tw-m-0">
         {message ?? "We couldn't load your received xTDH yet."}
       </p>
components/xtdh/received/subcomponents/XtdhReceivedGranterAvatarGroup.tsx (1)

21-27: Use next/image for avatars to reduce CLS and enable optimization

Swap <img> for next/image and add fixed dimensions. This aligns with Next best practices and improves performance.

Ensure remote image domains are allowed in next.config images.domains.

+import Image from "next/image";
@@
-          <img
+          <Image
             key={granter.profileId}
             src={granter.profileImage}
             alt={`${granter.displayName} avatar`}
-            className="tw-h-7 tw-w-7 tw-rounded-full tw-border tw-border-iron-800 tw-object-cover"
-            loading="lazy"
+            width={28}
+            height={28}
+            loading="lazy"
+            className="tw-h-7 tw-w-7 tw-rounded-full tw-border tw-border-iron-800 tw-object-cover"
           />

As per coding guidelines

app/api/profiles/[profile]/xtdh/received/nfts/route.ts (3)

13-20: Mark route as dynamic to avoid caching randomized responses

The handler uses Math.random() and artificial delay; add dynamic = "force-dynamic" so responses aren’t cached.

@@
 } from "../mock-data";
 
+export const dynamic = "force-dynamic";
+
 const RATE_LIMIT_PROBABILITY = 0.05;

As per coding guidelines


116-118: Prefer request.nextUrl.searchParams over constructing a new URL

Slightly cleaner and avoids redundant parsing.

-  const url = new URL(request.url);
-  const params = url.searchParams;
+  const params = request.nextUrl.searchParams;

75-81: Align context params typing with Next route handler signature

params isn’t a Promise in Next route handlers. Remove Promise<> and the await.

-export async function GET(
-  request: NextRequest,
-  context: { params: Promise<{ profile: string }> }
-) {
-  const { profile } = await context.params;
+export async function GET(
+  request: NextRequest,
+  context: { params: { profile: string } }
+) {
+  const { profile } = context.params;
components/xtdh/received/index.tsx (1)

17-19: Wrap props with Readonly<> for consistency with project style

Other components use Readonly<...> on the function signature; mirror that here.

-export function XtdhReceivedSection({
-  profileId,
-}: XtdhReceivedSectionProps) {
+export function XtdhReceivedSection({
+  profileId,
+}: Readonly<XtdhReceivedSectionProps>) {

As per coding guidelines

components/user/xtdh/UserPageXtdh.tsx (2)

74-81: Preserve existing query params when defaulting the tab

Creating a fresh URLSearchParams drops other params. Start from current search params to avoid losing state.

Apply this diff:

-    const params = new URLSearchParams();
+    const params = new URLSearchParams(searchParams?.toString());
     params.set("tab", DEFAULT_FILTER);
     const queryString = params.toString();

-    router.replace(queryString ? `${pathname}?${queryString}` : pathname, {
+    router.replace(queryString ? `${pathname}?${queryString}` : pathname, {
       scroll: false,
     });

91-98: Preserve other query params on tab changes

Same issue here: replacing with only "tab" clears other params. Merge with existing instead.

Apply this diff:

-      const params = new URLSearchParams();
-      params.set("tab", filter);
+      const params = new URLSearchParams(searchParams?.toString());
+      params.set("tab", filter);
 
       const queryString = params.toString();
       router.replace(queryString ? `${pathname}?${queryString}` : pathname, {
         scroll: false,
       });
components/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsx (2)

27-33: Align terminology: use “xTDH”

Use “xTDH” consistently in messaging.

Apply this diff:

-        Unable to load TDH grants for this profile.
+        Unable to load xTDH grants for this profile.

47-55: Use friendly status labels instead of lowercasing enum

Map status to a readable label to avoid “failed”/“pending” in lowercase.

Apply this diff:

-    if (status !== "ALL") {
-      const statusLabel = status.toLowerCase();
+    if (status !== "ALL") {
+      const statusLabel = ({ ALL: "All", PENDING: "Pending", FAILED: "Failed", GRANTED: "Granted" } as const)[status];
       return (
         <GrantedListMessage>
           No {statusLabel} grants found. Try a different filter.
         </GrantedListMessage>
       );
     }
components/xtdh/received/subcomponents/XtdhReceivedCollectionsView.tsx (1)

85-92: Disable sorting while refetching

Prevent toggling during background fetch for smoother UX.

Apply this diff:

   <CommonSelect<XtdhCollectionsSortField>
     items={XTDH_COLLECTION_SORT_ITEMS}
     activeItem={activeSort}
     filterLabel="Sort collections"
     setSelected={handleSortChange}
     sortDirection={activeDirection}
-    disabled={isLoading}
+    disabled={isLoading || isFetching}
   />
components/xtdh/received/subcomponents/XtdhReceivedNftsView.tsx (2)

83-91: Disable sorting while fetching to avoid jittery state.

Also disable the select when fetching.

Apply:

-              disabled={isLoading}
+              disabled={isLoading || isFetching}

134-141: Ensure list keys are unique across collections.

Using tokenId alone may collide across different contracts/collections. Prefer a composite key (e.g., contractAddress-tokenId) if available.

components/xtdh/stats-overview/useXtdhStatsOverviewData.ts (1)

68-79: Treat disabled state as loading to avoid false error.

When enabled is false, don’t emit an error for missing data; return loading.

Apply:

   const networkState: NetworkState = useMemo(() => {
+    if (!enabled) {
+      return { kind: "loading" };
+    }
     if (isLoading && !data) {
       return { kind: "loading" };
     }
 
     if (!networkStats || isError) {
       const message = error instanceof Error ? error.message : undefined;
       return { kind: "error", message: message ?? DEFAULT_ERROR_MESSAGE };
     }
 
     return { kind: "ready", stats: networkStats };
-  }, [isLoading, data, networkStats, isError, error]);
+  }, [enabled, isLoading, data, networkStats, isError, error]);
app/api/profiles/[profile]/xtdh/received/collections/route.ts (1)

75-81: Use Next’s standard context params (not a Promise) and remove await

Next.js provides context.params as a plain object. Align types and remove the unnecessary await.

-export async function GET(
-  request: NextRequest,
-  context: { params: Promise<{ profile: string }> }
-) {
-  const { profile } = await context.params;
+export async function GET(
+  request: NextRequest,
+  context: { params: { profile: string } }
+) {
+  const { profile } = context.params;

As per coding guidelines

hooks/useXtdhReceived.ts (2)

36-41: Support request cancellation via AbortSignal from react-query

Pass react-query’s AbortSignal to fetch to avoid unnecessary work on fast navigation.

-async function fetchWithErrorHandling<T>(
-  endpoint: string
-): Promise<T> {
-  const response = await fetch(endpoint);
+async function fetchWithErrorHandling<T>(
+  endpoint: string,
+  init?: RequestInit
+): Promise<T> {
+  const response = await fetch(endpoint, init);
@@
-  return (await response.json()) as T;
+  return (await response.json()) as T;
-    queryFn: async () =>
-      await fetchWithErrorHandling<XtdhReceivedCollectionsResponse>(
-        buildCollectionsUrl(options)
-      ),
+    queryFn: async ({ signal }) =>
+      await fetchWithErrorHandling<XtdhReceivedCollectionsResponse>(
+        buildCollectionsUrl(options),
+        { signal }
+      ),
-    queryFn: async () =>
-      await fetchWithErrorHandling<XtdhReceivedNftsResponse>(
-        buildNftsUrl(options)
-      ),
+    queryFn: async ({ signal }) =>
+      await fetchWithErrorHandling<XtdhReceivedNftsResponse>(
+        buildNftsUrl(options),
+        { signal }
+      ),

As per coding guidelines

Also applies to: 56-57, 162-166, 193-197


153-161: Stabilize query keys without JSON.stringify

Prefer explicit parts to avoid accidental cache misses due to object key ordering.

-    queryKey: [
-      QueryKey.XTDH_RECEIVED_COLLECTIONS,
-      profile,
-      sort,
-      dir,
-      page,
-      pageSize ?? null,
-      JSON.stringify(filters ?? {}),
-    ],
+    queryKey: [
+      QueryKey.XTDH_RECEIVED_COLLECTIONS,
+      profile,
+      sort,
+      dir,
+      page,
+      pageSize ?? null,
+      filters?.collections?.join("|") ?? null,
+      filters?.minRate ?? null,
+      filters?.minGrantors ?? null,
+    ],
-    queryKey: [
-      QueryKey.XTDH_RECEIVED_NFTS,
-      profile,
-      sort,
-      dir,
-      page,
-      pageSize ?? null,
-      JSON.stringify(filters ?? {}),
-    ],
+    queryKey: [
+      QueryKey.XTDH_RECEIVED_NFTS,
+      profile,
+      sort,
+      dir,
+      page,
+      pageSize ?? null,
+      filters?.collections?.join("|") ?? null,
+      filters?.minRate ?? null,
+      filters?.minGrantors ?? null,
+    ],

As per coding guidelines

Also applies to: 184-192

components/xtdh/received/hooks/useXtdhReceivedNftsState.ts (1)

176-183: Clamp page updates to valid range

Prevent out-of-range page navigation when totalCount changes or user jumps.

-  const handlePageChange = useCallback(
-    (nextPage: number) => {
-      handleUpdateParams((params) => {
-        params.set("page", nextPage.toString());
-      });
-    },
-    [handleUpdateParams]
-  );
+  const handlePageChange = useCallback(
+    (nextPage: number) => {
+      const target = Math.max(1, Math.min(nextPage, totalPages));
+      handleUpdateParams((params) => {
+        params.set("page", target.toString());
+      });
+    },
+    [handleUpdateParams, totalPages]
+  );

As per coding guidelines

components/xtdh/received/hooks/useXtdhReceivedCollectionsState.ts (1)

180-187: Clamp page updates to valid range

Avoid invalid page transitions when dataset size changes.

-  const handlePageChange = useCallback(
-    (nextPage: number) => {
-      handleUpdateParams((params) => {
-        params.set("page", nextPage.toString());
-      });
-    },
-    [handleUpdateParams]
-  );
+  const handlePageChange = useCallback(
+    (nextPage: number) => {
+      const target = Math.max(1, Math.min(nextPage, totalPages));
+      handleUpdateParams((params) => {
+        params.set("page", target.toString());
+      });
+    },
+    [handleUpdateParams, totalPages]
+  );

As per coding guidelines

types/xtdh.ts (3)

15-25: Consider consolidating duplicate error interfaces.

XtdhStatsError (lines 15-19) and XtdhReceivedError (lines 21-25) are identical. Consider creating a single shared error type or a generic error interface to reduce duplication.

-export interface XtdhStatsError {
+export interface XtdhError {
   readonly error: string;
   readonly message: string;
   readonly statusCode: number;
 }

-export interface XtdhReceivedError {
-  readonly error: string;
-  readonly message: string;
-  readonly statusCode: number;
-}
+export type XtdhStatsError = XtdhError;
+export type XtdhReceivedError = XtdhError;

40-174: Inconsistent array immutability pattern.

Line 174 uses readonly XtdhMultiplierMilestone[] for deep immutability, but other array properties throughout the file use Type[] without the readonly modifier. For consistency and full immutability guarantees, consider applying the same pattern to all array types.

Example for lines 40, 51, 61, 65, 75, 79, 107-109, 128-131, 142-143, 148, 153-154:

-  readonly granters: XtdhGranter[];
+  readonly granters: readonly XtdhGranter[];

82-82: XtdhAllocationNetwork type pattern allows any string.

The type "ethereum" | string effectively accepts any string, making the "ethereum" literal less meaningful for type narrowing. If only specific networks are valid, consider a strict union. If extensibility is needed, the current pattern is acceptable but limits compile-time safety.

Comment thread __tests__/components/xtdh/XtdhPage.test.tsx
Comment thread app/api/profiles/[profile]/xtdh/received/collections/route.ts Outdated
Comment thread app/api/xtdh/collections/route.ts Outdated
Comment thread app/api/xtdh/tokens/route.ts Outdated
Comment thread app/api/xtdh/tokens/route.ts Outdated
Comment thread components/xtdh/stats-overview/constants.ts Outdated
Comment thread components/xtdh/stats-overview/InfoTooltip.tsx Outdated
Comment thread components/xtdh/stats-overview/StatsMetricsGrid.tsx Outdated
Comment thread components/xtdh/user/received/index.tsx Outdated
Comment thread hooks/useXtdhReceived.ts Outdated
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (4)
components/user/collected/cards/UserPageCollectedNetworkCards.tsx (2)

18-38: Move hooks before early return to comply with Rules of Hooks.

The early return at line 18 causes useMemo (line 26) and useTokenMetadataQuery (line 35) to be called conditionally, which violates the Rules of Hooks and can corrupt React state.

Move both hooks before the conditional return:

 export default function UserPageCollectedNetworkCards({
   cards,
   page,
   setPage,
   next,
 }: {
   readonly cards: ApiXTdhToken[];
   readonly page: number;
   readonly setPage: (page: number) => void;
   readonly next: boolean;
 }) {
-  if (!cards.length) {
-    return (
-      <div className="tw-w-full tw-text-center tw-py-10 tw-text-iron-400">
-        No tokens found
-      </div>
-    );
-  }
-
   const tokens = useMemo(
     () =>
       cards.map((card) => ({
         contract: card.contract,
         tokenId: card.token.toString(),
       })),
     [cards]
   );

   const { data: metadata } = useTokenMetadataQuery({
     tokens,
     enabled: cards.length > 0,
   });

+  if (!cards.length) {
+    return (
+      <div className="tw-w-full tw-text-center tw-py-10 tw-text-iron-400">
+        No tokens found
+      </div>
+    );
+  }
+
   return (

57-61: Replace <img> with Next.js <Image> component.

The plain HTML <img> element violates the @next/next/no-img-element ESLint rule and coding guidelines.

As per coding guidelines.

Import Image at the top:

+import Image from "next/image";
+
 import CommonTablePagination from "@/components/utils/table/paginator/CommonTablePagination";

Replace the img element:

                 <div className="tw-h-[200px] min-[800px]:tw-h-[250px] min-[1200px]:tw-h-[18.75rem] tw-text-center tw-flex tw-items-center tw-justify-center">
-                  <img
+                  <Image
                     src={tokenMetadata?.imageUrl ?? ""}
                     alt={tokenMetadata?.collectionName ?? "Network Token"}
                     className="tw-bg-transparent tw-max-w-full tw-max-h-full tw-h-auto tw-w-auto tw-mx-auto tw-object-contain"
+                    width={300}
+                    height={300}
                   />
                 </div>
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx (1)

121-124: Guard BigInt conversion to avoid runtime crashes on malformed token IDs

Direct BigInt(t.token) will throw if the API returns an invalid string. Consider using defensive conversion with flatMap and try/catch to filter out malformed IDs instead of crashing the component.

-  const mappedTokens = tokens.map((t) => ({
-    tokenId: BigInt(t.token),
-    xtdh: t.xtdh,
-  }));
+  const mappedTokens = tokens.flatMap((t) => {
+    try {
+      return [{ tokenId: BigInt(t.token), xtdh: t.xtdh }];
+    } catch {
+      return [];
+    }
+  });
components/user/xtdh/UserPageXtdhGrantedList.tsx (1)

94-98: Remove inline comment to comply with coding guidelines.

The comment inside the catch block violates the project's "no comments in code" rule. Based on coding guidelines, code should be self-explanatory.

   const handleRetry = useCallback(() => {
-    refetch().catch(() => {
-      // Errors propagate through the query state that's already rendered.
-    });
+    refetch().catch(() => {});
   }, [refetch]);
🧹 Nitpick comments (5)
components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx (1)

14-17: Simplify redundant object mapping.

The mapping operation spreads filter and then explicitly reassigns label: filter.label. Since the spread already includes all properties from filter, the explicit label assignment is redundant.

Consider simplifying to:

-  const items: import("@/components/utils/select/CommonSelect").CommonSelectItem<GrantedActiveFilter>[] = GRANTED_ACTIVE_FILTERS.map((filter) => ({
-    ...filter,
-    label: filter.label,
-  }));
+  const items = GRANTED_ACTIVE_FILTERS;

If GRANTED_ACTIVE_FILTERS already matches the expected CommonSelectItem<GrantedActiveFilter>[] shape, the mapping is unnecessary. Alternatively, if type coercion is needed, move the type import to the top of the file:

import type { CommonSelectItem } from "@/components/utils/select/CommonSelect";

// Then in the component:
const items: CommonSelectItem<GrantedActiveFilter>[] = GRANTED_ACTIVE_FILTERS;
components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1)

67-70: Consider using clsx for conditional className.

The template literal with ternary can produce trailing whitespace and is less maintainable than using the clsx utility (already imported in related components like XtdhRatePill).

Import clsx at the top of the file:

import clsx from "clsx";

Then apply this diff:

   <li
-    className={`tw-list-none tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-4 tw-transition-colors ${isClickable
-      ? "tw-cursor-pointer hover:tw-bg-iron-800 hover:tw-border-iron-700"
-      : ""
-      }`}
+    className={clsx(
+      "tw-list-none tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-4 tw-transition-colors",
+      isClickable && "tw-cursor-pointer hover:tw-bg-iron-800 hover:tw-border-iron-700"
+    )}
components/user/xtdh/UserPageXtdhGrantedList.tsx (1)

51-72: Remove unused isFetching variable.

The isFetching value is destructured from useXtdhGrantsQuery but is never used in the component.

   const {
     grants,
     isLoading,
     isError,
-    isFetching,
     errorMessage,
     refetch,
     hasNextPage,
     fetchNextPage,
     isFetchingNextPage,
   } = useXtdhGrantsQuery({
components/xtdh/received/index.tsx (1)

80-93: Remove inline comments in callbacks to match repo guidelines.

handleRetry and handleLoadMore are implemented correctly and already self-explanatory. The inline // comments inside their .catch blocks conflict with the “no comments in the code” guideline; consider removing them and relying on the query state + component naming to convey intent.

components/xtdh/received/collection-tokens/index.tsx (1)

74-87: Drop inline comments in error handlers to keep code comment‑free.

The retry and load‑more handlers correctly guard against repeated fetches and rely on query state to surface errors. The // Error surfaced via query state comments are redundant and violate the “no comments in the code” rule; you can safely delete them without losing clarity. As per coding guidelines, this keeps TSX components comment‑free.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 042ec50 and 8d5e950.

📒 Files selected for processing (22)
  • components/token-list/VirtualizedTokenList.tsx (1 hunks)
  • components/token-list/components/GridRow.tsx (1 hunks)
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantedList.tsx (1 hunks)
  • components/user/xtdh/UserXtdhStats.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/statusVisuals.ts (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantDetailsRow.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx (1 hunks)
  • components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx (1 hunks)
  • components/xtdh/received/collection-card-content/index.tsx (1 hunks)
  • components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/index.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/CollectionBreadcrumbs.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1 hunks)
  • components/xtdh/received/index.tsx (1 hunks)
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantDetailsRow.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/xtdh/received/collection-tokens/subcomponents/CollectionBreadcrumbs.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx
  • components/token-list/components/GridRow.tsx
  • components/token-list/VirtualizedTokenList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/statusVisuals.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/xtdh/received/collection-card-content/index.tsx
  • components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx
  • components/xtdh/received/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx
  • components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/xtdh/received/collection-card-content/index.tsx
  • components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx
  • components/xtdh/received/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx
  • components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/xtdh/received/collection-card-content/index.tsx
  • components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx
  • components/xtdh/received/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx
  • components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/xtdh/received/collection-card-content/index.tsx
  • components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx
  • components/xtdh/received/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx
  • components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
🧠 Learnings (9)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/xtdh/received/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/xtdh/received/index.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/xtdh/received/index.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not include any comments in the code; it should be self-explanatory

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : If the `react-hooks/exhaustive-deps` lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in `useEffectEvent`

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Replace `<img>` elements with `<Image />` from `next/image` to satisfy `next/next/no-img-element` ESLint rule

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use framework APIs: internal links should use `<Link>`, images should use `next/image`, and adopt Next's ESLint rules (Core Web Vitals)

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Link href="/path">` from Next.js for internal navigation instead of plain HTML links to satisfy `next/next/no-html-link-for-pages` ESLint rule

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
🧬 Code graph analysis (7)
components/xtdh/received/index.tsx (8)
hooks/useDebouncedValue.ts (1)
  • useDebouncedValue (3-10)
components/xtdh/received/hooks/useXtdhCollectionsFilters.ts (1)
  • useXtdhCollectionsFilters (26-88)
hooks/useXtdhCollectionsQuery.ts (1)
  • useXtdhCollectionsQuery (45-119)
components/xtdh/received/hooks/useXtdhCollectionSelection.ts (1)
  • useXtdhCollectionSelection (25-80)
generated/models/ApiXTdhCollectionsPage.ts (1)
  • ApiXTdhCollectionsPage (16-51)
components/xtdh/received/collection-tokens/index.tsx (1)
  • XtdhCollectionTokensPanel (24-192)
components/xtdh/received/collections-controls/index.tsx (1)
  • XtdhCollectionsControls (21-59)
components/xtdh/received/subcomponents/XtdhCollectionsList.tsx (1)
  • XtdhCollectionsList (26-99)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx (4)
types/nft.ts (1)
  • SupportedChain (1-1)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/hooks/useGrantTokensDisclosure.ts (1)
  • useGrantTokensDisclosure (24-93)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/types.ts (1)
  • GrantTokensDisclosureState (33-45)
components/token-list/VirtualizedTokenList.tsx (1)
  • VirtualizedTokenList (15-67)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (6)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/types.ts (1)
  • GrantItemContentProps (47-56)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/formatters.ts (1)
  • shortenAddress (32-34)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx (1)
  • GrantExpiryBadge (3-12)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx (1)
  • StatusBadge (7-34)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantDetailsGrid.tsx (1)
  • GrantDetailsGrid (4-39)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantErrorDetails.tsx (1)
  • GrantErrorDetails (1-15)
components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (7)
generated/models/ApiXTdhContribution.ts (1)
  • ApiXTdhContribution (17-73)
components/xtdh/received/utils/formatters.ts (2)
  • formatXtdhValue (18-24)
  • formatXtdhRate (26-28)
helpers/AllowlistToolHelpers.ts (2)
  • isEthereumAddress (62-69)
  • isAutoGeneratedHandle (72-77)
components/common/profile/ProfileBadge.tsx (1)
  • ProfileBadge (29-68)
helpers/Helpers.ts (1)
  • cicToType (497-515)
components/utils/tooltip/UserProfileTooltipWrapper.tsx (1)
  • UserProfileTooltipWrapper (12-34)
components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1)
  • XtdhRatePill (9-29)
components/user/xtdh/UserPageXtdhGrantedList.tsx (7)
components/user/xtdh/user-page-xtdh-granted-list/constants.ts (1)
  • getApiParamsFromFilters (215-255)
hooks/useXtdhGrantsQuery.ts (1)
  • useXtdhGrantsQuery (57-152)
hooks/usePendingGrantsCount.ts (1)
  • usePendingGrantsCount (6-40)
components/react-query-wrapper/ReactQueryWrapper.tsx (1)
  • ReactQueryWrapperContext (191-220)
components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListTabs.tsx (1)
  • UserPageXtdhGrantedListTabs (12-35)
components/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsx (1)
  • UserPageXtdhGrantedListSubFilters (10-28)
components/user/xtdh/granted-list/index.tsx (1)
  • UserPageXtdhGrantedListContent (8-39)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx (1)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/statusVisuals.ts (1)
  • getStatusVisuals (48-90)
components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (7)
components/xtdh/received/collection-tokens/types.ts (1)
  • ApiXtdhToken (7-7)
components/xtdh/received/collection-tokens/utils/getTokenLabel.ts (1)
  • getTokenLabel (1-7)
components/xtdh/received/utils/formatters.ts (2)
  • formatXtdhValue (18-24)
  • formatXtdhRate (26-28)
components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx (1)
  • XtdhTokenListItemThumbnail (14-57)
app/layout.tsx (1)
  • metadata (23-23)
components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1)
  • XtdhContributorSummary (9-39)
components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1)
  • XtdhRatePill (9-29)
🪛 Biome (2.1.2)
components/user/collected/cards/UserPageCollectedNetworkCards.tsx

[error] 26-26: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 35-35: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1)

1-39: LGTM!

Clean presentational component with good practices:

  • Semantic HTML (<dl>, <dt>, <dd>) provides proper accessibility for term-value pairs
  • TailwindCSS styling follows project conventions
  • Props correctly marked as readonly

Minor note: the Readonly<> wrapper on line 13 is technically redundant since the interface already has readonly on each property, but it's harmless and satisfies the coding guideline.

components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx (1)

1-37: LGTM! Clean and accessible skeleton component.

The implementation follows best practices with proper semantic HTML, good accessibility features (sr-only announcement, aria-busy, aria-live), and appropriate use of TailwindCSS utilities including the pulse animation for loading states.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx (3)

145-169: LGTM!

Loading and error states are well-implemented with appropriate styling and UX patterns. The error state provides clear feedback with an actionable retry button.


171-188: LGTM!

Empty state and loading-more indicator are appropriately implemented with clear messaging and consistent styling.


26-90: LGTM!

The disclosure component is well-structured with proper accessibility attributes (aria-expanded, aria-controls, aria-hidden), clean state management via the custom hook, and appropriate conditional rendering. The toggle button provides clear visual feedback with the rotating chevron and focus-visible styles.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsx (1)

1-34: StatusBadge implementation looks solid and consistent

Imports, prop typing, integration with getStatusVisuals, and the JSX (including the ARIA labeling and FontAwesome usage) are all clean and cohesive; no issues stand out for correctness, accessibility, or style here. Based on learnings, this aligns well with the existing TypeScript + React patterns in the repo.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (1)

1-62: GrantItemContent is well-structured and correctly wired

Overall composition (image/placeholder, name + shortened address, expiry/status badges, optional actions, details grid, and conditional error block) is clean and type-safe, with appropriate fallbacks for missing images and error details; I don’t see any functional or accessibility issues in this implementation. Based on learnings, this matches the repo’s established component and typing style.

components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (3)

1-9: LGTM!

All imports are properly structured with type-only import for the API model and appropriate relative paths for local utilities.


11-18: LGTM!

Props interface correctly uses readonly modifiers per coding guidelines, and the JSDoc comment provides helpful context about the dual nature of the contribution prop.


20-60: LGTM!

The component logic is well-structured with appropriate null safety using optional chaining, sensible fallback chains for display values, and correct conditional rendering logic.

components/user/xtdh/UserPageXtdhGrantedList.tsx (2)

1-28: LGTM!

Imports use named React hooks as required, and the interface properly uses readonly modifiers on props. Type re-exports provide a clean public API.


129-150: LGTM!

Content component receives all required props correctly. The "Load More" button has proper disabled state handling, loading feedback, and accessible type="button" attribute.

components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1)

3-27: Well-factored, reusable pill component.

Props are readonly, styling is consistent with the rest of the xTDH UI, and the component stays narrowly focused on presentation. No issues from a typing, accessibility, or Next.js perspective.

components/xtdh/received/collection-card-content/index.tsx (1)

34-155: Contract resolution, query wiring, and card rendering look solid.

Address normalization, guarded useContractOverviewQuery enabling, and the selectable vs. static interaction modes are all handled cleanly. Metric formatting and the XtdhRatePill integration are consistent with the rest of the xTDH surface, and types are precise with readonly props.

components/xtdh/received/collection-tokens/index.tsx (1)

24-191: Token panel composition and state wiring look correct.

Sorting, selection, back‑navigation, and grant selection are all wired coherently. Query parameters (identity, contract, page size, order) match expectations, controlsDisabled and showLoadMore are derived sensibly, and the split between “token selected” vs “token list” views is clean. No correctness or typing issues stand out.

components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (1)

12-87: Clean, composable token row implementation.

Readonly props, shared formatters, and the as + onSelect pattern provide a flexible token row that works in both list and detail contexts. Wrapper semantics, Tailwind classes, and reuse of XtdhTokenListItemThumbnail, XtdhContributorSummary, and XtdhRatePill all look good.

Comment thread components/user/xtdh/UserPageXtdhGrantedList.tsx
Comment thread components/user/xtdh/UserPageXtdhGrantedList.tsx Outdated
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
components/user/collected/cards/UserPageCollectedNetworkCards.tsx (2)

18-38: Move hooks before the early return to satisfy Rules of Hooks.

The early return at line 18 causes useMemo (line 26) and useTokenMetadataQuery (line 35) to be called conditionally, violating React's Rules of Hooks and potentially causing state corruption.

Apply this diff to fix:

 export default function UserPageCollectedNetworkCards({
   cards,
   page,
   setPage,
   next,
 }: {
   readonly cards: ApiXTdhToken[];
   readonly page: number;
   readonly setPage: (page: number) => void;
   readonly next: boolean;
 }) {
-  if (!cards.length) {
-    return (
-      <div className="tw-w-full tw-text-center tw-py-10 tw-text-iron-400">
-        No tokens found
-      </div>
-    );
-  }
-
   const tokens = useMemo(
     () =>
       cards.map((card) => ({
         contract: card.contract,
         tokenId: card.token.toString(),
       })),
     [cards]
   );

   const { data: metadata } = useTokenMetadataQuery({
     tokens,
     enabled: cards.length > 0,
   });

+  if (!cards.length) {
+    return (
+      <div className="tw-w-full tw-text-center tw-py-10 tw-text-iron-400">
+        No tokens found
+      </div>
+    );
+  }
+
   return (

89-95: Replace <img> with Next.js <Image> component.

Per coding guidelines and the @next/next/no-img-element ESLint rule, use <Image> from next/image for optimized image loading.

As per coding guidelines.

Apply this diff:

+import Image from "next/image";
+
 export default function UserPageCollectedNetworkCards({
             {imageUrl && (
-              <img
+              <Image
                 src={imageUrl}
                 alt={tokenMetadata?.collectionName ?? "Network Token"}
                 onLoad={() => setIsImageLoaded(true)}
+                fill
+                sizes="(max-width: 800px) 200px, (max-width: 1200px) 250px, 300px"
                 className={`tw-bg-transparent tw-max-w-full tw-max-h-full tw-h-auto tw-w-auto tw-mx-auto tw-object-contain ${!isImageLoaded ? "tw-opacity-0" : "tw-opacity-100"
                   } tw-transition-opacity tw-duration-300`}
               />
             )}

Note: When using fill, the parent container (line 84) already has relative positioning via tw-relative, so the Image will fill it correctly. Ensure external image domains are configured in next.config.js if needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d5e950 and 5949f91.

📒 Files selected for processing (3)
  • components/user/collected/cards/UserPageCollectedCard.tsx (4 hunks)
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx (1 hunks)
  • components/user/xtdh/UserXtdhStats.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/user/xtdh/UserXtdhStats.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
🧠 Learnings (8)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components

Applied to files:

  • components/user/collected/cards/UserPageCollectedCard.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : If the `react-hooks/exhaustive-deps` lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in `useEffectEvent`

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Replace `<img>` elements with `<Image />` from `next/image` to satisfy `next/next/no-img-element` ESLint rule

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use framework APIs: internal links should use `<Link>`, images should use `next/image`, and adopt Next's ESLint rules (Core Web Vitals)

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Link href="/path">` from Next.js for internal navigation instead of plain HTML links to satisfy `next/next/no-html-link-for-pages` ESLint rule

Applied to files:

  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
🧬 Code graph analysis (1)
components/user/collected/cards/UserPageCollectedNetworkCards.tsx (4)
generated/models/ApiXTdhToken.ts (1)
  • ApiXTdhToken (16-79)
hooks/useAlchemyNftQueries.ts (1)
  • useTokenMetadataQuery (277-348)
components/utils/table/paginator/CommonTablePagination.tsx (1)
  • CommonTablePagination (1-82)
helpers/Helpers.ts (1)
  • formatStatFloor (127-137)
🪛 Biome (2.1.2)
components/user/collected/cards/UserPageCollectedNetworkCards.tsx

[error] 26-26: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 35-35: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
components/user/collected/cards/UserPageCollectedCard.tsx (3)

15-15: LGTM! Correct import pattern.

The direct named import of useState aligns with the coding guidelines preference for direct imports over namespace usage.


56-57: LGTM! Proper state initialization.

The state management for tracking image load status is correctly implemented and serves the loading UX enhancement.


239-246: LGTM! Improved readability.

The reformatting of props improves code readability without changing functionality.

Comment thread components/user/collected/cards/UserPageCollectedCard.tsx Outdated
Comment thread components/user/collected/cards/UserPageCollectedNetworkCards.tsx
ragnep and others added 2 commits December 4, 2025 17:13
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
components/user/xtdh/UserXtdhStats.tsx (3)

1-2: Missing FontAwesome imports for icon usage.

The inline SVG icon at lines 61-74 should use FontAwesome according to project guidelines. The necessary imports (FontAwesomeIcon from @fortawesome/react-fontawesome and faPlus from @fortawesome/free-solid-svg-icons) are missing.

As per coding guidelines, React components should use FontAwesome for icons.


61-74: Replace inline SVG with FontAwesome icon.

This inline SVG should use FontAwesomeIcon with faPlus icon to comply with project guidelines for React components.

As per coding guidelines and learnings, use FontAwesome for icons in React components.


105-113: Guard against division by zero in progress bar width calculation.

When generationRate is 0, the division (Math.floor(outbound * 10) / 10) / (Math.floor(generationRate * 10) / 10) produces NaN, resulting in invalid CSS width: "NaN%". Add a guard to handle generationRate <= 0 by returning 0 for the width percentage.

components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1)

65-75: Make the clickable <li> keyboard-accessible and properly exposed to assistive tech.

This <li> is effectively a button (clickable, changes state upstream) but lacks role, tabIndex, and keyboard handling, which blocks keyboard-only users and screen readers. This was already raised in a previous review and still applies here.

Apply this diff to add accessibility without changing behavior:

-  return (
-    <li
-      className={`tw-list-none tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-4 tw-transition-colors ${isClickable
-        ? "tw-cursor-pointer hover:tw-bg-iron-800 hover:tw-border-iron-700"
-        : ""
-        }`}
-      onClick={() => {
-        if (grantId) {
-          onSelectGrant(grantId, displayHandle);
-        }
-      }}
-    >
+  return (
+    <li
+      className={`tw-list-none tw-rounded-2xl tw-border tw-border-iron-800 tw-bg-iron-900 tw-p-4 tw-transition-colors ${isClickable
+        ? "tw-cursor-pointer hover:tw-bg-iron-800 hover:tw-border-iron-700"
+        : ""
+        }`}
+      role={isClickable ? "button" : undefined}
+      tabIndex={isClickable ? 0 : -1}
+      aria-disabled={!isClickable}
+      onClick={() => {
+        if (!isClickable || !grantId) return;
+        onSelectGrant(grantId, displayHandle);
+      }}
+      onKeyDown={(event) => {
+        if (!isClickable || !grantId) return;
+        if (event.key === "Enter" || event.key === " " || event.key === "Spacebar") {
+          event.preventDefault();
+          onSelectGrant(grantId, displayHandle);
+        }
+      }}
+    >

This keeps pointer behavior while making the row operable and understandable via keyboard and screen readers.

🧹 Nitpick comments (9)
components/user/xtdh/UserXtdhTestModeBanner.tsx (1)

6-19: Replace inline SVG with FontAwesome icon per coding guidelines.

As per coding guidelines, React components should use FontAwesome for icons rather than inline SVG.

Add the FontAwesome import at the top of the file:

+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
+
 export function UserXtdhTestModeBanner() {

Then replace the inline SVG with a FontAwesome icon:

-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-            className="tw-h-5 tw-w-5"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
-            />
-          </svg>
+          <FontAwesomeIcon icon={faCircleInfo} className="tw-h-5 tw-w-5" />
components/user/utils/stats/UserStatsRow.tsx (1)

34-125: Consider extracting a reusable stat link subcomponent to reduce duplication

The TDH and xTDH sections share the same structure (value + label + optional positive rate) and link styling. You could slightly simplify and future‑proof this by extracting an internal StatWithRate component and reusing it for TDH/xTDH, keeping NIC/Rep/Followers as-is.

For example:

+"use client";
+
+import Link from "next/link";
+import { formatNumberWithCommas, formatStatFloor } from "@/helpers/Helpers";
+
+interface UserStatsRowProps {
+  readonly handle: string;
+  readonly tdh: number;
+  readonly tdh_rate: number;
+  readonly xtdh: number;
+  readonly xtdh_rate: number;
+  readonly rep: number;
+  readonly cic: number;
+  readonly followersCount: number | null;
+  readonly className?: string;
+}
+
+interface StatWithRateProps {
+  readonly href: string;
+  readonly value: number;
+  readonly label: string;
+  readonly rate?: number;
+}
+
+function StatWithRate({ href, value, label, rate }: StatWithRateProps) {
+  return (
+    <Link
+      href={href}
+      className="tw-no-underline desktop-hover:hover:tw-underline tw-transition tw-duration-300 tw-ease-out"
+    >
+      <span className="tw-text-base tw-font-semibold tw-text-iron-50">
+        {formatStatFloor(value)}
+      </span>{" "}
+      <span className="tw-text-base tw-font-medium tw-text-iron-400">
+        {label}
+      </span>
+      {rate !== undefined && rate > 0 && (
+        <>
+          {" "}
+          <span className="tw-text-sm tw-font-medium tw-text-iron-400">
+            +
+            <span className="tw-text-iron-50">{formatStatFloor(rate)}</span>
+          </span>
+        </>
+      )}
+    </Link>
+  );
+}
+
 export default function UserStatsRow({
   handle,
   tdh,
   tdh_rate,
   xtdh,
   xtdh_rate,
   rep,
   cic,
   followersCount,
   className = "",
 }: UserStatsRowProps) {
   const routeHandle = encodeURIComponent(handle.toLowerCase());
   const count = followersCount ?? 0;
   const followerLabel = count === 1 ? "Follower" : "Followers";
 
   return (
     <div className={`@container ${className}`}>
       <div className="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
-        <Link
-          href={`/${routeHandle}/collected`}
-          className="tw-no-underline desktop-hover:hover:tw-underline tw-transition tw-duration-300 tw-ease-out"
-        >
-          <span className="tw-text-base tw-font-semibold tw-text-iron-50">
-            {formatStatFloor(tdh)}
-          </span>{" "}
-          <span className="tw-text-base tw-font-medium tw-text-iron-400">
-            TDH
-          </span>
-          {tdh_rate > 0 && (
-            <>
-              {" "}
-              <span className="tw-text-sm tw-font-medium tw-text-iron-400">
-                +
-                <span className="tw-text-iron-50">
-                  {formatStatFloor(tdh_rate)}
-                </span>
-              </span>
-            </>
-          )}
-        </Link>
+        <StatWithRate
+          href={`/${routeHandle}/collected`}
+          value={tdh}
+          label="TDH"
+          rate={tdh_rate}
+        />
 
         <div className="tw-text-iron-600 tw-mx-3">|</div>
 
-        <Link
-          href={`/${routeHandle}/xtdh`}
-          className="tw-no-underline desktop-hover:hover:tw-underline tw-transition tw-duration-300 tw-ease-out"
-        >
-          <span className="tw-text-base tw-font-semibold tw-text-iron-50">
-            {formatStatFloor(xtdh)}
-          </span>{" "}
-          <span className="tw-text-base tw-font-medium tw-text-iron-400">
-            xTDH
-          </span>
-          {xtdh_rate > 0 && (
-            <>
-              {" "}
-              <span className="tw-text-sm tw-font-medium tw-text-iron-400">
-                +
-                <span className="tw-text-iron-50">
-                  {formatStatFloor(xtdh_rate)}
-                </span>
-              </span>
-            </>
-          )}
-        </Link>
+        <StatWithRate
+          href={`/${routeHandle}/xtdh`}
+          value={xtdh}
+          label="xTDH"
+          rate={xtdh_rate}
+        />
 
         <div className="tw-text-iron-600 tw-mx-3">|</div>
@@
         <Link
           href={`/${routeHandle}/followers`}
           className="tw-no-underline desktop-hover:hover:tw-underline tw-transition tw-duration-300 tw-ease-out"
         >
           <span className="tw-text-base tw-font-semibold tw-text-iron-50">
             {formatNumberWithCommas(count)}
           </span>{" "}
           <span className="tw-text-base tw-font-medium tw-text-iron-400">
             {followerLabel}
           </span>
         </Link>
       </div>
     </div>
   );
 }

This keeps behavior identical while centralizing the common layout and formatting for the stat-with-rate links.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (2)

87-91: Optional: Simplify redundant null check.

The condition if (!isUnsupportedChain) is redundant because when isUnsupportedChain is true, shouldLoadContract is false, so useContractOverviewQuery won't execute and fetchedContract will be undefined anyway.

Apply this diff to simplify:

-let contract: ContractOverview | null = null;
-
-if (!isUnsupportedChain) {
-  contract = fetchedContract ?? null;
-}
+const contract: ContractOverview | null = fetchedContract ?? null;

149-157: Optional: Consider extracting tokens description logic.

The IIFE for tokensDescription could be extracted to a separate helper function for consistency with the module's pattern of small, focused utility functions.

Example extraction:

function getTokensDescription(tokensCountInfo: ReturnType<typeof getTargetTokensCountInfo>): string {
  if (tokensCountInfo.kind === "all") {
    return "all tokens in this collection";
  }
  if (tokensCountInfo.kind === "count") {
    return `${tokensCountInfo.label} tokens granted`;
  }
  return "an unknown number of tokens";
}

Then use:

-const tokensDescription = (() => {
-  if (tokensCountInfo.kind === "all") {
-    return "all tokens in this collection";
-  }
-  if (tokensCountInfo.kind === "count") {
-    return `${tokensCountInfo.label} tokens granted`;
-  }
-  return "an unknown number of tokens";
-})();
+const tokensDescription = getTokensDescription(tokensCountInfo);
components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx (2)

45-49: Remove inline comment to comply with “no comments in TS/TSX” guideline

The catch block currently contains an inline comment:

} catch {
  // Ignore unsupported chain error for display purposes
}

Per the provided coding guidelines, TS/TSX code should not include comments. You can safely drop the comment and keep the current behavior by leaving the catch block empty (assuming no-empty is configured to allow empty catches):

-    let chain = null;
-    try {
-      chain = mapGrantChainToSupportedChain(grant.target_chain);
-    } catch {
-      // Ignore unsupported chain error for display purposes
-    }
+    let chain = null;
+    try {
+      chain = mapGrantChainToSupportedChain(grant.target_chain);
+    } catch {
+      // chain stays null for unsupported or unmapped chains
+    }

If no-empty is enforced for catch blocks, consider adjusting the helper to return null instead of throwing so you can remove the try/catch entirely in this component.

As per coding guidelines, TS/TSX files should be comment‑free.


93-96: Minor: avoid wrapping refetch in a new arrow

You can pass refetch directly to onRetry instead of creating a new function each render:

-      <GrantedListError
-        message={errorMessage ?? "Failed to load grant details."}
-        onRetry={() => refetch()}
-      />
+      <GrantedListError
+        message={errorMessage ?? "Failed to load grant details."}
+        onRetry={refetch}
+      />

Behavior is identical and slightly cleaner.

components/xtdh/received/hooks/useXtdhCollectionsFilters.ts (1)

3-37: Consider dropping useMemo and deriving sort values directly during render

activeSortField and activeSortDirection are cheap to compute from searchParams, so useMemo is not buying much here and slightly increases complexity and hook dependencies. You can simplify by computing them directly on each render and removing the unused useMemo import:

-import { useCallback, useMemo } from "react";
+import { useCallback } from "react";
@@
-  const activeSortField = useMemo(
-    () => parseCollectionSortField(searchParams?.get(SORT_PARAM) ?? null),
-    [searchParams]
-  );
-  const activeSortDirection = useMemo(
-    () => parseCollectionSortDirection(searchParams?.get(DIRECTION_PARAM) ?? null),
-    [searchParams]
-  );
+  const activeSortField = parseCollectionSortField(
+    searchParams.get(SORT_PARAM)
+  );
+  const activeSortDirection = parseCollectionSortDirection(
+    searchParams.get(DIRECTION_PARAM)
+  );

This keeps the hook straightforward and avoids depending on the identity semantics of searchParams. Based on learnings, this aligns with the preference for clean, simple hooks in this codebase.

__tests__/hooks/useXtdhTokensQuery.test.ts (1)

1-17: Prefer ESM import for commonApiFetch instead of require + type assertion

You can lean on Jest’s module mocking and avoid require entirely, which is more idiomatic for TS/Next and keeps types simpler:

-import { renderHook } from "@testing-library/react";
-import { useXtdhTokensQuery } from "@/hooks/useXtdhTokensQuery";
-
-const mockUseInfiniteQuery = jest.fn();
-
-jest.mock("@tanstack/react-query", () => ({
-  keepPreviousData: Symbol("keepPreviousData"),
-  useInfiniteQuery: (...args: unknown[]) => mockUseInfiniteQuery(...args),
-}));
-
-jest.mock("@/services/api/common-api", () => ({
-  commonApiFetch: jest.fn(),
-}));
-
-const commonApiFetch =
-  require("@/services/api/common-api").commonApiFetch as jest.Mock;
+import { renderHook } from "@testing-library/react";
+import { useXtdhTokensQuery } from "@/hooks/useXtdhTokensQuery";
+import { commonApiFetch } from "@/services/api/common-api";
+
+const mockUseInfiniteQuery = jest.fn();
+
+jest.mock("@tanstack/react-query", () => ({
+  keepPreviousData: Symbol("keepPreviousData"),
+  useInfiniteQuery: (...args: unknown[]) => mockUseInfiniteQuery(...args),
+}));
+
+jest.mock("@/services/api/common-api", () => ({
+  commonApiFetch: jest.fn(),
+}));

This keeps everything in ESM style and still gives you a fully mocked commonApiFetch for the expectations below. As per coding guidelines, prefer modern TS imports over require.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (1)

18-45: Avoid recalculating the shortened address.

You call shortenAddress(contract.address) twice; you can compute it once and reuse it for both the name fallback and the subtitle for slightly cleaner code:

-export function GrantItemContent({
-  contract,
+export function GrantItemContent({
+  contract,
   details,
   errorDetails,
   status,
   validFrom,
   validTo,
   actions,
 }: Readonly<GrantItemContentProps>) {
-  const name = contract.name ?? shortenAddress(contract.address);
+  const addressLabel = shortenAddress(contract.address);
+  const name = contract.name ?? addressLabel;

   return (
@@
-            <p className="tw-m-0 tw-text-xs tw-text-iron-500">
-              {shortenAddress(contract.address)}
-            </p>
+            <p className="tw-m-0 tw-text-xs tw-text-iron-500">
+              {addressLabel}
+            </p>

Purely a readability/DRY tweak; behavior stays the same.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5949f91 and 3f52692.

📒 Files selected for processing (18)
  • __tests__/components/user/xtdh/granted-list/UserPageXtdhGrantListItem.test.tsx (1 hunks)
  • __tests__/hooks/useXtdhTokensQuery.test.ts (1 hunks)
  • components/user/collected/filters/UserPageCollectedFiltersNetworkCollection.tsx (1 hunks)
  • components/user/utils/stats/UserStatsRow.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdh.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGranted.tsx (1 hunks)
  • components/user/xtdh/UserXtdhStats.tsx (1 hunks)
  • components/user/xtdh/UserXtdhTestModeBanner.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/types.ts (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (1 hunks)
  • components/xtdh/received/collection-tokens/token-contributors/hooks/useXtdhTokenContributorsFilters.ts (1 hunks)
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1 hunks)
  • components/xtdh/received/hooks/useXtdhCollectionsFilters.ts (1 hunks)
  • components/xtdh/received/hooks/useXtdhTokensFilters.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/components/user/xtdh/granted-list/UserPageXtdhGrantListItem.test.tsx
  • components/xtdh/received/hooks/useXtdhTokensFilters.ts
  • components/xtdh/received/collection-tokens/token-contributors/hooks/useXtdhTokenContributorsFilters.ts
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/types.ts
  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/collected/filters/UserPageCollectedFiltersNetworkCollection.tsx
  • components/user/xtdh/UserPageXtdhGranted.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/user/xtdh/UserXtdhTestModeBanner.tsx
  • __tests__/hooks/useXtdhTokensQuery.test.ts
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
  • components/user/utils/stats/UserStatsRow.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/hooks/useXtdhCollectionsFilters.ts
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/user/xtdh/UserXtdhTestModeBanner.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/utils/stats/UserStatsRow.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/user/xtdh/UserXtdhTestModeBanner.tsx
  • __tests__/hooks/useXtdhTokensQuery.test.ts
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
  • components/user/utils/stats/UserStatsRow.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/hooks/useXtdhCollectionsFilters.ts
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/user/xtdh/UserXtdhTestModeBanner.tsx
  • __tests__/hooks/useXtdhTokensQuery.test.ts
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
  • components/user/utils/stats/UserStatsRow.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/hooks/useXtdhCollectionsFilters.ts
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
🧠 Learnings (8)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/user/xtdh/UserXtdhTestModeBanner.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
  • components/xtdh/received/hooks/useXtdhCollectionsFilters.ts
📚 Learning: 2025-11-25T08:37:36.725Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:36.725Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js,jsx} : Use the Arrange – Act – Assert pattern for test structure, keeping assertions focused on behaviour, not implementation details

Applied to files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
📚 Learning: 2025-11-25T08:37:36.725Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:36.725Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js,jsx} : Give each test a clear, descriptive name that conveys the scenario and expected outcome

Applied to files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
📚 Learning: 2025-11-25T08:37:36.725Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:36.725Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js,jsx} : Organize test files in `__tests__` directory mirroring source folder structure (components, contexts, hooks, utils) to keep structure familiar

Applied to files:

  • __tests__/hooks/useXtdhTokensQuery.test.ts
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components

Applied to files:

  • components/user/xtdh/UserXtdhStats.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/xtdh/UserXtdhStats.tsx
🧬 Code graph analysis (7)
__tests__/hooks/useXtdhTokensQuery.test.ts (1)
hooks/useXtdhTokensQuery.ts (1)
  • useXtdhTokensQuery (45-127)
components/user/xtdh/UserXtdhStats.tsx (1)
helpers/Helpers.ts (1)
  • formatStatFloor (127-137)
components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (7)
generated/models/ApiXTdhContribution.ts (1)
  • ApiXTdhContribution (17-73)
components/xtdh/received/utils/formatters.ts (2)
  • formatXtdhValue (18-24)
  • formatXtdhRate (26-28)
helpers/AllowlistToolHelpers.ts (2)
  • isEthereumAddress (62-69)
  • isAutoGeneratedHandle (72-77)
components/common/profile/ProfileBadge.tsx (1)
  • ProfileBadge (29-68)
helpers/Helpers.ts (1)
  • cicToType (497-515)
components/utils/tooltip/UserProfileTooltipWrapper.tsx (1)
  • UserProfileTooltipWrapper (12-34)
components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1)
  • XtdhRatePill (9-29)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (5)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/types.ts (2)
  • GrantDetails (13-24)
  • GrantItemVariant (26-26)
generated/models/ApiXTdhGrant.ts (1)
  • ApiXTdhGrant (18-140)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/formatters.ts (4)
  • getContractAddress (16-30)
  • mapGrantChainToSupportedChain (73-83)
  • formatTotalSupply (49-61)
  • formatFloorPrice (63-71)
hooks/useAlchemyNftQueries.ts (1)
  • useContractOverviewQuery (238-275)
components/user/xtdh/utils/xtdhGrantFormatters.ts (3)
  • getTargetTokensCountInfo (38-60)
  • formatTdhRatePerToken (157-195)
  • formatDateTime (99-117)
components/user/utils/stats/UserStatsRow.tsx (1)
helpers/Helpers.ts (2)
  • formatStatFloor (127-137)
  • formatNumberWithCommas (103-120)
components/xtdh/received/hooks/useXtdhCollectionsFilters.ts (2)
hooks/useXtdhCollectionsQuery.ts (2)
  • XtdhCollectionsSortField (15-15)
  • XtdhCollectionsOrder (16-16)
components/xtdh/received/constants.ts (4)
  • parseCollectionSortField (25-36)
  • parseCollectionSortDirection (38-48)
  • DEFAULT_COLLECTION_SORT_FIELD (22-22)
  • DEFAULT_COLLECTION_SORT_DIRECTION (23-23)
components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx (1)
components/user/xtdh/utils/xtdhGrantFormatters.ts (1)
  • formatDateTime (99-117)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (12)
components/user/xtdh/UserXtdhTestModeBanner.tsx (1)

1-1: No action required. The component correctly omits the React import because the project is configured with the "react-jsx" JSX transform in tsconfig.json and uses Next.js 16, which supports the automatic JSX runtime. Importing React is unnecessary and not the pattern used in this codebase.

Likely an incorrect or invalid review comment.

components/user/utils/stats/UserStatsRow.tsx (1)

1-31: Solid props typing and routing logic

Props are correctly typed (including readonly), routeHandle encoding and follower count defaulting are sensible, and the helpers are used appropriately. No correctness or type-safety issues here.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (5)

1-21: LGTM!

The imports and client directive are correct. Direct named imports are used appropriately, and the module structure follows Next.js conventions.


24-36: LGTM!

The interface is well-defined with appropriate readonly modifiers and type safety.


38-60: LGTM!

The error derivation logic is well-structured with clear priority and appropriate contextual messages for different failure scenarios.


120-136: LGTM!

The contract enrichment logic is clean and correctly overrides base details when contract data is available.


177-185: LGTM!

The error normalization logic correctly handles null, empty, and whitespace-only strings.

components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsx (1)

31-183: Overall structure, typing, and UI state handling look solid

  • Props are correctly marked readonly and wrapped in Readonly<…>.
  • useXtdhGrantQuery result is handled cleanly with loading, error, and “no grant/viewModel” guards.
  • The useMemo view model neatly isolates formatting/mapping logic and only depends on grant.
  • Token count, rate, and per‑token hint derivations look robust against missing/unknown counts.
  • Grantor fallbacks (handle → shortened address → "Unknown grantor") are sensible, and you correctly use next/image and Tailwind utility classes.

No functional or type‑safety issues stand out beyond the small nits noted separately.

components/xtdh/received/hooks/useXtdhCollectionsFilters.ts (1)

19-24: URL-synced sorting hook is coherent and type-safe

The hook’s public API, sort/direction parsing, toggle behavior, default handling, and URL updates (including stripping default params and preserving other query params) all look consistent and type-safe. The implementation should integrate cleanly with the collections query layer and UI consumers.

Also applies to: 26-88

components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1)

23-59: Data fallback, formatting, and tooltip gating logic look solid.

The fallbacks for grantor, displayHandle, and profileHref, combined with isEthereumAddress / isAutoGeneratedHandle-driven truncation and conditional tooltip wrapping, are defensive and align well with the shared helpers and XtdhRatePill API.

__tests__/hooks/useXtdhTokensQuery.test.ts (1)

18-88: Tests cleanly exercise ASC/DESC behaviour and follow AAA structure

The shared defaultQueryResult plus the beforeEach reset keep the mock state predictable, and both tests clearly:

  • Arrange: override mockUseInfiniteQuery to capture config.
  • Act: call the hook and then invoke capturedConfig.queryFn.
  • Assert: verify commonApiFetch receives the expected order param for ASC and DESC.

Names are descriptive and focused on behaviour, which aligns with the test guidelines. Based on learnings, this looks solid.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsx (1)

8-38: Error-state composition and badge wiring look good.

Props are correctly threaded into GrantExpiryBadge, StatusBadge, GrantDetailsGrid, and GrantErrorDetails, and the error copy uses formatContractLabel as expected. No issues from a typing or UI-structure perspective.

Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
components/user/xtdh/UserPageXtdhGrantedList.tsx (3)

83-91: Fix cache invalidation to use lowercase grantor to match query key construction.

The useXtdhGrantsQuery hook normalizes grantor to lowercase in its query key, but the invalidation call uses the raw grantor value. When grantor contains uppercase letters, the partial key match will fail to invalidate the cached queries.

Apply this diff:

     if (prevPendingCount.current !== pendingCount) {
       queryClient.invalidateQueries({
-        queryKey: [QueryKey.TDH_GRANTS, grantor],
+        queryKey: [QueryKey.TDH_GRANTS, grantor?.toLowerCase() ?? ""],
       });
       invalidateIdentityTdhStats({ identity: grantor });
       prevPendingCount.current = pendingCount;
     }

94-98: Remove inline comment to comply with the "no comments" rule.

The inline comment in the catch block conflicts with the project rule that forbids comments in *.ts/*.tsx files.

Based on coding guidelines, apply this diff:

   const handleRetry = useCallback(() => {
-    refetch().catch(() => {
-      // Errors propagate through the query state that's already rendered.
-    });
+    refetch().catch(() => {});
   }, [refetch]);

104-128: Fix malformed JSX structure - close header div before tabs section.

The div opened at line 104 is not properly closed before the tabs section begins at line 110. This creates incorrect nesting where the tabs/filters become children of the header div instead of siblings.

Apply this diff:

       <div className="tw-flex md:tw-items-center tw-flex-col md:tw-flex-row tw-justify-between tw-gap-4 tw-px-6 tw-pt-6 tw-pb-4 tw-border-b tw-border-solid tw-border-x-0 tw-border-t-0 tw-border-iron-800">
         <h2 className="tw-text-base tw-font-semibold tw-text-iron-100 tw-m-0">
           Granted xTDH
         </h2>
-     
-
+      </div>
       <div className="tw-flex tw-flex-col sm:tw-flex-row sm:tw-items-center tw-justify-between tw-gap-4 tw-border-b tw-border-iron-800">
         <div className="tw-flex-1 tw-min-w-0">
           <UserPageXtdhGrantedListTabs
             activeTab={activeTab}
             onTabChange={handleTabChange}
             fill={false}
             pendingCount={pendingCount}
           />
         </div>
         {activeTab === "ACTIVE" && (
           <div className="tw-w-full sm:tw-w-auto">
             <UserPageXtdhGrantedListSubFilters
               activeSubFilter={activeSubFilter}
               onSubFilterChange={handleSubFilterChange}
             />
           </div>
         )}
       </div>
- </div>
🧹 Nitpick comments (3)
components/user/xtdh/UserPageXtdhGrantedList.tsx (2)

40-40: Remove unused activeSortDirection variable.

activeSortDirection is destructured but never referenced. Only apiSortDirection is used in the query call.

Apply this diff to remove the unused variable:

   const {
     activeTab,
     activeSubFilter,
     activeSortField,
-    activeSortDirection,
     apiSortDirection,
     handleTabChange,
     handleSubFilterChange,
   } = useUserPageXtdhGrantedListFilters();

55-55: Remove unused isFetching variable.

isFetching is destructured but never referenced in the component.

Apply this diff to remove the unused variable:

   const {
     grants,
     isLoading,
     isError,
-    isFetching,
     errorMessage,
     refetch,
     hasNextPage,
     fetchNextPage,
     isFetchingNextPage,
   } = useXtdhGrantsQuery({
components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (1)

66-87: LGTM! Solid polymorphic component implementation.

The wrapper logic correctly handles both li and div rendering, and the conditional button wrapper provides proper interaction when onSelect is provided. The button is keyboard-accessible and maintains the card's visual styling.

Optional: Consider adding aria-label for enhanced screen reader support.

While the current implementation is functional and accessible (the button content includes descriptive text), you could optionally add an aria-label to the button when onSelect is provided for more explicit screen reader context:

       {onSelect ? (
         <button
           type="button"
           onClick={onSelect}
+          aria-label={`View details for ${metadata?.name ?? tokenLabel}`}
           className="tw-flex tw-w-full tw-flex-col tw-gap-3 tw-rounded-xl tw-border-0 tw-bg-transparent tw-p-0 tw-text-left tw-text-inherit"
         >

This enhancement would make the interactive purpose more explicit to assistive technologies, though the existing implementation already provides good accessibility through its descriptive content structure.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f52692 and 949a26b.

📒 Files selected for processing (16)
  • components/user/xtdh/UserPageXtdhGrantedList.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantList.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantDetailsGrid.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantListItemContainer.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx (1 hunks)
  • components/xtdh/received/collection-card-content/index.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/index.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx (1 hunks)
  • components/xtdh/received/collections-controls/index.tsx (1 hunks)
  • components/xtdh/received/index.tsx (1 hunks)
  • components/xtdh/received/subcomponents/XtdhCollectionsList.tsx (1 hunks)
  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantList.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsx
  • components/xtdh/received/collection-card-content/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantDetailsGrid.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsx
  • components/xtdh/received/collections-controls/index.tsx
  • components/xtdh/received/index.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantListItemContainer.tsx
  • components/xtdh/received/subcomponents/XtdhCollectionsList.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
🧠 Learnings (4)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not include any comments in the code; it should be self-explanatory

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
🧬 Code graph analysis (2)
components/xtdh/received/collection-tokens/index.tsx (11)
components/xtdh/received/collection-tokens/types.ts (1)
  • XtdhCollectionTokensPanelProps (9-16)
components/xtdh/received/hooks/useXtdhTokensFilters.ts (1)
  • useXtdhTokensFilters (26-91)
components/xtdh/received/collection-tokens/hooks/useXtdhTokenSelection.ts (1)
  • useXtdhTokenSelection (11-28)
components/xtdh/received/collection-tokens/hooks/useCollectionContractDetails.ts (1)
  • useCollectionContractDetails (17-60)
hooks/useXtdhTokensQuery.ts (1)
  • useXtdhTokensQuery (45-127)
components/xtdh/received/collection-tokens/utils/getTokenLabel.ts (1)
  • getTokenLabel (1-7)
components/xtdh/received/collection-tokens/subcomponents/CollectionBreadcrumbs.tsx (1)
  • CollectionBreadcrumbs (10-47)
components/xtdh/received/collection-card-content/index.tsx (1)
  • XtdhReceivedCollectionCard (34-183)
components/xtdh/received/tokens-controls/index.tsx (1)
  • XtdhTokensControls (15-41)
components/xtdh/received/collection-tokens/XtdhTokensList.tsx (1)
  • XtdhTokensList (9-90)
components/xtdh/received/collection-tokens/subcomponents/CollectionLoadMore.tsx (1)
  • CollectionLoadMore (7-28)
components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (7)
components/xtdh/received/collection-tokens/types.ts (1)
  • ApiXtdhToken (7-7)
components/xtdh/received/collection-tokens/utils/getTokenLabel.ts (1)
  • getTokenLabel (1-7)
components/xtdh/received/utils/formatters.ts (2)
  • formatXtdhValue (18-24)
  • formatXtdhRate (26-28)
components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItemThumbnail.tsx (1)
  • XtdhTokenListItemThumbnail (14-57)
app/layout.tsx (1)
  • metadata (23-23)
components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1)
  • XtdhContributorSummary (9-39)
components/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsx (1)
  • XtdhRatePill (9-29)
🪛 GitHub Check: SonarCloud Code Analysis
components/user/xtdh/UserPageXtdhGrantedList.tsx

[warning] 55-55: Remove this useless assignment to variable "isFetching".

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZrLMjEaNlZM0X20D8X_&open=AZrLMjEaNlZM0X20D8X_&pullRequest=1510


[warning] 40-40: Remove this useless assignment to variable "activeSortDirection".

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZrLMjEaNlZM0X20D8X-&open=AZrLMjEaNlZM0X20D8X-&pullRequest=1510

🔇 Additional comments (10)
components/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsx (1)

1-52: LGTM! Well-structured fallback components.

The implementation is clean and follows all guidelines:

  • Proper TypeScript types with readonly props
  • Tailwind CSS styling as required
  • Accessible with role="alert" on error messages
  • Sensible default messages with override capability
  • Button includes type="button" to prevent unintended form submissions
  • Clear component structure with good separation of concerns
components/xtdh/received/subcomponents/XtdhContributorSummary.tsx (1)

3-13: Props typing and component signature look solid

Readonly props plus Readonly<XtdhContributorSummaryProps> align with the TypeScript + React guidelines and should type‑check cleanly. No changes needed here.

components/xtdh/received/collection-tokens/index.tsx (5)

1-23: LGTM! Clean imports and setup.

The imports are well-organized, and the "use client" directive is correctly placed. The constant TOKENS_PAGE_SIZE provides a clear configuration point for pagination.


24-72: LGTM! Solid hook composition and state management.

The component properly leverages custom hooks for filtering, selection, and data fetching. The requireIdentity parameter is thoughtfully propagated to useXtdhTokensQuery, allowing flexible identity requirements.


74-87: LGTM! Proper error handling and guard clauses.

Both callbacks correctly handle errors by relying on query state propagation, and handleLoadMore includes appropriate guard clauses to prevent redundant fetches.


91-117: LGTM! Well-structured computed values and navigation.

The memoized values are efficiently computed with proper null/undefined handling, and the navigation callbacks clearly manage the state hierarchy (collections → tokens → contributors → grant).


118-190: LGTM! Clear conditional rendering and component composition.

The JSX structure clearly distinguishes between the token detail view and the token list view. The breadcrumb navigation is properly wired to support back navigation through the collection → tokens → contributors hierarchy.

components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (3)

1-20: LGTM! Well-defined interface and imports.

The interface clearly defines all necessary props for the polymorphic list item component. The as prop enables flexible rendering as either a list item or div, which is a solid pattern for reusable components.


22-36: LGTM! Clean value formatting.

The component properly delegates formatting to utility functions, ensuring consistent display of token labels, xTDH values, rates, and contributor counts across the UI.


37-64: LGTM! Responsive layout with proper fallbacks.

The content structure uses a flexible grid layout that adapts across breakpoints. The fallback from metadata?.name to tokenLabel ensures graceful handling of missing metadata.

Comment thread components/xtdh/received/subcomponents/XtdhContributorSummary.tsx
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (5)
components/nft-picker/subcomponents/NftSuggestList.tsx (1)

71-75: Remove inline comments per coding guidelines.

As per coding guidelines, code should be self-explanatory without comments. The component names (NftSuggestHiddenSelect and NftSuggestVirtualList) are descriptive enough to convey their purpose.

Apply this diff:

     >
-      {/* 
-        We render two lists:
-        1. NftSuggestHiddenSelect: A hidden native <select> for accessibility (screen readers).
-        2. NftSuggestVirtualList: The visible, virtualized list for sighted users.
-      */}
       <NftSuggestHiddenSelect items={items} onSelect={onSelect} />
components/nft-picker/index.tsx (4)

27-29: Replace any cast with proper type guard.

The as any cast bypasses TypeScript's type checking and could allow invalid chain values to pass through. Use a proper type guard or validation instead.

Apply this diff:

 function ensureChain(value?: string): SupportedChain {
-  return (value as any) ?? DEFAULT_CHAIN;
+  const validChains: SupportedChain[] = ["ethereum", "polygon", "arbitrum", "optimism", "base"];
+  return validChains.includes(value as SupportedChain) 
+    ? (value as SupportedChain) 
+    : DEFAULT_CHAIN;
 }

Note: Adjust the validChains array to match your actual SupportedChain type definition.


189-200: Improve type safety for error handling.

The as any cast on line 197 bypasses type checking. Import the proper ParseError type from your parsing utilities.

Apply this diff:

+import type { ParseError } from "./utils/parsing";
+
 const handleApplyText = () => {
   try {
     const canonical = parseTokenExpressionToRanges(textValue);
     setSelectionFromText(canonical);
     setParseErrors([]);
     setIsEditingText(false);
   } catch (error) {
     if (Array.isArray(error)) {
-      setParseErrors(error as any);
+      setParseErrors(error as ParseError[]);
     }
   }
 };

226-228: Malformed ID string with spaces breaks accessibility.

The template literal contains erroneous spaces that would produce invalid IDs like "nft - suggestion - 0 ". This will break aria-activedescendant functionality for screen readers.

Apply this diff:

   const activeSuggestionId = isOpen && suggestionList[activeIndex]
-    ? `nft - suggestion - ${activeIndex} `
+    ? `nft-suggestion-${activeIndex}`
     : undefined;

311-315: Missing space in button label.

The label Select All(1,000) should have a space before the parenthesis for better readability: Select All (1,000).

Apply this diff:

                   selectAllLabel={
                     contractTotalSupply
-                      ? `Select All(${new Intl.NumberFormat("en-US").format(contractTotalSupply)})`
+                      ? `Select All (${new Intl.NumberFormat("en-US").format(contractTotalSupply)})`
                       : "Select All"
                   }
🧹 Nitpick comments (1)
components/xtdh/received/collection-tokens/index.tsx (1)

90-117: Consider inlining simple useMemo computations (optional).

Both selectedTokenLabel and selectedTokenId are cheap derivations from a single selectedToken object and don’t depend on expensive work. You could compute them directly during render instead of via useMemo to slightly simplify the component; React 19 optimizations make memoization for such simple selectors largely unnecessary.

This is stylistic; current code is correct.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 949a26b and 7da7c61.

📒 Files selected for processing (10)
  • components/nft-picker/index.tsx (1 hunks)
  • components/nft-picker/subcomponents/NftPickerSearch.tsx (1 hunks)
  • components/nft-picker/subcomponents/NftSuggestList.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGranted.tsx (1 hunks)
  • components/user/xtdh/UserXtdhTestModeBanner.tsx (1 hunks)
  • components/user/xtdh/user-page-xtdh-grant/index.tsx (1 hunks)
  • components/xtdh/received/collection-card-content/index.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/index.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx (1 hunks)
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • components/user/xtdh/UserPageXtdhGranted.tsx
  • components/nft-picker/subcomponents/NftPickerSearch.tsx
  • components/user/xtdh/user-page-xtdh-grant/index.tsx
  • components/xtdh/received/collection-card-content/index.tsx
  • components/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
  • components/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsx
  • components/user/xtdh/UserXtdhTestModeBanner.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/nft-picker/index.tsx
  • components/nft-picker/subcomponents/NftSuggestList.tsx
  • components/xtdh/received/collection-tokens/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/nft-picker/index.tsx
  • components/nft-picker/subcomponents/NftSuggestList.tsx
  • components/xtdh/received/collection-tokens/index.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/nft-picker/index.tsx
  • components/nft-picker/subcomponents/NftSuggestList.tsx
  • components/xtdh/received/collection-tokens/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/nft-picker/index.tsx
  • components/nft-picker/subcomponents/NftSuggestList.tsx
  • components/xtdh/received/collection-tokens/index.tsx
🧠 Learnings (7)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/nft-picker/index.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/nft-picker/index.tsx
  • components/nft-picker/subcomponents/NftSuggestList.tsx
📚 Learning: 2025-11-25T08:37:44.688Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:44.688Z
Learning: Applies to app/api/**/*.{ts,tsx} : Use TypeScript types for request parameters and responses; avoid `any` unless a 3rd-party payload truly has no shape guarantees.

Applied to files:

  • components/nft-picker/index.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code

Applied to files:

  • components/nft-picker/index.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : Must pass `tsc --noEmit` type checking

Applied to files:

  • components/nft-picker/index.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • components/nft-picker/index.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not include any comments in the code; it should be self-explanatory

Applied to files:

  • components/nft-picker/subcomponents/NftSuggestList.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
components/xtdh/received/collection-tokens/index.tsx (1)

24-189: Overall structure and state orchestration look solid.

Token query wiring, pagination guards, selection state, and the conditional rendering between list vs. contributor panel are all coherent and type‑safe. Props are wrapped in Readonly<…>, Tailwind is used consistently, and hooks have appropriate dependency arrays. I don’t see correctness issues in this component as written.

Comment thread components/nft-picker/index.tsx
Comment thread components/xtdh/received/collection-tokens/index.tsx
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
__tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts (1)

4-20: Solid coverage for default vs date-only formatting; consider trimming inline comments

Both tests nicely exercise the default includeTime behaviour vs includeTime: false using loose assertions (colon presence and year inclusion) instead of brittle exact strings, which should be stable across time zones for your current locale setup. To align with the repo guideline of avoiding comments in .ts files, you could fold the // Expect ... notes into the test names and drop the inline comments.

components/user/xtdh/utils/xtdhGrantFormatters.ts (1)

101-104: formatDateTime logic aligns with tests; consider dropping the inline comment

The normalization (treating > 1_000_000_000_000 as ms, otherwise seconds), explicit timestamp == null || timestamp <= 0 sentinel handling, and Invalid date fallback all look good and are exercised by your new tests via the default vs includeTime: false paths. To comply with the repo guideline that .ts code be comment‑free and self‑explanatory, you can remove the “Normalize timestamp…” comment and rely on the normalizedTimestamp naming and heuristic itself to convey intent.

-  // Normalize timestamp: values > 1 trillion are assumed milliseconds,
-  // otherwise treat as seconds
   const normalizedTimestamp =
     timestamp > 1_000_000_000_000 ? timestamp : timestamp * 1000;

Also applies to: 106-128

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7da7c61 and 6cd0959.

📒 Files selected for processing (14)
  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts (1 hunks)
  • components/user/xtdh/UserXtdhStats.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx (1 hunks)
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (1 hunks)
  • components/user/xtdh/utils/xtdhGrantFormatters.ts (1 hunks)
  • components/xtdh/XtdhPage.tsx (1 hunks)
  • components/xtdh/received/index.tsx (1 hunks)
  • components/xtdh/stats/buildGlobalXtdhStatsContent.ts (1 hunks)
  • components/xtdh/stats/index.tsx (1 hunks)
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx (1 hunks)
  • components/xtdh/stats/subcomponents/XtdhMetricsSection.tsx (1 hunks)
  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx (1 hunks)
  • components/xtdh/stats/subcomponents/XtdhStatCard.tsx (1 hunks)
  • components/xtdh/stats/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • components/xtdh/stats/buildGlobalXtdhStatsContent.ts
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
  • components/xtdh/stats/subcomponents/XtdhMetricsSection.tsx
  • components/xtdh/XtdhPage.tsx
  • components/xtdh/received/index.tsx
  • components/xtdh/stats/index.tsx
  • components/xtdh/stats/types.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx
  • components/xtdh/stats/subcomponents/XtdhStatCard.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
  • components/user/xtdh/utils/xtdhGrantFormatters.ts
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx
  • components/xtdh/stats/subcomponents/XtdhStatCard.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx
  • components/xtdh/stats/subcomponents/XtdhStatCard.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
  • components/user/xtdh/utils/xtdhGrantFormatters.ts
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx
  • components/xtdh/stats/subcomponents/XtdhStatCard.tsx
  • components/user/xtdh/UserXtdhStats.tsx
  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
  • components/user/xtdh/utils/xtdhGrantFormatters.ts
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
🧠 Learnings (6)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/xtdh/stats/subcomponents/XtdhProgressBar.tsx
  • components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx
  • components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components

Applied to files:

  • components/user/xtdh/UserXtdhStats.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/xtdh/UserXtdhStats.tsx
📚 Learning: 2025-11-25T08:37:36.725Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:36.725Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js,jsx} : Use the Arrange – Act – Assert pattern for test structure, keeping assertions focused on behaviour, not implementation details

Applied to files:

  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
📚 Learning: 2025-11-25T08:37:36.725Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:36.725Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js,jsx} : Give each test a clear, descriptive name that conveys the scenario and expected outcome

Applied to files:

  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts
🧬 Code graph analysis (3)
components/xtdh/stats/subcomponents/XtdhStatCard.tsx (1)
components/utils/tooltip/CustomTooltip.tsx (1)
  • CustomTooltip (36-432)
__tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.ts (1)
components/user/xtdh/utils/xtdhGrantFormatters.ts (1)
  • formatDateTime (106-128)
components/user/xtdh/utils/xtdhGrantFormatters.ts (2)
components/nft-picker/utils/errors.ts (1)
  • PARSE_ERROR_ARRAY_NAME (10-10)
components/nft-picker/utils/parsing.ts (1)
  • parseTokenExpressionToRanges (174-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
components/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsx (1)

1-29: Well-typed labeled wrapper around XtdhProgressBar looks solid

Props extension, readonly usage, Tailwind styling, and the ariaLabel ?? label fallback all look correct and accessible. The component is a clean, reusable wrapper over XtdhProgressBar with no obvious correctness or lint issues.

components/xtdh/stats/subcomponents/XtdhProgressBar.tsx (1)

1-30: Accessible, clamped progress bar implementation looks correct

percentage clamping, ARIA attributes, and styling are all correct and self-contained. The interface is minimal but sufficient, and there are no apparent type, accessibility, or runtime issues.

components/user/xtdh/UserXtdhStats.tsx (5)

1-3: LGTM: Clean imports following project conventions.

Direct named imports and proper module paths are used throughout.


5-10: LGTM: Props interface correctly uses readonly modifiers.

All properties are properly typed with readonly as required by the coding guidelines.


12-19: LGTM: Component signature and core calculations are sound.

The Readonly<> wrapper on props and the Math.max guard for outbound prevent negative values correctly.


21-43: LGTM: Stats array is well-structured and consistent.

Data-driven approach with uniform formatting and proper optional onClick handling.


52-79: LGTM: Render logic is clean and accessible.

Proper TailwindCSS usage, responsive grid layout, component composition, and accessibility attributes are all well-implemented.

components/xtdh/stats/subcomponents/XtdhStatCard.tsx (2)

54-68: Excellent accessibility implementation.

The tooltip implementation correctly uses aria-describedby to link to screen reader text, while marking the visual tooltip content as aria-hidden. This follows WCAG best practices and ensures assistive technology receives the tooltip information without duplication.


70-77: Good use of tabular numerals.

The tw-tabular-nums class ensures numeric values align consistently, which is particularly important for stat cards where values may update or be compared side-by-side. The visual hierarchy between value and subtext is well established.

components/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts (1)

38-117: View-model composition, contract loading, and error derivation look cohesive

The hook cleanly derives contractAddress/contractLabel, maps chains with an explicit unsupported-chain path, and gates useContractOverviewQuery via enabled while still calling the hook unconditionally, which keeps React Hooks happy. The split between createBaseGrantDetails and buildGrantDetails gives you a nice layering where contract metadata is optional, and normalizeErrorDetails + deriveErrorDetails ensure user-facing errors are non-empty and consistently prioritized. The returned GrantItemViewModel shape is easy to consume and appears type-safe given the imported types.

Also applies to: 120-176, 178-186

components/user/xtdh/utils/xtdhGrantFormatters.ts (6)

7-16: Confirm ParseErrorArray runtime shape matches this type guard

This guard assumes that parse errors thrown by parseTokenExpressionToRanges are Error instances with name === PARSE_ERROR_ARRAY_NAME and an errors array. If ParseErrorArray is actually implemented as an augmented array (e.g. an array with Error-like props assigned onto it), error instanceof Error would be false and this guard would never match, causing formatTargetTokens to rethrow parse errors instead of logging and falling back to fallbackLabel. Please double‑check the current ParseErrorArray implementation in NftPicker.utils and either keep this instanceof Error check (if it’s a real Error subtype) or switch to an array-based check (Array.isArray(error) plus property checks) to ensure the guard narrows correctly.


44-66: Token count info helpers handle edge cases sensibly

getTargetTokensCountInfo’s treatment of null/undefined as “All tokens”, invalid/negative values as “unknown”, and truncation for fractional counts is straightforward and defensive, and formatTargetTokensCount cleanly reuses it rather than duplicating logic. This matches how the grant view-model consumes TargetTokensCountInfo.

Also applies to: 138-141


68-99: Graceful fallback on parse errors in formatTargetTokens

The parse‑then‑canonicalize flow with parseTokenExpressionToRanges and a console.error + fallbackLabel path for recognized parse errors is a good balance between robustness and visibility. Non‑parse errors are correctly rethrown so unexpected failures don’t get silently swallowed. Once the isParseErrorArray guard is confirmed correct, this should give predictable UX for malformed token selections.


130-136: Numeric amount formatting is defensive and consistent

formatAmount guarding against non‑finite values and truncating via Math.trunc before using the shared numberFormatter is simple and robust, and matches how you format counts and totals elsewhere in the grant view‑model.


142-166: Decimal formatter caching avoids repeated Intl.NumberFormat allocations

The small createDecimalFormatter helper plus decimalFormatters map is a nice way to avoid re‑creating Intl.NumberFormat instances for each call while still supporting different precision levels. The non‑null assertion is safe here since you always populate the map for a given key before reading it.


168-206: Adaptive precision in formatTdhRatePerToken looks well thought‑out

The function defensively bails out on non‑finite inputs or non‑positive token counts, then uses PRECISION_THRESHOLDS to pick a suitable number of fraction digits based on the absolute per‑token value. This should produce human‑friendly labels across very small and very large rates without over‑ or under‑precision.

Comment thread components/user/xtdh/UserXtdhStats.tsx
Comment thread components/xtdh/stats/subcomponents/XtdhStatCard.tsx
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Dec 5, 2025

Quality Gate Passed Quality Gate passed

Issues
0 New issues
70 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.2% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
components/user/xtdh/UserPageXtdh.tsx (1)

65-74: Authorization bug: Fix canGrant identity check so missing consolidation keys don't grant access

As noted in a previous review, connectedProfile.consolidation_key === profile.consolidation_key returns true when both are undefined/null, which incorrectly allows canGrant for profiles lacking consolidation keys.

Apply the fix from the previous review to only treat consolidation keys as matching when both are present and equal.

components/user/xtdh/UserPageXtdhGrantedList.tsx (3)

83-91: Fix cache invalidation to use lowercase grantor to match query key construction.

The useXtdhGrantsQuery hook normalizes grantor to lowercase in its query key, but the invalidation call at line 86 uses the raw grantor value. When grantor contains uppercase letters, the partial key match will fail to invalidate the cached queries, causing stale data to persist after pending count changes.

Apply this diff to fix the cache key mismatch:

     if (prevPendingCount.current !== pendingCount) {
       queryClient.invalidateQueries({
-        queryKey: [QueryKey.TDH_GRANTS, grantor],
+        queryKey: [QueryKey.TDH_GRANTS, grantor?.toLowerCase() ?? ""],
       });
       invalidateIdentityTdhStats({ identity: grantor });
       prevPendingCount.current = pendingCount;
     }

94-98: Remove inline comment to comply with "no comments" coding guideline.

The catch block contains an inline comment that violates the project rule: "Do not include any comments in the code; it should be self-explanatory" (applies to **/*.{ts,tsx,js,jsx}).

Based on coding guidelines, remove the comment:

   const handleRetry = useCallback(() => {
-    refetch().catch(() => {
-      // Errors propagate through the query state that's already rendered.
-    });
+    refetch().catch(() => {});
   }, [refetch]);

104-128: Fix malformed JSX structure - close header div before tabs section.

The div opened at line 104 is never properly closed before the tabs section begins at line 110. This creates malformed JSX and likely produces unintended layout where the header and tabs are nested incorrectly.

Apply this diff to properly close the header div:

       <div className="tw-flex md:tw-items-center tw-flex-col md:tw-flex-row tw-justify-between tw-gap-4 tw-px-6 tw-pt-6 tw-pb-4">
         <h2 className="tw-text-base tw-font-semibold tw-text-iron-100 tw-m-0">
           Granted xTDH
         </h2>
-     
-
+      </div>
       <div className="tw-flex tw-flex-col sm:tw-flex-row sm:tw-items-center tw-justify-between tw-gap-4 tw-border-b tw-border-iron-800">
         <div className="tw-flex-1 tw-min-w-0">
           <UserPageXtdhGrantedListTabs
             activeTab={activeTab}
             onTabChange={handleTabChange}
             fill={false}
             pendingCount={pendingCount}
           />
         </div>
         {activeTab === "ACTIVE" && (
           <div className="tw-w-full sm:tw-w-auto">
             <UserPageXtdhGrantedListSubFilters
               activeSubFilter={activeSubFilter}
               onSubFilterChange={handleSubFilterChange}
             />
           </div>
         )}
       </div>
- </div>
🧹 Nitpick comments (1)
components/user/xtdh/UserPageXtdh.tsx (1)

111-130: Consider a more robust approach for tab-switch-then-scroll

The setTimeout on line 126 is fragile—rendering may take longer than 100ms on slower devices, causing the scroll/focus to fail silently.

A more robust alternative would use a ref or callback passed to UserPageXtdhGranted to signal when the grant section is mounted, or a useEffect in that child component to scroll/focus on mount when triggered. However, given this is a UX enhancement and the current approach is common practice, it's acceptable if you prefer to defer this improvement.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd0959 and ca27b2e.

📒 Files selected for processing (4)
  • components/user/xtdh/UserPageXtdh.tsx (1 hunks)
  • components/user/xtdh/UserPageXtdhGrantedList.tsx (1 hunks)
  • components/xtdh/received/index.tsx (1 hunks)
  • components/xtdh/received/subcomponents/XtdhCollectionsList.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/xtdh/received/subcomponents/XtdhCollectionsList.tsx
  • components/xtdh/received/index.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

Files:

  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

Files:

  • components/user/xtdh/UserPageXtdh.tsx
  • components/user/xtdh/UserPageXtdhGrantedList.tsx
🧠 Learnings (4)
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/user/xtdh/UserPageXtdh.tsx
📚 Learning: 2025-12-03T14:52:34.255Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.255Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not include any comments in the code; it should be self-explanatory

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/user/xtdh/UserPageXtdhGrantedList.tsx
🧬 Code graph analysis (1)
components/user/xtdh/UserPageXtdh.tsx (6)
generated/models/ApiIdentity.ts (1)
  • ApiIdentity (17-185)
components/auth/Auth.tsx (1)
  • AuthContext (83-93)
components/user/xtdh/UserXtdhTestModeBanner.tsx (1)
  • UserXtdhTestModeBanner (1-29)
components/user/xtdh/UserPageXtdhStatsHeader.tsx (1)
  • UserPageXtdhStatsHeader (15-56)
components/user/xtdh/UserPageXtdhGranted.tsx (1)
  • UserPageXtdhGranted (6-30)
components/xtdh/user/received/index.tsx (1)
  • UserPageXtdhReceived (10-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)

Comment thread components/user/xtdh/UserPageXtdh.tsx
@simo6529 simo6529 merged commit ca17f70 into main Dec 5, 2025
10 checks passed
@simo6529 simo6529 deleted the xTDH branch December 5, 2025 10:55
@coderabbitai coderabbitai Bot mentioned this pull request Jan 8, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Feb 3, 2026
This was referenced Mar 10, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 13, 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.

3 participants