Skip to content

remove wave.outcomes#1672

Merged
simo6529 merged 4 commits intomainfrom
remove-wave-outcomes
Dec 22, 2025
Merged

remove wave.outcomes#1672
simo6529 merged 4 commits intomainfrom
remove-wave-outcomes

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Dec 22, 2025

Summary by CodeRabbit

  • Refactor

    • Leaderboard outcome display now fetches outcome totals asynchronously: shows a pulsing loading placeholder while fetching, displays an outcome button and label when data exists, and renders nothing when there are no outcomes.
  • Tests

    • Tests updated to mock the outcome data source directly, covering loaded, empty, and loading states.

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

Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 22, 2025

Walkthrough

Replaced local outcome-calculation with a new hook useWaveRankReward, added query key WAVE_OUTCOME_DISTRIBUTION_PAGE, and updated leaderboard components/tests to stop passing wave (now use only drop). Components now handle loading and empty-outcome states from the hook.

Changes

Cohort / File(s) Summary
New Hook
hooks/waves/useWaveRankReward.ts
New hook that queries outcome metadata and per-outcome distribution pages (via useQueries), aggregates nicTotal, repTotal, manualOutcomes, returns isLoading, and exports WaveRankRewards.
Query Key
components/react-query-wrapper/ReactQueryWrapper.tsx
Added WAVE_OUTCOME_DISTRIBUTION_PAGE to QueryKey enum.
Outcome UI (core)
components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
Removed internal calculation logic and wave prop; now calls useWaveRankReward with drop.wave.id and drop.rank; added loading-state rendering and null-render when totals are zero.
Small-leaderboard: drop variants
components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
components/waves/small-leaderboard/WaveSmallLeaderboardDrop.tsx
components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
Removed wave prop from public signatures and call sites; child components now receive only drop and onDropClick.
Leaderboard footer / author
components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
Stopped passing wave into WaveSmallLeaderboardItemOutcomes; simplified prop usage to drop only; removed ApiWave import/usages where applicable.
Leaderboard drops (other)
components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
components/waves/leaderboard/drops/WaveLeaderboardDrop.tsx
Removed wave from props and call sites where forwarded to footers/default drops.
Tests
__tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
Refactored to mock useWaveRankReward, supply nicTotal, repTotal, manualOutcomes, isLoading; updated drop test data to include wave.id; added loading and empty-outcome cases.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant Component as WaveSmallLeaderboardItemOutcomes
    participant Hook as useWaveRankReward
    participant QMeta as react-query (useWaveOutcomesQuery)
    participant QDist as react-query (distribution useQueries)
    participant API as Wave Outcomes API

    Note over Component,Hook: Render when a drop is shown (has drop.wave.id and drop.rank)
    User->>Component: view drop
    Component->>Hook: call useWaveRankReward(waveId, rank, enabled)
    Hook->>QMeta: fetch outcomes metadata (useWaveOutcomesQuery)
    QMeta->>API: GET /waves/:waveId/outcomes
    API-->>QMeta: outcomes list
    QMeta-->>Hook: outcomes metadata
    Loop per outcome (relevant to rank)
      Hook->>QDist: fetch outcome distribution page (WAVE_OUTCOME_DISTRIBUTION_PAGE)
      QDist->>API: GET /outcomes/:id/distribution?page=N
      API-->>QDist: distribution page
      QDist-->>Hook: distribution data
    end
    Hook-->>Component: { nicTotal, repTotal, manualOutcomes, isLoading }
    alt isLoading
      Component->>Component: render pulsing skeleton
    else has outcomes
      Component->>Component: render button + tooltip with nic/rep/manual outcomes
    else no outcomes
      Component->>Component: render null
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect paging math and index-to-page calculation in useWaveRankReward for off-by-one and edge cases.
  • Verify react-query keys/options (staleTime, enabled) and consistency with WAVE_OUTCOME_DISTRIBUTION_PAGE.
  • Confirm all call sites updated (no remaining wave prop forwarding) and public prop/interface changes intended.
  • Review tests to ensure mocking accurately reflects new hook behavior and loading/empty branches.

Possibly related PRs

  • Outcomes refactor #1671 — Overlaps on wave outcome fetching and QueryKey changes (distribution-page queries and related hooks).
  • fix build errs #1670 — Related updates to outcome types and distribution endpoints used by the new hook.

Suggested reviewers

  • ragnep
  • prxt6529
  • analyticsflowee

Poem

🐰 I hopped through code and found a thread,
From drops I fetched the wave id instead.
Hook tallied nic, rep, and manual treats,
No extra prop — outcomes lined up neat.
Tiny paws dance — cached results so sweet! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'remove wave.outcomes' directly matches the primary change: eliminating direct dependency on wave.outcomes by replacing outcome calculation logic with a useWaveRankReward hook.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remove-wave-outcomes

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c65f08 and 335f2b6.

📒 Files selected for processing (4)
  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/WaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • hooks/waves/useWaveRankReward.ts
💤 Files with no reviewable changes (1)
  • components/waves/leaderboard/drops/WaveLeaderboardDrop.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • hooks/waves/useWaveRankReward.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

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

Files:

  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
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/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
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/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
🧬 Code graph analysis (2)
components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx (1)
components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx (1)
  • WaveLeaderboardDropFooter (9-13)
components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx (1)
components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx (1)
  • WaveSmallLeaderboardItemOutcomes (16-160)
⏰ 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/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx (2)

22-29: LGTM! Consistent removal of wave prop.

The removal of the wave prop from both the interface and component signature is consistent with the refactor. This component was only passing wave down to child components and never used it directly.


88-88: LGTM! Call site updated to match new signature.

The call to WaveLeaderboardDropFooter correctly passes only the drop prop, matching the updated component signature.

components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx (1)

5-12: LGTM! Simplified API by removing redundant wave prop.

The refactor correctly removes the wave prop since WaveSmallLeaderboardItemOutcomes accesses drop.wave.id through the useWaveRankReward hook. ExtendedDrop extends ApiDrop, which includes the properly typed wave: ApiWaveMin property with an id field, ensuring type safety is maintained.


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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (5)
components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx (2)

15-23: Remove unused wave prop from interface and update destructuring.

The wave prop is defined in WaveSmallLeaderboardDefaultDropProps but is no longer destructured or used. This creates an inconsistency where callers might pass wave thinking it's used.

Proposed fix
 interface WaveSmallLeaderboardDefaultDropProps {
   readonly drop: ExtendedDrop;
-  readonly wave: ApiWave;
   readonly onDropClick: (drop: ExtendedDrop) => void;
 }

3-3: Remove unused ApiWave import.

The ApiWave import is no longer used after removing the wave prop from the interface.

Proposed fix
 import { ExtendedDrop } from "@/helpers/waves/drop.helpers";
-import { ApiWave } from "@/generated/models/ApiWave";
 import Link from "next/link";
components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx (2)

6-13: Remove unused wave prop from interface.

The wave prop is defined in WaveLeaderboardDropFooterProps but is not destructured or used. This creates an inconsistency.

Proposed fix
 interface WaveLeaderboardDropFooterProps {
   readonly drop: ExtendedDrop;
-  readonly wave: ApiWave;
 }

 export const WaveLeaderboardDropFooter: React.FC<
   WaveLeaderboardDropFooterProps
-> = ({ drop }) => {
+> = ({ drop }) => {

3-3: Remove unused ApiWave import.

The ApiWave import is no longer used after removing the wave prop from the interface.

Proposed fix
 import { ExtendedDrop } from "@/helpers/waves/drop.helpers";
-import { ApiWave } from "@/generated/models/ApiWave";
 import { WaveSmallLeaderboardItemOutcomes } from "@/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes";
components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx (1)

14-22: Remove unused wave prop from interface.

The interface declares wave: ApiWave at line 16, but the implementation at line 22 only destructures drop and onDropClick. This creates a type mismatch where the prop is declared but never used.

🔎 Proposed fix
 interface WaveSmallLeaderboardTopThreeDropProps {
   readonly drop: ExtendedDrop;
-  readonly wave: ApiWave;
   readonly onDropClick: (drop: ExtendedDrop) => void;
 }
🧹 Nitpick comments (5)
hooks/waves/useWaveRankReward.ts (3)

29-31: Remove comments per coding guidelines.

The comment on line 29 should be removed. The code should be self-explanatory as per the project's coding guidelines for TypeScript files.

Proposed fix
   const { outcomes, isEnabled: isOutcomesEnabled } = useWaveOutcomesQuery({
     waveId,
-    // We only need the types of outcomes to know what to fetch
     enabled: enabled && !!rank,
   });

33-37: Remove comments per coding guidelines.

Comments on lines 33-35 should be removed. Consider extracting the calculation into a well-named helper function if the logic needs clarification.

Proposed fix
-  // Calculate which page the rank falls into.
-  // Rank 1 -> index 0. Page 1 (indices 0-99).
-  // Rank 101 -> index 100. Page 2 (indices 100-199).
   const targetIndex = rank ? rank - 1 : 0;
   const page = Math.floor(targetIndex / DISTRIBUTION_PAGE_SIZE) + 1;

76-93: Remove comment per coding guidelines.

Proposed fix
-  // Iterate over outcomes and their corresponding query results
   outcomes.forEach((outcome, i) => {
components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx (1)

84-89: Replace <img> with <Image /> from next/image.

Per coding guidelines, <img> elements should be replaced with <Image /> from next/image to satisfy the @next/next/no-img-element ESLint rule.

Proposed fix
+import Image from "next/image";
                   {drop.author.pfp ? (
-                    <img
+                    <Image
                       className="tw-size-6 tw-flex-shrink-0 tw-rounded-lg tw-bg-iron-800 tw-ring-1 tw-ring-inset tw-ring-white/10"
                       src={drop.author.pfp}
                       alt=""
+                      width={24}
+                      height={24}
                     />
components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx (1)

67-88: Consider extracting repeated rank check.

The condition drop.rank && drop.rank <= 3 is repeated four times in the boxShadow computation. Extracting this to a variable would improve readability and maintainability.

🔎 Suggested refactor
+          const isTopThree = drop.rank && drop.rank <= 3;
+          const rankColor = isTopThree 
+            ? getRankTextColor(drop.rank)?.replace("tw-text-", "").trim()
+            : "#60606C";
           style={{
             border: "1px solid transparent",
-            boxShadow: `inset 2px 0 0 ${drop.rank && drop.rank <= 3
-              ? getRankTextColor(drop.rank)?.replace("tw-text-", "").trim()
-              : "#60606C"
-              }, 
-                       inset 0 1px 0 ${drop.rank && drop.rank <= 3
-                ? getRankTextColor(drop.rank)
-                  ?.replace("tw-text-", "")
-                  .trim()
-                : "#60606C"
-              }20, 
-                       inset -1px 0 0 ${drop.rank && drop.rank <= 3
-                ? getRankTextColor(drop.rank)
-                  ?.replace("tw-text-", "")
-                  .trim()
-                : "#60606C"
-              }20, 
-                       inset 0 -1px 0 ${drop.rank && drop.rank <= 3
-                ? getRankTextColor(drop.rank)
-                  ?.replace("tw-text-", "")
-                  .trim()
-                : "#60606C"
-              }20`,
+            boxShadow: `inset 2px 0 0 ${rankColor}, 
+                       inset 0 1px 0 ${rankColor}20, 
+                       inset -1px 0 0 ${rankColor}20, 
+                       inset 0 -1px 0 ${rankColor}20`,
             transition: "box-shadow 0.2s ease, background-color 0.2s ease",
           }}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad6bf46 and 13071d9.

📒 Files selected for processing (8)
  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • hooks/waves/useWaveRankReward.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

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

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
  • components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • hooks/waves/useWaveRankReward.ts
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
  • components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
  • components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • hooks/waves/useWaveRankReward.ts
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx
  • components/waves/drop/SingleWaveDropInfoAuthorSection.tsx
  • components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx
  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • hooks/waves/useWaveRankReward.ts
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
🧠 Learnings (8)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label

Applied to files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
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/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
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:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests

Applied to files:

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

Applied to files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Remove unnecessary Effects; if Effect only derives state, compute during render instead

Applied to files:

  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
🧬 Code graph analysis (3)
components/waves/drop/SingleWaveDropInfoAuthorSection.tsx (1)
components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx (1)
  • WaveSmallLeaderboardItemOutcomes (16-160)
__tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx (1)
components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx (1)
  • WaveSmallLeaderboardItemOutcomes (16-160)
hooks/waves/useWaveRankReward.ts (2)
hooks/waves/useWaveOutcomesQuery.ts (1)
  • useWaveOutcomesQuery (41-105)
generated/models/ApiWaveOutcomeDistributionItemsPage.ts (1)
  • ApiWaveOutcomeDistributionItemsPage (16-58)
⏰ 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)
hooks/waves/useWaveRankReward.ts (2)

39-59: LGTM!

The useQueries implementation correctly maps over outcomes to create parallel distribution queries with appropriate caching and conditional enablement.


61-70: LGTM!

The early return correctly handles the disabled state, returning zeros and isLoading: false since no queries are executed when the hook is disabled.

components/waves/drop/SingleWaveDropInfoAuthorSection.tsx (1)

25-27: LGTM!

The change correctly removes the wave prop from WaveSmallLeaderboardItemOutcomes since the component now extracts waveId from drop.wave.id via the new useWaveRankReward hook.

components/react-query-wrapper/ReactQueryWrapper.tsx (1)

98-98: LGTM!

The new query key WAVE_OUTCOME_DISTRIBUTION_PAGE follows the existing naming convention and is correctly positioned after WAVE_OUTCOME_DISTRIBUTION.

components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx (1)

132-132: LGTM!

The change correctly passes only drop to WaveSmallLeaderboardItemOutcomes, aligning with the refactored component signature.

components/waves/leaderboard/drops/footer/WaveLeaderboardDropFooter.tsx (1)

14-14: LGTM!

The component correctly passes only drop to WaveSmallLeaderboardItemOutcomes, consistent with the refactored component signature.

components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx (1)

164-164: LGTM!

The component correctly passes only drop to WaveSmallLeaderboardItemOutcomes, matching the updated component signature.

__tests__/components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.test.tsx (1)

5-52: LGTM!

The test file correctly mocks the new useWaveRankReward hook and comprehensively tests all three states:

  1. Rendering when outcomes exist
  2. Hiding when no outcomes and not loading
  3. Showing loading state

The mock setup with beforeEach reset and the drop fixture with wave.id are appropriate for the refactored component.

components/waves/small-leaderboard/WaveSmallLeaderboardItemOutcomes.tsx (2)

45-49: LGTM!

The loading state with a pulsing placeholder provides appropriate visual feedback while data is being fetched.


114-116: LGTM!

The condensed className construction is more readable and maintainable than the previous implementation.

Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
hooks/waves/useWaveRankReward.ts (3)

32-33: Optional: Simplify targetIndex calculation.

The ternary fallback to 0 is redundant since !rank is handled by the guard at line 59. For clarity, you could use non-null assertion or simplify the logic.

🔎 Suggested simplification
-  const targetIndex = rank ? rank - 1 : 0;
+  const targetIndex = (rank ?? 1) - 1;
   const page = Math.floor(targetIndex / DISTRIBUTION_PAGE_SIZE) + 1;

Alternatively, after adding validation as suggested earlier, you can safely assume rank is valid at this point.


59-66: Optional: Consider moving the guard earlier.

The guard correctly returns early when rank is invalid, but it executes after the useQueries hook setup. While the queries won't run (due to enabled: false), moving the validation before query setup would be more efficient and clearer.

🔎 Suggested restructuring

Move the guard to before the queries:

   const { outcomes, isEnabled: isOutcomesEnabled } = useWaveOutcomesQuery({
     waveId,
     enabled: enabled && !!rank,
   });

+  if (!rank || !enabled) {
+    return {
+      nicTotal: 0,
+      repTotal: 0,
+      manualOutcomes: [],
+      isLoading: false
+    }
+  }
+
   const targetIndex = rank ? rank - 1 : 0;
   const page = Math.floor(targetIndex / DISTRIBUTION_PAGE_SIZE) + 1;

   const distributionQueries = useQueries({
     // ... queries
   });

   const isLoading = distributionQueries.some((q) => q.isLoading);

-  if (!rank || !enabled) {
-    return {
-      nicTotal: 0,
-      repTotal: 0,
-      manualOutcomes: [],
-      isLoading: false
-    }
-  }

Note: React hooks cannot be called conditionally, so useWaveOutcomesQuery must remain before the guard. However, the useQueries call happens after all other hooks, so this pattern would still violate the rules of hooks. The current implementation is actually correct given hook constraints. Disregard this suggestion.


90-95: Consider exposing error state to consumers.

The hook doesn't return any error information from the distribution queries. If a query fails, consumers won't know and may display incomplete or incorrect data.

🔎 Suggested error handling

Update the interface:

 export interface WaveRankRewards {
   readonly nicTotal: number;
   readonly repTotal: number;
   readonly manualOutcomes: string[];
   readonly isLoading: boolean;
+  readonly error?: string;
 }

Add error checking:

   const isLoading = distributionQueries.some((q) => q.isLoading);
+  const error = distributionQueries.find((q) => q.error)?.error;
+  const errorMessage = error instanceof Error ? error.message : undefined;

   if (!rank || !enabled) {
     return {
       nicTotal: 0,
       repTotal: 0,
       manualOutcomes: [],
-      isLoading: false
+      isLoading: false,
+      error: undefined
     }
   }

And in the return:

   return {
     nicTotal,
     repTotal,
     manualOutcomes,
     isLoading,
+    error: errorMessage,
   };
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13071d9 and 3c65f08.

📒 Files selected for processing (6)
  • components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
  • hooks/waves/useWaveRankReward.ts
💤 Files with no reviewable changes (1)
  • components/waves/small-leaderboard/WaveSmallLeaderboardDrop.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/waves/small-leaderboard/WaveSmallLeaderboardDefaultDrop.tsx
  • components/waves/small-leaderboard/WaveSmallLeaderboardTopThreeDrop.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

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

Files:

  • components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
  • hooks/waves/useWaveRankReward.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
  • hooks/waves/useWaveRankReward.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx
  • components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx
  • hooks/waves/useWaveRankReward.ts
🧬 Code graph analysis (1)
hooks/waves/useWaveRankReward.ts (2)
hooks/waves/useWaveOutcomesQuery.ts (1)
  • useWaveOutcomesQuery (41-105)
generated/models/ApiWaveOutcomeDistributionItemsPage.ts (1)
  • ApiWaveOutcomeDistributionItemsPage (16-58)
⏰ 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)
hooks/waves/useWaveRankReward.ts (4)

1-10: LGTM!

Imports are clean and the distribution page size of 100 is reasonable for pagination.


11-16: LGTM!

Interface follows readonly conventions and is well-structured.


68-88: LGTM!

The aggregation logic correctly processes each outcome type and safely checks for data existence before accumulating values.


35-55: QueryKey is properly exported and defined.

QueryKey.WAVE_OUTCOME_DISTRIBUTION_PAGE is correctly defined and exported in the QueryKey enum at line 98 of ReactQueryWrapper.tsx. The implementation is correct.

components/waves/small-leaderboard/MemesWaveSmallLeaderboardDrop.tsx (1)

11-11: LGTM!

The removal of the wave prop is consistent throughout the component. Child components will now rely on drop.wave or the useWaveRankReward hook for wave-related data, which centralizes the data fetching logic.

components/waves/small-leaderboard/DefaultWaveSmallLeaderboardDrop.tsx (1)

13-13: LGTM!

Consistent with the refactoring across leaderboard components. The wave prop removal is complete and child components now receive only the drop prop.

Comment thread hooks/waves/useWaveRankReward.ts
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

@simo6529 simo6529 merged commit 766eecc into main Dec 22, 2025
8 checks passed
@simo6529 simo6529 deleted the remove-wave-outcomes branch December 22, 2025 12:30
@coderabbitai coderabbitai Bot mentioned this pull request Dec 22, 2025
This was referenced Jan 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants