Skip to content

[0] Custom Hackathon Dashboards#475

Open
alexanderpaolini wants to merge 5 commits into
mainfrom
blade/hackathon-pages
Open

[0] Custom Hackathon Dashboards#475
alexanderpaolini wants to merge 5 commits into
mainfrom
blade/hackathon-pages

Conversation

@alexanderpaolini

@alexanderpaolini alexanderpaolini commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Custom Hackathon Dashboards

The hackathon dashboard now gets presented at /hackathon/slug. /hackathon and /hackathon/current both redirect to the current hackathon (determined by the api fetch), if one exists.

Test Plan

cc @DGoel1602 (my forge doesn't work)

his images:

image image image

Summary by CodeRabbit

  • New Features

    • Added a dedicated hackathon landing page with session-based routing to the active event.
    • Introduced a cleaner dashboard experience with registration prompts, countdowns, upcoming events, and quick access actions.
    • Added a notice in the member area when a hackathon is currently running, with a direct link to the dashboard.
  • Bug Fixes

    • Improved active hackathon detection so the app now uses the correct start and end dates.
    • Updated countdown and event listings to reflect the selected hackathon more accurately.

@alexanderpaolini alexanderpaolini added Minor Small change - 1 reviewer required Blade Change modifies code in Blade app Database Change modifies code in the DB package labels Jun 23, 2026
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR splits hackathon dashboard rendering into named base components, threads explicit hackathon context through dashboard and route pages, updates current-hackathon selection to a date window, adds a current-hackathon notice on member dashboards, changes the top-level user interface to member-only rendering, and removes the old leaderboard surface.

Changes

Hackathon dashboard and routing

Layer / File(s) Summary
Current-hackathon routing and validation
packages/api/src/routers/hackathon.ts, packages/api/src/routers/hackers/mutations.ts, apps/blade/src/app/hackathon/page.tsx
getCurrentHackathon now uses a start/end date window, eventCheckIn validation uses schema parsing, and the /hackathon route adds metadata plus auth-gated redirect and empty-state handling.
Shared dashboard leaf widgets
apps/blade/src/app/_components/dashboard/hackathon-dashboard/countdown.tsx, apps/blade/src/app/_components/dashboard/hackathon-dashboard/issue-dialog.tsx, apps/blade/src/app/_components/dashboard/hackathon-dashboard/upcoming-events.tsx, apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
Countdown, issue-dialog, upcoming-events, and team-points exports are renamed to base components; countdown now uses endDate, issue-dialog becomes a client component, and upcoming-events filters by hackathonId.
Hackathon data card
apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx
BaseHackathonData now queries by hackathon.name, uses shared QR/wallet/guide buttons, changes the status label, and removes the mascot image block.
Dashboard shell and delegate
apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx, apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
The consolidated dashboard module re-exports shared widgets, adds a registration prompt and default guide link, and HackathonDashboard now resolves an active hackathon before delegating to BaseHackathonDashboard.
Hacker dashboard data flow
apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx, apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx
HackerDashboard now receives explicit hackathon context, and HackerData derives its queries from that context or a fallback hackathon lookup while keeping initial server data.
Member notice and shell
apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx, apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx, apps/blade/src/app/_components/user-interface.tsx
CurrentHackathonNotice is added, MemberDashboard shows it when a current hackathon exists, and UserInterface now only renders member surfaces.
BloomKnights route
apps/blade/src/app/hackathon/bloomknights/page.tsx
The BloomKnights page adds metadata, requires auth, loads the fixed hackathon and hacker, and renders inside HydrateClient with SessionNavbar before choosing BaseHackathonDashboard or HackerDashboard by check-in status.

Sequence Diagram(s)

/hackathon

sequenceDiagram
  participant Browser
  participant CurrentHackathonPage
  participant auth
  participant CurrentHackathonQuery
  participant redirect

  Browser->>CurrentHackathonPage: GET /hackathon
  CurrentHackathonPage->>auth: auth()
  CurrentHackathonPage->>CurrentHackathonQuery: api.hackathon.getCurrentHackathon()
  CurrentHackathonQuery-->>CurrentHackathonPage: currentHackathon or null
  alt currentHackathon exists
    CurrentHackathonPage->>redirect: /hackathon/{currentHackathon.name}
  else no current hackathon
    CurrentHackathonPage-->>Browser: render no-hackathon message
  end
Loading

/hackathon/bloomknights

sequenceDiagram
  participant Browser
  participant BloomKnightsHackathonPage
  participant auth
  participant HackathonQuery
  participant HackerQuery
  participant BaseHackathonDashboard
  participant HackerDashboard

  Browser->>BloomKnightsHackathonPage: GET /hackathon/bloomknights
  BloomKnightsHackathonPage->>auth: auth()
  BloomKnightsHackathonPage->>HackathonQuery: api.hackathon.getHackathon("bloomknights")
  HackathonQuery-->>BloomKnightsHackathonPage: hackathon or null
  alt hackathon missing
    BloomKnightsHackathonPage-->>Browser: notFound()
  else hackathon exists
    BloomKnightsHackathonPage->>HackerQuery: api.hackerQuery.getHacker(hackathon.name)
    HackerQuery-->>BloomKnightsHackathonPage: hacker
    alt hacker.status == "checkedin"
      BloomKnightsHackathonPage->>BaseHackathonDashboard: render
    else
      BloomKnightsHackathonPage->>HackerDashboard: render
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • KnightHacks/forge#374: Touches the same hackathon dashboard component namespace and tRPC hook wiring that this refactor also updates.
  • KnightHacks/forge#461: Modifies the same hackathon-data.tsx quick-action area and QR/Wallet behavior that this change reorganizes.

Suggested labels

Major, Feature

Suggested reviewers

  • DVidal1205
🚥 Pre-merge checks | ✅ 5 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is relevant, but it does not follow the required issue-number format because it uses [0] instead of [#123]. Change the title to start with a real issue key like "[#123] Custom Hackathon Dashboards" and keep it under 72 characters.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Validated Env Access ⚠️ Warning process.env.NODE_ENV is used in packages/consts/src/util.ts and apps/guild/src/trpc/react.tsx, outside env.ts/config files. Move those reads into src/env.ts (or a config file) and import a validated flag/boolean instead of reading process.env directly.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No Hardcoded Secrets ✅ Passed Scanned the modified hackathon/dashboard source; found no API-key/password/token literals. Only benign URLs/messages were present.
No Typescript Escape Hatches ✅ Passed No any, @ts-ignore, @ts-expect-error, or non-null assertions were found in the changed files; the code uses optional chaining and typed params instead.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch blade/hackathon-pages

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.

@DGoel1602 DGoel1602 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The first thing is minimal but I think the second one does need some eyes

Comment thread apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx Outdated
Comment thread apps/blade/src/app/hackathon/bloomknights/page.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx (1)

21-54: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the hardcoded leaderboard cutoff from the base component.

targetDate is fixed to October 25, 2025. On June 24, 2026, targetDate <= Date.now() is always true, so non-admin users will stay in the hidden-leaderboard branch indefinitely. This breaks the new reusable/base behavior across hackathons.

💡 Suggested fix
-export function BaseHackathonPointLeaderboard({
-  hacker,
-  hId,
-}: {
+export function BaseHackathonPointLeaderboard({
+  hacker,
+  hId,
+  hideLeaderboardAfter,
+}: {
   hacker: Awaited<ReturnType<(typeof serverCall.hackerQuery)["getHacker"]>>;
   hId: string;
+  hideLeaderboardAfter?: Date;
 }) {
@@
-  const targetDate = new Date("2025-10-25T23:00:00").getTime();
+  const targetDate = hideLeaderboardAfter?.getTime() ?? Number.POSITIVE_INFINITY;
-<BaseHackathonPointLeaderboard
-  hacker={hacker}
-  hId={hackathon.name}
-/>
+<BaseHackathonPointLeaderboard
+  hacker={hacker}
+  hId={hackathon.name}
+  hideLeaderboardAfter={hackathon.endDate}
+/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx`
around lines 21 - 54, The targetDate variable in the
BaseHackathonPointLeaderboard function is hardcoded to October 25, 2025, which
means once the current date passes this fixed date, the leaderboard visibility
condition will always be true for non-admin users, breaking the reusable
behavior across different hackathons. Remove the hardcoded targetDate variable
and instead pass the leaderboard cutoff date as a configurable prop to the
BaseHackathonPointLeaderboard function, or derive it from the hackathon data
that is already being passed in as the hId parameter, allowing each hackathon
instance to specify its own cutoff date.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx`:
- Around line 35-36: Remove the type assertion using `as Record<string,
BaseHackathonClassInfo>` from the DEFAULT_CLASS_INFO variable declaration and
replace it with an explicit type annotation instead. Change the declaration to
use a colon followed by the type annotation directly on the variable (`:
Record<string, BaseHackathonClassInfo>`) rather than casting with `as`. This
ensures proper type checking without bypassing it through assertions.

In `@apps/blade/src/app/hackathon/bloomknights/page.tsx`:
- Line 41: Remove the `as string` type assertion from the hacker.status
comparison on line 41. The status field is already properly typed as a string
literal union from the database schema, so the cast is unnecessary and bypasses
TypeScript's type safety. Replace the conditional check with optional chaining
syntax (hacker?.status) instead of the current `hacker && (hacker.status as
string)` pattern to achieve a cleaner, type-safe comparison while maintaining
the same functionality.

---

Outside diff comments:
In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx`:
- Around line 21-54: The targetDate variable in the
BaseHackathonPointLeaderboard function is hardcoded to October 25, 2025, which
means once the current date passes this fixed date, the leaderboard visibility
condition will always be true for non-admin users, breaking the reusable
behavior across different hackathons. Remove the hardcoded targetDate variable
and instead pass the leaderboard cutoff date as a configurable prop to the
BaseHackathonPointLeaderboard function, or derive it from the hackathon data
that is already being passed in as the hId parameter, allowing each hackathon
instance to specify its own cutoff date.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 8f344a94-5ac6-48f4-820f-80ce15e98baf

📥 Commits

Reviewing files that changed from the base of the PR and between 58ad852 and a2c28bb.

📒 Files selected for processing (19)
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/countdown.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/issue-dialog.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/upcoming-events.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx
  • apps/blade/src/app/_components/user-interface.tsx
  • apps/blade/src/app/hackathon/bloomknights/components/bk-hackathon-dashboard.tsx
  • apps/blade/src/app/hackathon/bloomknights/page.tsx
  • apps/blade/src/app/hackathon/current/page.tsx
  • apps/blade/src/app/hackathon/page.tsx
  • packages/api/src/routers/hackathon.ts
  • packages/api/src/routers/hackers/mutations.ts

Comment thread apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx Outdated
Comment thread apps/blade/src/app/hackathon/bloomknights/page.tsx Outdated
@alexanderpaolini alexanderpaolini marked this pull request as ready for review June 24, 2026 02:33
@alexanderpaolini alexanderpaolini changed the title [WIP] Custom Hackathon Dashboards [0] Custom Hackathon Dashboards Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
packages/api/src/routers/event.ts (1)

366-428: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Build break: discordEventId and googleEventId are no longer declared.

The catch block still references discordEventId (Line 406) and googleEventId (Line 418), but both were declared only inside the now-commented provisioning blocks (former Lines 301 and 330). The compiler will fail with Cannot find name 'discordEventId' / 'googleEventId', and even if it compiled, this cleanup is now dead logic since no Discord/Google events are created here.

Drop the external-cleanup branches and keep only the DB-failure path.

🐛 Proposed fix to remove the now-invalid cleanup branches
       } catch (error) {
         logger.error(JSON.stringify(error, null, 2));
 
-        try {
-          await discord.api.delete(
-            Routes.guildScheduledEvent(
-              DISCORD.KNIGHTHACKS_GUILD,
-              discordEventId,
-            ),
-          );
-        } catch (cleanupErr) {
-          logger.error(JSON.stringify(cleanupErr, null, 2));
-        }
-
-        try {
-          await google.calendar.events.delete({
-            calendarId: input.isOperationsCalendar
-              ? EVENTS.DEV_GOOGLE_CALENDAR_ID
-              : EVENTS.GOOGLE_CALENDAR_ID,
-            eventId: googleEventId,
-          });
-        } catch (cleanupErr) {
-          logger.error(JSON.stringify(cleanupErr, null, 2));
-        }
-
         throw new TRPCError({
           message: "Failed to create event in the database",
           code: "BAD_REQUEST",
         });
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/routers/event.ts` around lines 366 - 428, The catch block in
event creation still references discordEventId and googleEventId even though
those identifiers are no longer declared in this flow. Remove the Discord and
Google cleanup branches from the error handling in the event router’s create
path, and keep only the database rollback/error logging around the
db.insert(Event) and the final TRPCError throw. Use the existing catch block in
the create-event handler to locate and simplify this dead cleanup logic.
apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx (1)

201-219: 🎯 Functional Correctness | 🟠 Major

The "View Leaderboard" button currently opens an empty dialog.

Lines 201-219 wrap the button in a Dialog component, but the necessary DialogContent (or any child content) is missing. This renders the button non-functional; clicking it opens an empty modal overlay.

If the leaderboard UI is not yet ready, remove the Dialog wrapper and use a standard button or link instead to avoid dead interactions. If the dialog content exists elsewhere, ensure it is imported and placed inside the <Dialog> tags.

// Current (broken)
<Dialog>
  <DialogTrigger asChild>
    <button>View Leaderboard</button>
  </DialogTrigger>
  {/* Missing DialogContent here */}
</Dialog>

If the feature isn't ready, simplify to:

// Fixed (temporary)
<button className="..." onClick={() => window.location.href = '/leaderboard'}>
  View Leaderboard
</button>

Otherwise, add the modal structure:

<Dialog>
  <DialogTrigger asChild>
    <button>View Leaderboard</button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>Leaderboard</DialogHeader>
    {/* Leaderboard content here */}
  </DialogContent>
</Dialog>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx`
around lines 201 - 219, The View Leaderboard control is wrapped in a Dialog
without any DialogContent, so it opens an empty modal. Update the
hackathon-dashboard component around the Dialog/DialogTrigger block by either
removing the Dialog wrapper and using a normal button/link if the leaderboard UI
is not ready, or by adding the missing DialogContent (with the leaderboard UI)
inside the Dialog so the trigger has real content to display.
🧹 Nitpick comments (1)
apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx (1)

24-42: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Return before loading secondary data on the registration-only path.

Lines 24-27 fetch resume and past-hackathon data before the !hacker guard in Line 29. For unregistered users, those requests are unused and delay the prompt unnecessarily.

Possible fix
-  const [resume, pastHackathons] = await Promise.allSettled([
-    api.resume.getResume(),
-    api.hackathon.getPastHackathons(),
-  ]);
-
   if (!hacker) {
     return (
       <div className="flex flex-col items-center justify-center gap-y-6 text-xl font-semibold">
         <p className="w-full max-w-xl text-center text-2xl">
           Register for {hackathon.displayName} today!
@@
       </div>
     );
   }
+
+  const [resume, pastHackathons] = await Promise.allSettled([
+    api.resume.getResume(),
+    api.hackathon.getPastHackathons(),
+  ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx`
around lines 24 - 42, The `HackerDashboard` component is fetching secondary data
with `Promise.allSettled` before the `!hacker` registration-only branch, which
delays the registration prompt for users who don’t need that data. Move the
`api.resume.getResume()` and `api.hackathon.getPastHackathons()` loading so it
only runs after the `!hacker` early return, and keep the guard in
`HackerDashboard` as the first decision point before any unused requests are
started.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx`:
- Around line 155-167: The inline styles in hackathon-data.tsx are using invalid
raw color values like primary and secondary, which will not render as CSS
colors. Update the affected JSX in the dashboard component to use Tailwind
utility classes where possible, or replace the style values with the correct
hsl(var(--...)) theme syntax; check the elements around the Trophy icon and
surrounding containers in the hackathon dashboard markup.

In `@apps/blade/src/app/hackathon/bloomknights/page.tsx`:
- Around line 2-3: The page component in bloomknights/page.tsx is rendering
NotFoundPage directly, which leaves the response as 200 instead of a real 404.
Update the page logic to call Next.js notFound() from next/navigation at the
point where the 404 is needed, and remove the manual NotFoundPage return so
routing emits the proper Not Found status. Use the page component and
NotFoundPage references to locate the affected branch.

In `@packages/api/src/routers/event.ts`:
- Around line 277-364: Remove the large commented-out Discord/Google
provisioning experiment from the event router and keep only the live
`updateEvent`/`deleteEvent` logic. The commented blocks around the Discord
scheduled event creation and Google Calendar insert/callback cleanup should be
deleted entirely rather than left as dead code. If this work is intentionally
paused, capture the context in a TODO, issue, or ADR instead of preserving the
commented implementation in `event.ts`.
- Around line 379-380: The temporary discordId and googleId values in the event
flow are being treated like real external IDs, which will cause update/delete
calls in updateEvent and deleteEvent to hit Discord/Google APIs with invalid
identifiers. Replace the arbitrary placeholder strings in the event record with
an explicit sentinel such as "PLACEHOLDER" or null, and update the external call
sites in updateEvent and deleteEvent to guard on valid IDs before calling
discord.api or the Google Calendar client. Use the existing eventRecord
discordId/googleId checks so external requests are skipped whenever the ID is
missing or marked as a placeholder.

---

Outside diff comments:
In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx`:
- Around line 201-219: The View Leaderboard control is wrapped in a Dialog
without any DialogContent, so it opens an empty modal. Update the
hackathon-dashboard component around the Dialog/DialogTrigger block by either
removing the Dialog wrapper and using a normal button/link if the leaderboard UI
is not ready, or by adding the missing DialogContent (with the leaderboard UI)
inside the Dialog so the trigger has real content to display.

In `@packages/api/src/routers/event.ts`:
- Around line 366-428: The catch block in event creation still references
discordEventId and googleEventId even though those identifiers are no longer
declared in this flow. Remove the Discord and Google cleanup branches from the
error handling in the event router’s create path, and keep only the database
rollback/error logging around the db.insert(Event) and the final TRPCError
throw. Use the existing catch block in the create-event handler to locate and
simplify this dead cleanup logic.

---

Nitpick comments:
In
`@apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx`:
- Around line 24-42: The `HackerDashboard` component is fetching secondary data
with `Promise.allSettled` before the `!hacker` registration-only branch, which
delays the registration prompt for users who don’t need that data. Move the
`api.resume.getResume()` and `api.hackathon.getPastHackathons()` loading so it
only runs after the `!hacker` early return, and keep the guard in
`HackerDashboard` as the first decision point before any unused requests are
started.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6eba0fde-c616-4abd-b735-d373c45aaa05

📥 Commits

Reviewing files that changed from the base of the PR and between a2c28bb and 8c152ba.

📒 Files selected for processing (12)
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx
  • apps/blade/src/app/hackathon/bloomknights/page.tsx
  • apps/blade/src/app/hackathon/page.tsx
  • packages/api/src/routers/event.ts
💤 Files with no reviewable changes (4)
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx

Comment thread apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx Outdated
Comment on lines +2 to +3
import { redirect } from "next/navigation";

@coderabbitai coderabbitai Bot Jun 25, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/blade/src/app/hackathon/bloomknights/page.tsx | sed -n '1,35p'

Repository: KnightHacks/forge

Length of output: 1425


Use notFound() instead of rendering the 404 page component directly

Line 29 currently returns <NotFoundPage />, which renders a 404 UI but keeps the HTTP response status as 200 OK. This is invalid for an App Router page.tsx and interferes with correct 404 status handling.

Replace the manual return with notFound() to ensure a proper 404 Not Found status and to integrate with Next.js routing conventions.

Fix
-import { redirect } from "next/navigation";
+import { notFound, redirect } from "next/navigation";

-    return <NotFoundPage />;
+    notFound();

See App Router docs for details on 404 semantic handling.

📝 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
import { redirect } from "next/navigation";
import { notFound, redirect } from "next/navigation";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blade/src/app/hackathon/bloomknights/page.tsx` around lines 2 - 3, The
page component in bloomknights/page.tsx is rendering NotFoundPage directly,
which leaves the response as 200 instead of a real 404. Update the page logic to
call Next.js notFound() from next/navigation at the point where the 404 is
needed, and remove the manual NotFoundPage return so routing emits the proper
Not Found status. Use the page component and NotFoundPage references to locate
the affected branch.

Source: Path instructions

✅ Confirmed as addressed by @alexanderpaolini

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@alexanderpaolini The PR description image 1 shows a dark BloomKnights dashboard with “Hello, Alchemist Dhruv!”, a checked-in status card, quick action buttons for QR / Apple Wallet / Hacker’s Guide / Report an Issue, a Hack Points card showing 35, a humanity vs monstrosity progress bar, and the top of a countdown section labeled “HACKING ENDS IN.”

The PR description image 2 shows the Blade member dashboard dimmed behind a centered modal titled “BloomKnights is live,” with copy about check-in tools, points, live events, and event-specific info, plus “Stay Here” and “Open Hackathon Dashboard” buttons.

The PR description image 3 shows the Hackathon Dashboard with “Hello, Dhruv!”, a “Confirmed” status, a QR action bar, a notice that “The confirmation deadline has passed.”, a red “WITHDRAW” button, and lower actions for “View Past Hackathons” and “Download Resume.”

Thanks for resolving this. Using notFound() here is the right App Router fix because it returns a real 404 response instead of a 200 page that only looks like a 404.

For reference, the intended pattern is:

import { notFound, redirect } from "next/navigation";

if (!hackathon) {
  notFound();
}

🧠 Learnings used
Learnt from: DVidal1205
Repo: KnightHacks/forge PR: 354
File: apps/blade/src/app/admin/club/data/_components/MemberDemographics.tsx:19-35
Timestamp: 2026-02-10T19:07:03.232Z
Learning: In the KnightHacks/forge repository, apply the pattern in TSX React components: gating rendering should occur only when all required data fetches succeed. Do not render partial success when some queries fail; instead, implement a unified loading/state or error handling that surfaces a single, coherent state once all data is ready or failed. This repo-wide guideline helps ensure consistent UX when multiple data sources are involved.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Comment thread packages/api/src/routers/event.ts Outdated
Comment thread packages/api/src/routers/event.ts Outdated
Comment on lines 204 to 219
className="w-full rounded-lg py-2 text-sm font-semibold transition-all hover:shadow-md sm:py-2.5"
style={{
backgroundColor: `${teamColor}20`,
color: teamColor,
backgroundColor: `primary`,
color: "secondary",
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = `${teamColor}33`;
e.currentTarget.style.backgroundColor = `primary`;
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = `${teamColor}20`;
e.currentTarget.style.backgroundColor = `primary`;
}}
>
View Leaderboard
</button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Leaderboard</DialogTitle>
</DialogHeader>
<PointLeaderboard
hacker={hacker}
hId={hackathonData?.name ?? ""}
/>
</DialogContent>
</Dialog>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

let's nuke the leaderboard trigger along with the content, base dash won't need a leaderboard

textShadow: `0 0 10px primary`,
}}
>
{hacker?.points || 0}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we also don't rlly need this big point section in base dash, since points are dependent on the hack at hand
base is ideally:

  • hacker info
  • hacker status management
  • upcoming events

base can remove anything concerning classes, points, leaderboards, etc

@DVidal1205

Copy link
Copy Markdown
Contributor

Addressed the requested cleanup in commit 64f5d90e:

  • Removed the base hackathon dashboard points card and leaderboard trigger so the base dashboard stays focused on hacker info, status management, and upcoming events.
  • Dropped the leftover class-info prop/type from the base dashboard API.
  • Switched the BloomKnights missing-hackathon branch to notFound() so it returns a real App Router 404 instead of rendering the 404 page with a 200.

Verified with:

  • git diff --check
  • pnpm --dir apps/blade exec prettier --check src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx src/app/_components/dashboard/hackathon-dashboard/components.tsx src/app/hackathon/bloomknights/page.tsx
  • cd apps/blade && timeout 90 pnpm exec eslint src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx src/app/_components/dashboard/hackathon-dashboard/components.tsx src/app/hackathon/bloomknights/page.tsx
  • cd apps/blade && timeout 120 pnpm exec tsc --noEmit --pretty false

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx (1)

54-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive status during render instead of syncing it via useEffect.

hackerStatus/hackerStatusColor are pure functions of hacker, so the useState + useEffect round-trip isn't needed. Computing them inline removes the react-hooks/set-state-in-effect suppression (the codebase discourages unjustified eslint-disable) and avoids the one-render lag where the badge shows the empty initial state before the effect runs.

♻️ Proposed refactor
-  const [hackerStatus, setHackerStatus] = useState<string | null>("");
-  const [hackerStatusColor, setHackerStatusColor] = useState<string>("");
-
   const { data: hacker, isError } = api.hackerQuery.getHacker.useQuery(
     { hackathonName: hackathon.name },
     {
       initialData: data,
     },
   );

   function getStatusName(status: StatusKey) {
     if (!status) return "";
     return HACKER_STATUS_MAP[status].name;
   }

   function getStatusColor(status: StatusKey) {
     if (!status) return "";
     return HACKER_STATUS_MAP[status].color;
   }

-  useEffect(() => {
-    // eslint-disable-next-line react-hooks/set-state-in-effect
-    setHackerStatus(getStatusName(hacker?.status));
-    setHackerStatusColor(getStatusColor(hacker?.status));
-  }, [hacker]);
+  const hackerStatus = getStatusName(hacker?.status);
+  const hackerStatusColor = getStatusColor(hacker?.status);

Note: drop the now-unused useState/useEffect imports after applying this.

As per coding guidelines: "Do not bypass type or lint errors with any, broad casts, eslint-disable, or ignored promises unless justified."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx`
around lines 54 - 78, The `hackathon-data.tsx` component is deriving
`hackerStatus` and `hackerStatusColor` through `useState` plus `useEffect`, but
both values are pure functions of `hacker`. Move this logic into the render path
by computing the status name/color directly from `hacker?.status` (using
`getStatusName` and `getStatusColor`), remove the state/effect pair and the
`react-hooks/set-state-in-effect` suppression, and drop any now-unused
`useState`/`useEffect` imports.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx`:
- Around line 54-78: The `hackathon-data.tsx` component is deriving
`hackerStatus` and `hackerStatusColor` through `useState` plus `useEffect`, but
both values are pure functions of `hacker`. Move this logic into the render path
by computing the status name/color directly from `hacker?.status` (using
`getStatusName` and `getStatusColor`), remove the state/effect pair and the
`react-hooks/set-state-in-effect` suppression, and drop any now-unused
`useState`/`useEffect` imports.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a2fe0ccc-4bd0-429f-a0b1-c3a27cf6cf82

📥 Commits

Reviewing files that changed from the base of the PR and between 8c152ba and 64f5d90.

📒 Files selected for processing (10)
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-data.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx
  • apps/blade/src/app/hackathon/bloomknights/page.tsx
  • apps/blade/src/app/hackathon/page.tsx
💤 Files with no reviewable changes (4)
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/team-points.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/point-leaderboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/hackathon-dashboard.tsx
  • apps/blade/src/app/_components/dashboard/hackathon-dashboard/components.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/blade/src/app/_components/dashboard/member-dashboard/current-hackathon-notice.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-dashboard.tsx
  • apps/blade/src/app/hackathon/bloomknights/page.tsx
  • apps/blade/src/app/_components/dashboard/hacker-dashboard/hacker-data.tsx
  • apps/blade/src/app/hackathon/page.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Blade Change modifies code in Blade app Database Change modifies code in the DB package Minor Small change - 1 reviewer required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants