Skip to content

This year BW leaderboards#4041

Merged
elisescu merged 2 commits intomainfrom
updateBWLeaderboards
Jan 22, 2026
Merged

This year BW leaderboards#4041
elisescu merged 2 commits intomainfrom
updateBWLeaderboards

Conversation

@elisescu
Copy link
Contributor

@elisescu elisescu commented Jan 12, 2026

Add support for including this year BW leaderboards as well as last years. Remove code duplication and extract the spreadsheets interaction into a small services function.

Summary by CodeRabbit

  • New Features

    • Added Bridgewater 2025 leaderboard page with updated data retrieval
  • Removed Features

    • Removed Bridgewater 2024 leaderboard page
  • Refactor

    • Reorganized leaderboard infrastructure and shared data service for improved maintainability

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

Copy link
Contributor

@ncarazon ncarazon left a comment

Choose a reason for hiding this comment

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

lgtm

Add support for including this year BW leaderboards as well as last years.
Remove code duplication and extract the spreadsheets interaction into a
small services function.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

This PR removes the 2024 Bridgewater leaderboard implementation and introduces a 2025 version. It simultaneously refactors the data-fetching logic into a centralized service function (getAllSheetsData), eliminating duplicate Google Sheets API code across multiple files and consolidating the leaderboard component imports to a shared path.

Changes

Cohort / File(s) Summary
Campaign Year Migration (2024→2025)
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2024/leaderboards/page.tsx, front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/leaderboards/page.tsx
Deleted legacy 2024 leaderboard page (94 lines). Added new 2025 leaderboard page that mirrors the old structure but uses the new shared getAllSheetsData service function instead of inline Google Sheets API calls.
Leaderboard Component Refactoring
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/leaderboard-tabs.tsx, front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/page.tsx
Removed leaderboard-tabs.tsx component (182 lines) that rendered tabbed leaderboard UI with per-sheet row expansion and conditional user highlighting. Refactored page.tsx to use new getAllSheetsData service function, updated LeaderboardTabs import path to shared components, and added guard for missing data.
Google Sheets Service Consolidation
front_end/src/services/google_spreadsheets.ts
Added new getAllSheetsData() export function that centralizes Google Sheets metadata fetching and multi-sheet data retrieval logic with consistent error handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With a hop and a skip, we've moved from old to new,
Twenty-twenty-five awaits the leaderboard crew!
Shared services flow, no duplication we sow,
The spreadsheets dance as the data will grow. 📊✨

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "This year BW leaderboards" is vague and does not clearly convey the primary changes. It doesn't specify that this adds 2025 leaderboards, removes 2024 leaderboards, or refactors shared code. Use a more specific title like "Add 2025 BW leaderboards and extract spreadsheet service" to clearly indicate the main changes and intent.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@front_end/src/services/google_spreadsheets.ts`:
- Around line 9-51: The catch block in the Google Sheets fetch logic currently
swallows all errors (credentialsBase64 parsing, GoogleAuth,
sheets.spreadsheets.get, sheets.spreadsheets.values.get) by logging and
returning an empty array; instead propagate the failure so callers can show an
error state — replace the console.error + return [] with rethrowing the original
error (or throw a new Error that wraps it) so the calling code receives the
exception and can handle/display it appropriately.
🧹 Nitpick comments (1)
front_end/src/services/google_spreadsheets.ts (1)

26-46: Filter blank sheet titles and handle per-sheet failures independently.

Mapping missing titles to "" creates invalid ranges (like !A1:Z), and without per-sheet error handling, a single sheet request failure causes the entire load to fail and return empty results. Filter empty sheet names and wrap each sheet fetch in a try-catch so one bad sheet doesn't prevent retrieving data from others.

♻️ Proposed refactor
-    const sheetNames = spreadsheet.data.sheets?.map(
-      (sheet) => sheet.properties?.title || ""
-    );
+    const sheetNames = (spreadsheet.data.sheets ?? [])
+      .map((sheet) => sheet.properties?.title || "")
+      .filter((name) => name.trim().length > 0);
@@
-    const allSheetsData = await Promise.all(
-      sheetNames.map(async (sheetName) => {
-        const response = await sheets.spreadsheets.values.get({
-          spreadsheetId,
-          range: `${sheetName}!A1:Z`,
-        });
-        return {
-          name: sheetName,
-          data: response.data.values || [],
-        };
-      })
-    );
+    const allSheetsData = (
+      await Promise.all(
+        sheetNames.map(async (sheetName) => {
+          try {
+            const response = await sheets.spreadsheets.values.get({
+              spreadsheetId,
+              range: `${sheetName}!A1:Z`,
+            });
+            return {
+              name: sheetName,
+              data: response.data.values || [],
+            };
+          } catch (error) {
+            console.error(`Error fetching sheet "${sheetName}"`, error);
+            return null;
+          }
+        })
+      )
+    ).filter(Boolean) as { name: string; data: string[][] }[];
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc29917 and b72300f.

⛔ Files ignored due to path filters (2)
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/opengraph-image.jpg is excluded by !**/*.jpg
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/twitter-image.jpg is excluded by !**/*.jpg
📒 Files selected for processing (31)
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2024/leaderboards/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/header.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/hero-section.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/pixels-tags.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/registration-forms.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/registration-status.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/results-announcement.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/results-dates.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/results-hero.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/results-leaderboard.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/results-prize.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/components/typography.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/constants.ts
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/contest-rules/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/how-it-works/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/layout.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/leaderboards/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/notice-at-collection/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/announcement.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/dates.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/disclaimer.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/hero.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/openLeaderboard.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/prize.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/components/undergradLeaderboard.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/q1/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/leaderboard-tabs.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/page.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/components/leaderboard-tabs.tsx
  • front_end/src/services/google_spreadsheets.ts
💤 Files with no reviewable changes (2)
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/leaderboard-tabs.tsx
  • front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2024/leaderboards/page.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/leaderboards/page.tsx (3)
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/page.tsx (1)
  • Page (72-147)
front_end/src/services/google_spreadsheets.ts (1)
  • getAllSheetsData (5-53)
front_end/src/app/(campaigns-registration)/(bridgewater)/components/leaderboard-tabs.tsx (1)
  • LeaderboardTabs (26-182)
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/page.tsx (1)
front_end/src/services/google_spreadsheets.ts (1)
  • getAllSheetsData (5-53)
⏰ 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). (3)
  • GitHub Check: Build Docker Image
  • GitHub Check: integration-tests
  • GitHub Check: Backend Checks
🔇 Additional comments (2)
front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater/leaderboards/page.tsx (1)

10-41: LGTM: server-side fetch + shared tabs wiring is clean.
Nice consolidation with the shared sheets service and tabs component.

front_end/src/app/(campaigns-registration)/(bridgewater)/bridgewater-2025/leaderboards/page.tsx (1)

10-41: LGTM for the 2025 leaderboard page flow.
Data fetch + highlighted user wiring into the shared tabs component looks consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +9 to +51
try {
const credentials = JSON.parse(
Buffer.from(credentialsBase64, "base64").toString()
);

const auth = new google.auth.GoogleAuth({
credentials,
scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
});

const sheets = google.sheets({ version: "v4", auth });

// First, get all sheet names
const spreadsheet = await sheets.spreadsheets.get({
spreadsheetId,
});

const sheetNames = spreadsheet.data.sheets?.map(
(sheet) => sheet.properties?.title || ""
);

if (!sheetNames || sheetNames.length === 0) {
return [];
}

// Then get data for all sheets
const allSheetsData = await Promise.all(
sheetNames.map(async (sheetName) => {
const response = await sheets.spreadsheets.values.get({
spreadsheetId,
range: `${sheetName}!A1:Z`,
});
return {
name: sheetName,
data: response.data.values || [],
};
})
);

return allSheetsData;
} catch (error) {
console.error("Error fetching sheet data:", error);
return [];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Surface fetch/auth failures instead of silently returning empty data.
Right now any error (bad credentials, API outage) is swallowed and the caller only sees “No data found,” masking a real failure. Consider rethrowing (or otherwise propagating) so the page can show a proper error state.

🐛 Suggested change
   } catch (error) {
     console.error("Error fetching sheet data:", error);
-    return [];
+    throw error;
   }
📝 Committable suggestion

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

Suggested change
try {
const credentials = JSON.parse(
Buffer.from(credentialsBase64, "base64").toString()
);
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
});
const sheets = google.sheets({ version: "v4", auth });
// First, get all sheet names
const spreadsheet = await sheets.spreadsheets.get({
spreadsheetId,
});
const sheetNames = spreadsheet.data.sheets?.map(
(sheet) => sheet.properties?.title || ""
);
if (!sheetNames || sheetNames.length === 0) {
return [];
}
// Then get data for all sheets
const allSheetsData = await Promise.all(
sheetNames.map(async (sheetName) => {
const response = await sheets.spreadsheets.values.get({
spreadsheetId,
range: `${sheetName}!A1:Z`,
});
return {
name: sheetName,
data: response.data.values || [],
};
})
);
return allSheetsData;
} catch (error) {
console.error("Error fetching sheet data:", error);
return [];
try {
const credentials = JSON.parse(
Buffer.from(credentialsBase64, "base64").toString()
);
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
});
const sheets = google.sheets({ version: "v4", auth });
// First, get all sheet names
const spreadsheet = await sheets.spreadsheets.get({
spreadsheetId,
});
const sheetNames = spreadsheet.data.sheets?.map(
(sheet) => sheet.properties?.title || ""
);
if (!sheetNames || sheetNames.length === 0) {
return [];
}
// Then get data for all sheets
const allSheetsData = await Promise.all(
sheetNames.map(async (sheetName) => {
const response = await sheets.spreadsheets.values.get({
spreadsheetId,
range: `${sheetName}!A1:Z`,
});
return {
name: sheetName,
data: response.data.values || [],
};
})
);
return allSheetsData;
} catch (error) {
console.error("Error fetching sheet data:", error);
throw error;
}
🤖 Prompt for AI Agents
In `@front_end/src/services/google_spreadsheets.ts` around lines 9 - 51, The catch
block in the Google Sheets fetch logic currently swallows all errors
(credentialsBase64 parsing, GoogleAuth, sheets.spreadsheets.get,
sheets.spreadsheets.values.get) by logging and returning an empty array; instead
propagate the failure so callers can show an error state — replace the
console.error + return [] with rethrowing the original error (or throw a new
Error that wraps it) so the calling code receives the exception and can
handle/display it appropriately.

@elisescu elisescu merged commit 13745e2 into main Jan 22, 2026
13 of 14 checks passed
@elisescu elisescu deleted the updateBWLeaderboards branch January 22, 2026 09:51
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