Conversation
WalkthroughMigrates 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
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)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 directlyReduces 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 consolidatingtitleandmetaLabelif they remain identical.Currently,
titleandmetaLabelhave 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
metaLabelfromtitleby default, with an optional overrideOnly implement this if the fields are confirmed to serve the same purpose long-term.
87-88:UserPageTabTypealias may cause confusion.The
UserPageTabTypealias duplicatesUserPageTabKey. 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
📒 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 propsUse TypeScript for implementation code
Files:
components/profile-activity/list/items/ProfileActivityLogRate.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxy.tsxapp/[user]/waves/page.tsxcomponents/user/xtdh/UserPageXtdh.tsxcomponents/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsxapp/[user]/collected/page.tsxapp/[user]/rep/page.tsxapp/[user]/groups/page.tsx__tests__/components/user/layout/UserPageTab.test.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyAction.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsxapp/[user]/page.tsxapp/[user]/proxy/page.tsx__tests__/components/user/layout/UserPageTabs.test.tsxcomponents/user/layout/userTabs.config.tsapp/[user]/identity/page.tsxcomponents/header/header-search/HeaderSearchModalItem.tsxapp/[user]/stats/page.tsxcomponents/user/utils/CommonProfileLink.tsxcomponents/user/layout/UserPageTab.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyActionState.tsxapp/[user]/subscriptions/page.tsxhelpers/Helpers.tscomponents/user/layout/UserPageTabs.tsxapp/[user]/followers/page.tsx__tests__/components/user/utils/CommonProfileLink.test.tsxcomponents/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsxapp/[user]/xtdh/page.tsx__tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsxcomponents/header/header-search/HeaderSearchModal.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/profile-activity/list/items/ProfileActivityLogRate.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxy.tsxapp/[user]/waves/page.tsxcomponents/user/xtdh/UserPageXtdh.tsxcomponents/user/stats/activity/wallet/table/row/UserPageStatsActivityWalletTableRowSecondAddress.tsxapp/[user]/collected/page.tsxapp/[user]/rep/page.tsxapp/[user]/groups/page.tsx__tests__/components/user/layout/UserPageTab.test.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyAction.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyActionChange.tsxapp/[user]/page.tsxapp/[user]/proxy/page.tsx__tests__/components/user/layout/UserPageTabs.test.tsxapp/[user]/identity/page.tsxcomponents/header/header-search/HeaderSearchModalItem.tsxapp/[user]/stats/page.tsxcomponents/user/utils/CommonProfileLink.tsxcomponents/user/layout/UserPageTab.tsxcomponents/profile-activity/list/items/ProfileActivityLogProxyActionState.tsxapp/[user]/subscriptions/page.tsxcomponents/user/layout/UserPageTabs.tsxapp/[user]/followers/page.tsx__tests__/components/user/utils/CommonProfileLink.test.tsxcomponents/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.tsxapp/[user]/xtdh/page.tsx__tests__/components/profile-activity/list/items/utils/ProfileActivityLogItemWrapper.test.tsxcomponents/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.tsxapp/[user]/collected/page.tsxapp/[user]/rep/page.tsxapp/[user]/groups/page.tsxapp/[user]/page.tsxapp/[user]/proxy/page.tsxapp/[user]/identity/page.tsxapp/[user]/stats/page.tsxapp/[user]/subscriptions/page.tsxapp/[user]/followers/page.tsxapp/[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.tsxapp/[user]/collected/page.tsxapp/[user]/rep/page.tsxapp/[user]/groups/page.tsxapp/[user]/page.tsxapp/[user]/proxy/page.tsxapp/[user]/identity/page.tsxapp/[user]/stats/page.tsxapp/[user]/subscriptions/page.tsxapp/[user]/followers/page.tsxapp/[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.tsxapp/[user]/collected/page.tsxapp/[user]/rep/page.tsxapp/[user]/groups/page.tsxapp/[user]/page.tsxapp/[user]/proxy/page.tsxapp/[user]/identity/page.tsxapp/[user]/stats/page.tsxapp/[user]/subscriptions/page.tsxapp/[user]/followers/page.tsxapp/[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/reactand@testing-library/user-eventfor 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.IDENTITYto the centralizedUSER_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
typeimport 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.IDENTITYas the default path, aligning with the similar change inHeaderSearchModalItem.tsx.Also applies to: 131-131
app/[user]/xtdh/page.tsx (1)
1-17: Approve XTDH user tab page implementationFollows established pattern and
USER_PAGE_TAB_IDS.XTDHis 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.routeandTAB_CONFIG.metaLabelensures 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
tabTargetbased onrating_matternow 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.idto 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_IDSconstants, 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
defaultPathparameter now referencesUSER_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_IDSandUSER_PAGE_TAB_MAPensure 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_IDSmaintains 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/metadataUsing 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 stateDeriving 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
UserPageTabKeyderived from TABS ensures compile-time safety- Visibility context pattern enables conditional tab rendering
- Utility functions provide convenient lookup methods
- Use of
as const satisfiesmaintains type safety while allowing inferenceThis refactoring successfully consolidates tab metadata into a single source of truth.
14-21: Emptyroutefor Brain tab is intentional (maps to the default user page).
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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)
There was a problem hiding this comment.
Actionable comments posted: 5
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
generated/models/ApiCreateTdhGrant.tsis excluded by!**/generated/**generated/models/ApiPageSortDirection.tsis excluded by!**/generated/**generated/models/ApiTdhGrant.tsis excluded by!**/generated/**generated/models/ApiTdhGrantStatus.tsis excluded by!**/generated/**generated/models/ApiTdhGrantTargetChain.tsis excluded by!**/generated/**generated/models/ApiTdhGrantsPage.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis 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 propsUse TypeScript for implementation code
Files:
types/xtdh.tscomponents/user/xtdh/UserPageXtdhReceived.tsxcomponents/user/xtdh/UserPageXtdhGrantSubmit.tsxcomponents/user/xtdh/granted-list/UserPageXtdhGrantList.tsxcomponents/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantSelection.tsxcomponents/user/xtdh/UserPageXtdhGranted.tsxcomponents/user/xtdh/UserPageXtdhStatsHeader.tsxcomponents/user/xtdh/UserPageXtdhGrantAmount.tsxcomponents/user/xtdh/UserPageXtdh.tsxcomponents/nft-picker/NftPicker.tsxcomponents/user/xtdh/UserPageXtdhGrantSummary.tsxapp/api/profiles/[profile]/xtdh/stats/route.tsservices/6529api.tscomponents/user/xtdh/utils/xtdhGrantFormatters.tscomponents/react-query-wrapper/ReactQueryWrapper.tsxcomponents/nft-picker/useAlchemyClient.tscomponents/user/xtdh/UserPageXtdhGrant.tsxcomponents/user/xtdh/UserPageXtdhGrantValidity.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/user/xtdh/granted-list/UserPageXtdhGrantedListContent.tsxcomponents/nft-picker/NftPicker.types.tshooks/useXtdhStats.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/user/xtdh/UserPageXtdhReceived.tsxcomponents/user/xtdh/UserPageXtdhGrantSubmit.tsxcomponents/user/xtdh/granted-list/UserPageXtdhGrantList.tsxcomponents/user/xtdh/granted-list/UserPageXtdhGrantListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantSelection.tsxcomponents/user/xtdh/UserPageXtdhGranted.tsxcomponents/user/xtdh/UserPageXtdhStatsHeader.tsxcomponents/user/xtdh/UserPageXtdhGrantAmount.tsxcomponents/user/xtdh/UserPageXtdh.tsxcomponents/nft-picker/NftPicker.tsxcomponents/user/xtdh/UserPageXtdhGrantSummary.tsxcomponents/react-query-wrapper/ReactQueryWrapper.tsxcomponents/user/xtdh/UserPageXtdhGrant.tsxcomponents/user/xtdh/UserPageXtdhGrantValidity.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/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
fetchUrlandpostData.
40-53: Header and method precedence are correct as written. Spreadinginitbefore explicitmethod: "POST"andheaderscorrectly enforces the intended overrides.
10-18: Remove header override warning. Infetch(url, { ...init, headers }), the explicitheadersproperty (placed after...init) always overrides anyinit.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_toat 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.idas 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 aruntime, so it runs on Node.js wheresetTimeoutis supported. If you switch to edge runtime later, ensure delays are acceptable.
There was a problem hiding this comment.
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.Elementto 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
📒 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 propsUse TypeScript for implementation code
Files:
components/user/xtdh/UserPageXtdhStatsHeader.tsxcomponents/user/xtdh/UserPageXtdhReceived.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/user/xtdh/UserPageXtdhStatsHeader.tsxcomponents/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
readonlyas 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
StatCardcomponent 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.
There was a problem hiding this comment.
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"androle="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
onRetryis 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 optimizationSwitch
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 mutationMark 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 guidelinesUse 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 responsesAdd 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 indicatorSwap 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
tabparam is present- Rendering the correct view component based on active tab
canGrantpermission 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 explicitaria-labelwould improve screen reader experience, especially whenactionLabelis 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 ofuserEvent.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: Usenext/imagefor avatars to reduce CLS and enable optimizationSwap
<img>fornext/imageand 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 responsesThe handler uses
Math.random()and artificial delay; adddynamic = "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: Preferrequest.nextUrl.searchParamsover constructing a new URLSlightly 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
paramsisn’t a Promise in Next route handlers. RemovePromise<>and theawait.-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 withReadonly<>for consistency with project styleOther 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 tabCreating 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 changesSame 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 enumMap 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 refetchingPrevent 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 awaitNext.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-queryPass 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.stringifyPrefer 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 rangePrevent 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 rangeAvoid 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) andXtdhReceivedError(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 useType[]without thereadonlymodifier. 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" | stringeffectively 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.
There was a problem hiding this comment.
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) anduseTokenMetadataQuery(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-elementESLint 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: GuardBigIntconversion to avoid runtime crashes on malformed token IDsDirect
BigInt(t.token)will throw if the API returns an invalid string. Consider using defensive conversion withflatMapand 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
filterand then explicitly reassignslabel: filter.label. Since the spread already includes all properties fromfilter, 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_FILTERSalready matches the expectedCommonSelectItem<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 unusedisFetchingvariable.The
isFetchingvalue is destructured fromuseXtdhGrantsQuerybut 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.
handleRetryandhandleLoadMoreare implemented correctly and already self-explanatory. The inline//comments inside their.catchblocks 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 statecomments 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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/xtdh/received/collection-card-content/index.tsxcomponents/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsxcomponents/xtdh/received/index.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsxcomponents/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsxcomponents/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/xtdh/received/collection-card-content/index.tsxcomponents/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsxcomponents/xtdh/received/index.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsxcomponents/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsxcomponents/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/xtdh/received/collection-card-content/index.tsxcomponents/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsxcomponents/xtdh/received/index.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsxcomponents/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsxcomponents/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/xtdh/received/collection-card-content/index.tsxcomponents/xtdh/received/collection-card-content/subcomponents/XtdhRatePill.tsxcomponents/xtdh/received/index.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantTokensDisclosure.tsxcomponents/user/xtdh/user-page-xtdh-granted-list/components/UserPageXtdhGrantedListSubFilters.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsxcomponents/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/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.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensSkeleton.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/StatusBadge.tsxcomponents/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/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.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/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.tsxcomponents/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
readonlyMinor note: the
Readonly<>wrapper on line 13 is technically redundant since the interface already hasreadonlyon 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 consistentImports, 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 wiredOverall 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
readonlymodifiers 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
useContractOverviewQueryenabling, and the selectable vs. static interaction modes are all handled cleanly. Metric formatting and theXtdhRatePillintegration 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,
controlsDisabledandshowLoadMoreare 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+onSelectpattern provide a flexible token row that works in both list and detail contexts. Wrapper semantics, Tailwind classes, and reuse ofXtdhTokenListItemThumbnail,XtdhContributorSummary, andXtdhRatePillall look good.
There was a problem hiding this comment.
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) anduseTokenMetadataQuery(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-elementESLint rule, use<Image>fromnext/imagefor 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 viatw-relative, so the Image will fill it correctly. Ensure external image domains are configured innext.config.jsif needed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/collected/cards/UserPageCollectedCard.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/user/collected/cards/UserPageCollectedCard.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/user/collected/cards/UserPageCollectedCard.tsxcomponents/user/collected/cards/UserPageCollectedNetworkCards.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/user/collected/cards/UserPageCollectedCard.tsxcomponents/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.tsxcomponents/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
useStatealigns 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.
There was a problem hiding this comment.
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 (
FontAwesomeIconfrom@fortawesome/react-fontawesomeandfaPlusfrom@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
FontAwesomeIconwithfaPlusicon 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
generationRateis0, the division(Math.floor(outbound * 10) / 10) / (Math.floor(generationRate * 10) / 10)producesNaN, resulting in invalid CSSwidth: "NaN%". Add a guard to handlegenerationRate <= 0by returning0for 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 lacksrole,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 duplicationThe 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
StatWithRatecomponent 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 whenisUnsupportedChainistrue,shouldLoadContractisfalse, souseContractOverviewQuerywon't execute andfetchedContractwill beundefinedanyway.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
tokensDescriptioncould 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” guidelineThe
catchblock 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-emptyis 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-emptyis enforced for catch blocks, consider adjusting the helper to returnnullinstead of throwing so you can remove thetry/catchentirely in this component.As per coding guidelines, TS/TSX files should be comment‑free.
93-96: Minor: avoid wrappingrefetchin a new arrowYou can pass
refetchdirectly toonRetryinstead 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 droppinguseMemoand deriving sort values directly during render
activeSortFieldandactiveSortDirectionare cheap to compute fromsearchParams, souseMemois not buying much here and slightly increases complexity and hook dependencies. You can simplify by computing them directly on each render and removing the unuseduseMemoimport:-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 forcommonApiFetchinstead ofrequire+ type assertionYou can lean on Jest’s module mocking and avoid
requireentirely, 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
commonApiFetchfor the expectations below. As per coding guidelines, prefer modern TS imports overrequire.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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/xtdh/UserXtdhTestModeBanner.tsx__tests__/hooks/useXtdhTokensQuery.test.tscomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsxcomponents/user/xtdh/UserXtdhStats.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.tscomponents/user/utils/stats/UserStatsRow.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/hooks/useXtdhCollectionsFilters.tscomponents/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 addreadonlybefore props in React components
Files:
components/user/xtdh/UserXtdhTestModeBanner.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsxcomponents/user/xtdh/UserXtdhStats.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/utils/stats/UserStatsRow.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/user/xtdh/UserXtdhTestModeBanner.tsx__tests__/hooks/useXtdhTokensQuery.test.tscomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsxcomponents/user/xtdh/UserXtdhStats.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.tscomponents/user/utils/stats/UserStatsRow.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/hooks/useXtdhCollectionsFilters.tscomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/user/xtdh/UserXtdhTestModeBanner.tsx__tests__/hooks/useXtdhTokensQuery.test.tscomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhGrantDetailsPanel.tsxcomponents/user/xtdh/UserXtdhStats.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemError.tsxcomponents/xtdh/received/collection-tokens/token-contributors/subcomponents/XtdhTokenContributorsListItem.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.tscomponents/user/utils/stats/UserStatsRow.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantItemContent.tsxcomponents/xtdh/received/hooks/useXtdhCollectionsFilters.tscomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/subcomponents/GrantExpiryBadge.tsx
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.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.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.tscomponents/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 intsconfig.jsonand 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 logicProps are correctly typed (including
readonly),routeHandleencoding 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
readonlyand wrapped inReadonly<…>.useXtdhGrantQueryresult is handled cleanly with loading, error, and “no grant/viewModel” guards.- The
useMemoview model neatly isolates formatting/mapping logic and only depends ongrant.- 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 usenext/imageand 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-safeThe 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, andprofileHref, combined withisEthereumAddress/isAutoGeneratedHandle-driven truncation and conditional tooltip wrapping, are defensive and align well with the shared helpers andXtdhRatePillAPI.__tests__/hooks/useXtdhTokensQuery.test.ts (1)
18-88: Tests cleanly exercise ASC/DESC behaviour and follow AAA structureThe shared
defaultQueryResultplus thebeforeEachreset keep the mock state predictable, and both tests clearly:
- Arrange: override
mockUseInfiniteQueryto capture config.- Act: call the hook and then invoke
capturedConfig.queryFn.- Assert: verify
commonApiFetchreceives the expectedorderparam 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, andGrantErrorDetails, and the error copy usesformatContractLabelas expected. No issues from a typing or UI-structure perspective.
There was a problem hiding this comment.
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
useXtdhGrantsQueryhook normalizesgrantorto lowercase in its query key, but the invalidation call uses the rawgrantorvalue. Whengrantorcontains 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/*.tsxfiles.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 unusedactiveSortDirectionvariable.
activeSortDirectionis destructured but never referenced. OnlyapiSortDirectionis 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 unusedisFetchingvariable.
isFetchingis 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
lianddivrendering, and the conditional button wrapper provides proper interaction whenonSelectis 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-labelto the button whenonSelectis 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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokenListItem.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/xtdh/received/subcomponents/XtdhContributorSummary.tsxcomponents/xtdh/received/collection-tokens/index.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsxcomponents/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.tsxcomponents/xtdh/received/collection-tokens/subcomponents/XtdhTokensFallbacks.tsxcomponents/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".
[warning] 40-40: Remove this useless assignment to variable "activeSortDirection".
🔇 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
readonlyprops- 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 solidReadonly 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_SIZEprovides 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
requireIdentityparameter is thoughtfully propagated touseXtdhTokensQuery, 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
handleLoadMoreincludes 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
asprop 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?.nametotokenLabelensures graceful handling of missing metadata.
There was a problem hiding this comment.
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 (
NftSuggestHiddenSelectandNftSuggestVirtualList) 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: Replaceanycast with proper type guard.The
as anycast 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
validChainsarray to match your actualSupportedChaintype definition.
189-200: Improve type safety for error handling.The
as anycast on line 197 bypasses type checking. Import the properParseErrortype 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 breakaria-activedescendantfunctionality 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 simpleuseMemocomputations (optional).Both
selectedTokenLabelandselectedTokenIdare cheap derivations from a singleselectedTokenobject and don’t depend on expensive work. You could compute them directly during render instead of viauseMemoto 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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/nft-picker/index.tsxcomponents/nft-picker/subcomponents/NftSuggestList.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/nft-picker/index.tsxcomponents/nft-picker/subcomponents/NftSuggestList.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/nft-picker/index.tsxcomponents/nft-picker/subcomponents/NftSuggestList.tsxcomponents/xtdh/received/collection-tokens/index.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/nft-picker/index.tsxcomponents/nft-picker/subcomponents/NftSuggestList.tsxcomponents/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.tsxcomponents/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.
There was a problem hiding this comment.
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 commentsBoth tests nicely exercise the default
includeTimebehaviour vsincludeTime: falseusing 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.tsfiles, you could fold the// Expect ...notes into the test names and drop the inline comments.components/user/xtdh/utils/xtdhGrantFormatters.ts (1)
101-104:formatDateTimelogic aligns with tests; consider dropping the inline commentThe normalization (treating
> 1_000_000_000_000as ms, otherwise seconds), explicittimestamp == null || timestamp <= 0sentinel handling, andInvalid datefallback all look good and are exercised by your new tests via the default vsincludeTime: falsepaths. To comply with the repo guideline that.tscode be comment‑free and self‑explanatory, you can remove the “Normalize timestamp…” comment and rely on thenormalizedTimestampnaming 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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/xtdh/stats/subcomponents/XtdhProgressBar.tsxcomponents/xtdh/stats/subcomponents/XtdhStatCard.tsxcomponents/user/xtdh/UserXtdhStats.tsx__tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.tscomponents/user/xtdh/utils/xtdhGrantFormatters.tscomponents/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/xtdh/stats/subcomponents/XtdhProgressBar.tsxcomponents/xtdh/stats/subcomponents/XtdhStatCard.tsxcomponents/user/xtdh/UserXtdhStats.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/xtdh/stats/subcomponents/XtdhProgressBar.tsxcomponents/xtdh/stats/subcomponents/XtdhStatCard.tsxcomponents/user/xtdh/UserXtdhStats.tsx__tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.tscomponents/user/xtdh/utils/xtdhGrantFormatters.tscomponents/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/xtdh/stats/subcomponents/XtdhProgressBar.tsxcomponents/xtdh/stats/subcomponents/XtdhStatCard.tsxcomponents/user/xtdh/UserXtdhStats.tsx__tests__/components/user/xtdh/utils/xtdhGrantFormatters.test.tscomponents/user/xtdh/utils/xtdhGrantFormatters.tscomponents/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsxcomponents/user/xtdh/granted-list/subcomponents/UserPageXtdhGrantListItem/useGrantItemViewModel.ts
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.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.tsxcomponents/xtdh/stats/subcomponents/XtdhLabeledProgressBar.tsxcomponents/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 solidProps extension, readonly usage, Tailwind styling, and the
ariaLabel ?? labelfallback all look correct and accessible. The component is a clean, reusable wrapper overXtdhProgressBarwith no obvious correctness or lint issues.components/xtdh/stats/subcomponents/XtdhProgressBar.tsx (1)
1-30: Accessible, clamped progress bar implementation looks correct
percentageclamping, 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
readonlyas required by the coding guidelines.
12-19: LGTM: Component signature and core calculations are sound.The
Readonly<>wrapper on props and theMath.maxguard foroutboundprevent negative values correctly.
21-43: LGTM: Stats array is well-structured and consistent.Data-driven approach with uniform formatting and proper optional
onClickhandling.
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-describedbyto link to screen reader text, while marking the visual tooltip content asaria-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-numsclass 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 cohesiveThe hook cleanly derives
contractAddress/contractLabel, maps chains with an explicit unsupported-chain path, and gatesuseContractOverviewQueryviaenabledwhile still calling the hook unconditionally, which keeps React Hooks happy. The split betweencreateBaseGrantDetailsandbuildGrantDetailsgives you a nice layering where contract metadata is optional, andnormalizeErrorDetails+deriveErrorDetailsensure user-facing errors are non-empty and consistently prioritized. The returnedGrantItemViewModelshape 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: ConfirmParseErrorArrayruntime shape matches this type guardThis guard assumes that parse errors thrown by
parseTokenExpressionToRangesareErrorinstances withname === PARSE_ERROR_ARRAY_NAMEand anerrorsarray. IfParseErrorArrayis actually implemented as an augmented array (e.g. an array with Error-like props assigned onto it),error instanceof Errorwould be false and this guard would never match, causingformatTargetTokensto rethrow parse errors instead of logging and falling back tofallbackLabel. Please double‑check the currentParseErrorArrayimplementation inNftPicker.utilsand either keep thisinstanceof Errorcheck (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 ofnull/undefinedas “All tokens”, invalid/negative values as “unknown”, and truncation for fractional counts is straightforward and defensive, andformatTargetTokensCountcleanly reuses it rather than duplicating logic. This matches how the grant view-model consumesTargetTokensCountInfo.Also applies to: 138-141
68-99: Graceful fallback on parse errors informatTargetTokensThe parse‑then‑canonicalize flow with
parseTokenExpressionToRangesand aconsole.error+fallbackLabelpath 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 theisParseErrorArrayguard is confirmed correct, this should give predictable UX for malformed token selections.
130-136: Numeric amount formatting is defensive and consistent
formatAmountguarding against non‑finite values and truncating viaMath.truncbefore using the sharednumberFormatteris simple and robust, and matches how you format counts and totals elsewhere in the grant view‑model.
142-166: Decimal formatter caching avoids repeatedIntl.NumberFormatallocationsThe small
createDecimalFormatterhelper plusdecimalFormattersmap is a nice way to avoid re‑creatingIntl.NumberFormatinstances 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 informatTdhRatePerTokenlooks well thought‑outThe function defensively bails out on non‑finite inputs or non‑positive token counts, then uses
PRECISION_THRESHOLDSto 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.
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
components/user/xtdh/UserPageXtdh.tsx (1)
65-74: Authorization bug: FixcanGrantidentity check so missing consolidation keys don't grant accessAs noted in a previous review,
connectedProfile.consolidation_key === profile.consolidation_keyreturnstruewhen both areundefined/null, which incorrectly allowscanGrantfor 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
useXtdhGrantsQueryhook normalizesgrantorto lowercase in its query key, but the invalidation call at line 86 uses the rawgrantorvalue. Whengrantorcontains 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
divopened 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-scrollThe
setTimeouton 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
UserPageXtdhGrantedto signal when the grant section is mounted, or auseEffectin 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
📒 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 />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/xtdh/UserPageXtdh.tsxcomponents/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 addreadonlybefore props in React components
Files:
components/user/xtdh/UserPageXtdh.tsxcomponents/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 usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
Files:
components/user/xtdh/UserPageXtdh.tsxcomponents/user/xtdh/UserPageXtdhGrantedList.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint 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 inuseEffectEvent
Files:
components/user/xtdh/UserPageXtdh.tsxcomponents/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)

Summary by CodeRabbit
New Features
Improvements
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.