Skip to content

Club/new site#450

Merged
DVidal1205 merged 30 commits into
mainfrom
club/new-site
Jun 21, 2026
Merged

Club/new site#450
DVidal1205 merged 30 commits into
mainfrom
club/new-site

Conversation

@myr124

@myr124 myr124 commented May 14, 2026

Copy link
Copy Markdown
Contributor

Why

To clean up club site files so that we can start implementing Figma from design team.

What

Closes: #ISSUE_NUMBER

Doesn't close any issues but helps devs start work on their respective pages.

  • Implemented navbar and gradient backgrounds with dot pattern from figma
  • Made routes for all pages
  • Modified globals.css to include these gradients, typography, colors and more so we can finetune.

Notes:

  • I would recommend to utilize global CSS variables to avoid duplicated code and fragmented CSS.
  • The gradients I have currently implemented don't match 1:1, if you guys know how to get that data from Figma accurately to convert to CSS that would be amazing (Dylan mentioned stacked gradients)

Test Plan

Current Home Page:
image

Other page with sample text placeholder (we should remove this comp once we put actual content):
image

Mobile Nav
image

Hamburger Menu (we can add css animations imo)
image

Checklist

  • [X ] Database: No schema changes, OR I ran pnpm db:generate and committed the generated files in packages/db/drizzle/
  • [X ] Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

Summary by CodeRabbit

  • New Features
    • Redesigned homepage with hero video, community carousel, mascot area, and upcoming events.
    • Added/updated pages: About, Events, Hackathon history, Sponsors, Teams.
    • Added a reduced-motion-aware motion/scroll effects system, plus improved navbar/mobile menu and a new footer.
    • Upgraded SEO with centralized metadata + JSON-LD, plus sitemap/robots and a custom 404 page.
    • Contact, Officers, and Links routes now redirect to their new destinations.
  • Bug Fixes
    • Improved image optimization for approved external CDN hosts.
  • Refactor
    • Consolidated styling into a unified design system.

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: d5ccb243-1fac-439b-bb39-4757a2bb76f5

📥 Commits

Reviewing files that changed from the base of the PR and between 2e81443 and 36c2e8c.

📒 Files selected for processing (8)
  • apps/club/src/app/_lib/site-config.ts
  • apps/club/src/app/globals.css
  • apps/club/src/app/page.tsx
  • apps/club/src/app/teams/team-roster.ts
  • apps/club/src/app/teams/teams-client.tsx
  • apps/club/src/app/teams/teams-config.ts
  • packages/api/src/routers/guild.ts
  • packages/consts/src/team.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/club/src/app/_lib/site-config.ts

📝 Walkthrough

Walkthrough

Complete rebuild of the apps/club Next.js application: removes all prior GSAP/rsuite/framer-motion landing components and inline SVG assets, then adds a centralized site config, SEO utilities, a tRPC Blade data client, a comprehensive CSS design system, a new layout shell (Navbar, Footer, MotionRuntime), and full page implementations for Home, About, Events, Hackathons, Sponsors, and Teams. Contact, Links, and Officers are converted to redirect stubs.

Changes

Club Site Redesign

Layer / File(s) Summary
Site config, SEO utilities, and asset registry
apps/club/next.config.js, apps/club/package.json, apps/club/src/app/_lib/site-config.ts, apps/club/src/app/_lib/assets.ts, apps/club/src/app/seo.ts, apps/club/src/app/sitemap.ts, apps/club/src/app/robots.ts
Centralizes site/org constants, public link map, nav/footer link arrays, canonical routes, CDN asset URL helpers, SEO metadata factory, JSON-LD generators, sitemap, robots, and Next.js image remote patterns. Removes typescript.ignoreBuildErrors and updates workspace dependencies to include @forge/consts, @forge/ui, and @trpc/client.
Blade tRPC client and event data pipeline
apps/club/src/app/_lib/blade-trpc.ts, apps/club/src/app/_lib/club-events.ts
Adds getBladeTrpcClient as a cached tRPC proxy wired to httpBatchLink + SuperJSON with x-trpc-source: club-site header. Defines PublicClubEvent shape, loadClubEvents with bounded-abort fetch logic and 8s timeout, plus timezone-aware formatting and date-key helpers for America/New_York.
CSS design system overhaul
apps/club/src/app/globals.css
Replaces the prior minimal stylesheet with ~2,400 lines of club design system: :root palette/sizing variables, hero/button/carousel/mascot/timeline/nav component classes, keyframe animations, and html[data-motion]-keyed motion handling with a reduced-motion path.
Root layout shell: MotionRuntime, Navbar, Footer
apps/club/src/app/_components/club-motion-runtime.tsx, apps/club/src/app/_components/navbar.tsx, apps/club/src/app/_components/footer.tsx, apps/club/src/app/layout.tsx
ClubMotionRuntime installs scroll/reveal/tilt CSS-variable effects via IntersectionObserver and requestAnimationFrame. Navbar adds scroll-driven Framer Motion fade with desktop and animated mobile menu, accessibility attributes, and route-based active state. Footer renders social, quick, and resource links via helper components. Root layout applies Montserrat, SEO metadata, viewport config, skip link, and JSON-LD injection.
Reusable shared UI components
apps/club/src/app/_components/club-content-page.tsx, apps/club/src/app/_components/json-ld.tsx, apps/club/src/app/_components/redirect.tsx, apps/club/src/app/global-error.tsx, apps/club/src/app/not-found.tsx
Adds ClubContentPage (prop-driven hero/stats/sections/CTA layout), JsonLd (safe script injection escaping < to reduce XSS risk), Redirect (static-export-compatible meta-refresh with internal/external link handling), a global error boundary, and a 404 page with mascot imagery.
Home page rebuild
apps/club/src/app/page.tsx, apps/club/src/app/_components/home-community-carousel.tsx, apps/club/src/app/_components/home-events.tsx
Replaces the WIP landing with HomePage composing a hero video background, HomeCommunityCarousel (pointer-swipe with axis-lock and min-distance gating), MascotsSection with hotspot markup and LinkedIn credit link, HomeEvents (fetched from Blade with abort), and an animated letter-indexed "sets you apart" headline.
About page
apps/club/src/app/about/page.tsx
Renders a hero section with background image and gradient overlays, programs grid with alternating left/right layout driven by a PROGRAMS constant, and a community section displaying culture points plus a collaboration image.
Events page with calendar and filter UI
apps/club/src/app/events/page.tsx, apps/club/src/app/events/events-client.tsx
Adds Schema.org EventSeries JSON-LD and full EventsClient component: interactive calendar panel with month navigation, tag-based FilterBar, upcoming events list with pagination, skeleton/empty states, all driven by loadClubEvents with abort signal.
Hackathons history page with timeline
apps/club/src/app/hackathons/hackathon-history.tsx, apps/club/src/app/hackathons/page.tsx
Computes desktop timeline layout (row positions, section height) and renders polaroid-style HackathonCard components with metric badges and metadata. Vertical CSS-driven Timeline rail shows progress. Mobile fallback renders a simplified card list via MobileHackathon.
Sponsors page with carousel and FAQ
apps/club/src/app/sponsors/sponsors-config.ts, apps/club/src/app/sponsors/sponsors-client.tsx, apps/club/src/app/sponsors/page.tsx
Hero section, featured supporter carousel (auto-advance on 5.2s interval, manual navigation, dot indicators), past sponsor grid with error fallback to text labels, and FAQ accordion. Data-driven by sponsors-config.ts (logo URLs, website URLs, sponsor entries, featured slides, FAQ items).
Teams page
apps/club/src/app/teams/page.tsx
Stub page that renders TeamsClient wired with the Blade URL from environment.
Legacy route redirects
apps/club/src/app/contact/page.tsx, apps/club/src/app/links/page.tsx, apps/club/src/app/officers/page.tsx
Contact, Links, and Officers routes replaced with Redirect stubs and robots: index=false. All prior GSAP/rsuite landing sections (About, Hero, Discover, Calendar, Impact, Sponsors), the old navigation system, and all inline SVG asset component files under contact, landing, links, and officers subtrees are deleted.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant RootLayout
  participant ClubMotionRuntime
  participant Navbar
  participant Page
  participant HomeEvents
  participant loadClubEvents
  participant BladeAPI

  Browser->>RootLayout: mount app
  RootLayout->>ClubMotionRuntime: mount useEffect
  ClubMotionRuntime->>Browser: install IntersectionObserver, scroll listener
  RootLayout->>Navbar: render with bladeUrl prop
  Navbar->>Browser: attach Framer Motion scroll listener
  RootLayout->>Page: render children (HomePage)
  Page->>HomeEvents: render with bladeUrl, eventLimit=6
  HomeEvents->>loadClubEvents: call(bladeUrl, AbortSignal)
  loadClubEvents->>BladeAPI: httpBatchLink POST /api/trpc
  BladeAPI-->>loadClubEvents: raw event records
  loadClubEvents-->>HomeEvents: PublicClubEvent[]
  HomeEvents-->>Browser: render event rows + skeleton/empty handling
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • KnightHacks/forge#359: Modifies apps/club/src/app/layout.tsx exported metadata with an OpenGraph banner image, directly overlapping with the root layout metadata refactor in this PR.

Suggested labels

Feature, Major, UI

Suggested reviewers

  • DVidal1205
  • Adr1an04
🚥 Pre-merge checks | ✅ 6 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Club/new site' does not follow the required format of starting with an issue number in brackets (e.g., '[#123]'). Update the title to follow the format: '[#ISSUE_NUMBER] Brief description' (max 72 chars). Example: '[#123] Implement refreshed Club site with navbar and pages'
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.
✅ Passed checks (6 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 No hardcoded secrets found. Environment variables (BLADE_URL) properly validated via Zod in env.ts; PUBLIC_LINKS contains only public social/platform URLs; .env.example has placeholder values only.
Validated Env Access ✅ Passed No direct process.env usage found outside env.ts and config files. All app code properly imports from "~/env" using the validated env pattern, following codebase conventions.
No Typescript Escape Hatches ✅ Passed Comprehensive scan of all 30+ new/modified TypeScript files in apps/club found zero instances of any type, @ts-ignore, @ts-expect-error, or non-null assertions. Code follows best practices (e...

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

✨ 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 club/new-site

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.

@farisAwheel

Copy link
Copy Markdown
Contributor
image i do think the navbar should not contain the sign up buttons if it goes into hamburger mode

@farisAwheel farisAwheel 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.

LGTM except the navbar behaves weirdly on mid-sized screens, see prev comment

@Adr1an04 Adr1an04 requested a review from a team as a code owner June 11, 2026 00:16
Comment on lines +45 to +54
const fieldTriggerClassName =
"h-12 overflow-hidden rounded-none border-x-0 border-b-2 border-t-0 border-white/75 bg-transparent px-0 text-left text-lg font-medium text-white shadow-none transition-colors hover:border-white hover:bg-transparent hover:text-white focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 data-[placeholder]:text-white/35 [&>span]:block [&>span]:max-w-[calc(100%-2rem)] [&>span]:truncate [&>svg]:text-white/55 sm:h-14 sm:text-xl md:text-2xl";
const fieldLabelClassName =
"text-base font-semibold leading-none text-white/85 md:text-lg";
const optionalTextClassName =
"ml-2 text-sm font-medium italic text-white/45 md:text-base";
const requiredMarkClassName =
"text-xl font-black text-[#ff4fd8] drop-shadow-[0_0_10px_rgba(255,79,216,0.85)] md:text-2xl";
const checkboxClassName =
"mt-0.5 flex h-5 w-5 items-center justify-center border-white/45 bg-white/5 text-white shadow-none data-[state=checked]:border-white data-[state=checked]:bg-white data-[state=checked]:text-[#21103d] focus-visible:ring-white/40 [&>span>svg]:h-5 [&>span>svg]:w-5";

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.

Why are you using class names like this rather than components?

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.

im confused on this too but honestly its ok probably just codex shenanigans lol

Comment thread apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx Outdated
Comment on lines +16 to +19
}: {
profile?: { firstName?: string | null; lastName?: string | null } | null;
profileKind?: PassProfileKind;
}) {

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.

Is there a better way to represent this?

@alexanderpaolini alexanderpaolini 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.

I only looked over @forge/blade and @forge/api for the most part. Most of everything I'm asking is just curiosity

Comment on lines +70 to +71
if (profile && (!profile.firstName || !profile.lastName)) {
toast.error("Missing profile information");

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.

I'm a bit confused why this is even possible. Since this component passes through profile, can we not just require that profile is defined? Might be something to look into.

>
{selectedJudge
? `${selectedJudge.name} ${selectedJudge.challengeTitle}${
? `${selectedJudge.name}, ${selectedJudge.challengeTitle}${

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.

A side effect of the control-f on m-dashes? I think this was unintentional

</a>
) : (
""
"Not rated"

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.

Haven't seen what this look like, but surely the m-dash is better than Not rated. Low importance.

Comment thread apps/blade/src/app/_components/navigation/user-qr-code.tsx
Comment thread apps/blade/src/app/_components/user-interface.tsx
Comment thread apps/blade/src/trpc/react.tsx Outdated
Comment thread apps/bloomknights/public/event-banner.png Outdated
Comment thread packages/validators/src/index.ts
Comment thread packages/email/src/hackathons/types.ts
Comment thread packages/api/src/services/qr-code.ts
@DanielJEfres

Copy link
Copy Markdown
Contributor

general tidbit: please mark any coomments as resolved when addressed in the changes. im unsure as to whether or not alex's comments have been addressed atm

@DanielJEfres DanielJEfres self-requested a review June 16, 2026 07:15

@DanielJEfres DanielJEfres 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.

pls read over my comments and resolve them when fixed / answer if im wrong about something. once all are resolved it's basically approved from me !

apart from what i pointed out everything looked amazing, this is a great job well done i just wanted to make sure it was as perfect as we could get it. mostly looked for accessibility or performance issues, but found some other stuff worth pointing out on the way aswell

great job chat

Comment thread apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx Outdated
Comment thread apps/blade/src/app/_components/judge/projects-table.tsx
Comment thread apps/blade/src/app/_components/dashboard/hacker/hacker-application-form.tsx Outdated
Comment thread apps/club/next.config.js
@@ -4,13 +4,32 @@ const config = {
reactStrictMode: true,
images: {
unoptimized: true,

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.

critical: why are images unoptimized? we should just remove this line and rely on the remote patterns and cdn r2

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.

codex say bad. jason not know enough to no agree

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

codex also say bad. michael don't know web

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.

56f140d safe to remove me think

Comment thread apps/club/src/app/_components/navigation/navbar.tsx Outdated
Comment thread apps/club/src/app/sponsors/sponsors-client.tsx Outdated
Comment thread apps/bloomknights/public/event-banner.png Outdated
Comment thread apps/blade/src/app/api/public/club-teams/route.ts Outdated

@Michael-RDev Michael-RDev 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.

lowkey most of it looks fine,no new issues that someone else didn't address.

Comment thread apps/club/next.config.js
@@ -4,13 +4,32 @@ const config = {
reactStrictMode: true,
images: {
unoptimized: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

codex also say bad. michael don't know web

Comment thread packages/db/scripts/update_team_profile_pictures.ts Outdated
@Adr1an04 Adr1an04 changed the base branch from club-site-staging to main June 17, 2026 22:48
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 17, 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: 6

🧹 Nitpick comments (11)
apps/club/src/app/sponsors/sponsors-config.ts (1)

150-366: 💤 Low value

Consider normalizing sponsor ID and logoUrl key references.

The PAST_SPONSORS array mixes direct key access (e.g., ONLINE_SPONSOR_LOGOS.github) with bracket notation using space-separated keys (e.g., ONLINE_SPONSOR_LOGOS["lockheed martin"]). While this works at runtime because normalizeSponsorKey handles both, it creates maintainability friction. Consider either:

  • Using kebab-case IDs consistently and updating ONLINE_SPONSOR_LOGOS keys to match, or
  • Extracting a helper like getLogo(sponsorId) that normalizes access.
🤖 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/club/src/app/sponsors/sponsors-config.ts` around lines 150 - 366, The
PAST_SPONSORS array uses inconsistent patterns to access ONLINE_SPONSOR_LOGOS,
mixing direct key access like ONLINE_SPONSOR_LOGOS.github with bracket notation
like ONLINE_SPONSOR_LOGOS["lockheed martin"]. Create a helper function (e.g.,
getLogo) that takes a sponsor ID and returns the normalized logo URL from
ONLINE_SPONSOR_LOGOS by handling the key normalization internally. Then replace
all logoUrl assignments in the PAST_SPONSORS array to use this helper function
instead of direct ONLINE_SPONSOR_LOGOS access, ensuring consistent and
maintainable code.
apps/club/src/app/hackathons/hackathon-history.tsx (1)

47-104: ⚡ Quick win

Consider documenting the source of precise timeline coordinates.

The baseTimelineRows array contains coordinates with sub-pixel precision (e.g., 92.71826171875px). While this precision is valid, adding a comment explaining these values come from Figma or design specs would help future maintainers understand why they shouldn't be rounded.

📝 Suggested documentation
+// Timeline row positions derived from Figma design specs (do not round)
 const baseTimelineRows = [
   {
     side: "left",
🤖 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/club/src/app/hackathons/hackathon-history.tsx` around lines 47 - 104,
The baseTimelineRows array contains coordinates with sub-pixel precision that
lack documentation about their origin. Add a comment block above the
baseTimelineRows constant explaining that these precise coordinate values come
from Figma or design specifications, and clarify that this precision should be
maintained and not rounded to simpler values to ensure accurate layout
positioning.
apps/club/src/app/events/page.tsx (1)

12-18: ⚡ Quick win

Unify Blade URL source to avoid schema/link drift.

eventsPageJsonLd.offers.url uses BLADE_URL, while the page UI and client fetches use env.BLADE_URL. Keeping one source prevents mismatches between structured data and user-facing links.

Proposed fix
 import {
-  BLADE_URL,
   createPageMetadata,
   createWebPageJsonLd,
   DISCORD_URL,
   INSTAGRAM_URL,
   SITE_URL,
 } from "../seo";
@@
       offers: {
         "`@type`": "Offer",
-        url: BLADE_URL,
+        url: env.BLADE_URL,
         price: "0",
         priceCurrency: "USD",
         availability: "https://schema.org/InStock",
       },

Also applies to: 72-75, 148-149, 168-169

🤖 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/club/src/app/events/page.tsx` around lines 12 - 18, The file imports
BLADE_URL from the seo module, but uses env.BLADE_URL in other places (lines
72-75, 148-149, 168-169), creating inconsistency between structured data and UI
links. Replace all instances of env.BLADE_URL with the imported BLADE_URL from
the seo module to ensure a single, unified source for the Blade URL throughout
the entire file, preventing schema and link drift.
apps/club/src/app/events/events-client.tsx (2)

253-275: ⚡ Quick win

Add explicit date semantics to calendar day buttons.

The button text is numeric-only, which is ambiguous for assistive tech. Add aria-label (date + event count) and aria-pressed for selected state.

Proposed fix
         {cells.map((cell) => {
           const hasVisibleEvent = cell.inMonth && cell.eventCount > 0;
+          const dayLabel = `${cell.dateKey}${cell.eventCount > 0 ? `, ${cell.eventCount} event${cell.eventCount === 1 ? "" : "s"}` : ", no events"}`;
 
           return (
             <button
               key={cell.key}
               type="button"
               disabled={!cell.inMonth}
+              aria-label={dayLabel}
+              aria-pressed={cell.inMonth ? cell.isSelected : undefined}
               className={cn(
🤖 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/club/src/app/events/events-client.tsx` around lines 253 - 275, The
calendar day button in the events-client.tsx file needs improved accessibility
attributes. Add an aria-label attribute to the button element that includes the
date information from cell.dateKey and indicates whether there is a visible
event (using hasVisibleEvent), and add an aria-pressed attribute set to the
cell.isSelected value to properly communicate the selected state to assistive
technologies. This will make the semantic meaning of each calendar day button
clear to screen readers.

408-418: ⚡ Quick win

Expose active pagination state with aria-current.

Screen readers currently don’t get an explicit current-page marker. Add aria-current="page" on the active page button.

Proposed fix
           <button
             key={page}
             type="button"
+            aria-current={page === currentPage ? "page" : undefined}
             className={cn(
               "underline-offset-4 transition-colors hover:text-white",
               page === currentPage && "text-white underline",
             )}
🤖 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/club/src/app/events/events-client.tsx` around lines 408 - 418, The
pagination button component needs accessibility improvements for screen readers.
When a page button is the active page (when page === currentPage), add the
aria-current attribute with value "page" to explicitly mark the current page
button. This should be added conditionally to the button element alongside the
existing className prop, so screen readers can announce which page is currently
selected.
apps/club/src/app/_lib/club-events.ts (1)

48-52: ⚡ Quick win

Replace the broad cast with a type guard.

Line 50 uses a broad as cast in external-data normalization. A narrow guard keeps the same runtime checks while preserving stronger TypeScript safety.

Proposed refactor
 interface BladeEventRecord {
   id?: unknown;
   name?: unknown;
   description?: unknown;
   startDateTime?: unknown;
   endDateTime?: unknown;
   location?: unknown;
   tag?: unknown;
 }

+function isBladeEventRecord(value: unknown): value is BladeEventRecord {
+  return typeof value === "object" && value !== null;
+}
+
 function normalizeBladeEvents(
   value: unknown,
   limit = DEFAULT_EVENT_LIMIT,
 ): PublicClubEvent[] {
   if (!Array.isArray(value)) return [];

   const now = new Date();

   return value
     .map((event): PublicClubEvent | null => {
-      if (!event || typeof event !== "object") return null;
-
-      const bladeEvent = event as BladeEventRecord;
+      if (!isBladeEventRecord(event)) return null;
+      const bladeEvent = event;

As per coding guidelines, **/*.{ts,tsx,js,jsx} and **/*.{ts,tsx} disallow broad casts/TypeScript escape hatches.

🤖 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/club/src/app/_lib/club-events.ts` around lines 48 - 52, Replace the
broad `as BladeEventRecord` cast on the line assigning the bladeEvent variable
with a proper type guard function. Create a type guard function that validates
the event object has the required properties of BladeEventRecord (such as
startDateTime and endDateTime fields) using property checks, then use that type
guard instead of the cast. This maintains the same runtime safety checks while
preserving stronger TypeScript type safety and complying with the coding
guidelines that restrict broad casts in TypeScript files.

Source: Coding guidelines

apps/club/src/app/globals.css (1)

94-94: 💤 Low value

Consider lowercase text-rendering value for stylelint compliance.

Stylelint expects geometricprecision (lowercase), but the CSS spec accepts both. If CI enforces this rule, update the casing.

-  text-rendering: geometricPrecision;
+  text-rendering: geometricprecision;
🤖 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/club/src/app/globals.css` at line 94, The text-rendering property value
in the globals.css file uses camelCase casing (geometricPrecision) but stylelint
requires lowercase casing (geometricprecision) for CI compliance. Locate the
text-rendering property and change its value from geometricPrecision to
geometricprecision to match stylelint's expected formatting rules.

Source: Linters/SAST tools

apps/club/src/app/_components/footer.tsx (2)

73-73: ⚡ Quick win

Add role="list" or use a semantic element for the social links container.

The aria-label on a generic <div> is less meaningful to assistive technologies without a corresponding role. Consider adding role="list" or using a <nav> or <ul> element.

-    <div className={className} aria-label="Social links">
+    <nav className={className} aria-label="Social links">
       {SOCIAL_LINKS.map((social) => {
         ...
       })}
-    </div>
+    </nav>
🤖 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/club/src/app/_components/footer.tsx` at line 73, The social links
container div element uses aria-label without a proper semantic role, which
reduces accessibility for assistive technologies. Either add role="list" to the
existing div with className and aria-label="Social links", or replace the div
element with a more semantic element such as nav or ul depending on the
structure of the social links content within it.

176-179: ⚡ Quick win

Consider using dynamic year for copyright.

The hardcoded year "2026" will require annual updates. A dynamic approach prevents staleness:

-            <span className="md:hidden">© 2026 Knight Hacks.</span>
-            <span className="hidden md:inline">
-              © Copyright 2026, All Rights Reserved by Knight Hacks
-            </span>
+            <span className="md:hidden">© {new Date().getFullYear()} Knight Hacks.</span>
+            <span className="hidden md:inline">
+              © Copyright {new Date().getFullYear()}, All Rights Reserved by Knight Hacks
+            </span>

Note: Since this is a Server Component, new Date() evaluates at build/request time, which is typically acceptable for copyright years.

🤖 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/club/src/app/_components/footer.tsx` around lines 176 - 179, Replace the
hardcoded year "2026" in both the mobile view span (className="md:hidden") and
the desktop view span (className="hidden md:inline") in the footer.tsx footer
component with a dynamic calculation using new Date().getFullYear() to ensure
the copyright year automatically updates without requiring annual maintenance.
apps/club/src/app/_components/json-ld.tsx (1)

1-10: 💤 Low value

dangerouslySetInnerHTML is safe here for server-controlled JSON-LD.

The static analysis warning about XSS is a false positive in this context. The pattern used—escaping < as \u003c—is the standard safe approach for JSON-LD injection, preventing </script> breakout attacks.

This works safely because:

  1. The data originates from server-controlled siteJsonLd in seo.ts, not user input
  2. JSON-LD scripts are non-executable data blocks for search engines

For extra defense-in-depth, you could also escape >:

Optional: Enhanced escaping
-        __html: JSON.stringify(data).replace(/</g, "\\u003c"),
+        __html: JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e"),
🤖 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/club/src/app/_components/json-ld.tsx` around lines 1 - 10, The JsonLd
component currently escapes only the `<` character to prevent script tag
breakout attacks, which is safe for server-controlled JSON-LD data. For enhanced
defense-in-depth security, modify the dangerouslySetInnerHTML escaping logic in
the JsonLd function to also escape the `>` character as `\u003e` in addition to
the existing `<` to `\u003c` replacement, providing an extra layer of protection
against potential edge cases.

Source: Linters/SAST tools

apps/club/src/app/global-error.tsx (1)

3-21: ⚡ Quick win

Add error recovery capability to the global error boundary.

Next.js provides error and reset props to global error boundaries. The current implementation doesn't accept these props, which means users have no way to recover from errors without manually refreshing the page.

Adding a reset button improves UX by letting users retry without a full page reload.

🔄 Suggested enhancement
-export default function GlobalError() {
+export default function GlobalError({
+  reset,
+}: {
+  error: Error & { digest?: string };
+  reset: () => void;
+}) {
   return (
     <html lang="en">
       <body>
         <main className="flex min-h-screen items-center justify-center bg-[`#140316`] px-6 text-center text-white">
           <div className="max-w-xl">
             <p className="club-eyebrow text-sm font-black">Knight Hacks</p>
             <h1 className="mt-5 text-4xl font-black uppercase md:text-6xl">
               Something went wrong
             </h1>
             <p className="mt-5 text-base font-semibold leading-7 text-[`#d1d5dc`]">
               Please refresh the page or try again in a moment.
             </p>
+            <button
+              onClick={() => reset()}
+              className="mt-6 rounded-lg bg-[var(--club-gold)] px-6 py-3 font-bold text-black transition hover:opacity-90"
+            >
+              Try again
+            </button>
           </div>
         </main>
       </body>
     </html>
   );
 }
🤖 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/club/src/app/global-error.tsx` around lines 3 - 21, The GlobalError
component is not accepting the error and reset props that Next.js provides to
global error boundaries, which prevents users from recovering from errors
without manual page refresh. Update the GlobalError function signature to accept
error and reset as parameters, then add a clickable button element within the
component that calls the reset function when clicked to allow users to retry
without requiring a full page reload.
🤖 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/club/src/app/_components/home-events.tsx`:
- Around line 22-26: The LoadingRows function is using the hardcoded
HOME_EVENT_LIMIT constant when rendering loading placeholders instead of
respecting a dynamic eventLimit parameter. Modify the LoadingRows function
signature to accept an eventLimit parameter, then replace HOME_EVENT_LIMIT in
the Array.from call on line 25 with the eventLimit parameter. Additionally,
update any calls to LoadingRows to pass the appropriate eventLimit value as an
argument to ensure loading placeholders match the number of events actually
displayed.

In `@apps/club/src/app/_lib/club-events.ts`:
- Around line 54-63: The validation block is incorrectly filtering events using
startDate <= now, which removes active in-progress events. Replace the startDate
comparison on line 57 with endDate <= now to properly expire only past events.
Additionally, add a validation check to reject invalid date ranges where
startDate >= endDate, ensuring events have a valid time window before the
current date check.

In `@apps/club/src/app/_lib/site-config.ts`:
- Around line 30-37: The CLUB_NAV_ITEMS constant currently contains 6 navigation
items, but the design spec calls for a 4-link primary navigation in the header.
Split this constant into two separate navigation lists: keep Home, Events,
Teams, and Sponsors in the primary CLUB_NAV_ITEMS (which the Navbar component
consumes), and create a new secondary navigation constant for About and
Hackathons that can be used in footer or secondary navigation areas. This
ensures the primary header navigation aligns with the 4-link desktop/mobile
specification.

In `@apps/club/src/app/layout.tsx`:
- Line 104: The root wrapper div with className "club-home-bg flex min-h-screen
flex-col overflow-hidden" is preventing horizontal scrolling on child elements
like the hackathon history page because the parent's overflow-hidden takes
precedence over child overflow-x-auto rules. Either remove the overflow-hidden
class from this root wrapper entirely and apply overflow-hidden only to specific
child components (like the hero component) that need it, or replace
overflow-hidden with overflow-y-hidden overflow-x-visible on the root wrapper to
prevent vertical clipping while allowing horizontal scroll. Choose the approach
that best maintains your layout design while enabling the hackathon history page
to scroll horizontally as needed.

In `@apps/club/src/app/seo.ts`:
- Around line 83-85: The alternates.canonical property is using the relative
path variable instead of the computed url variable. Since the function already
computes const url = absoluteUrl(path) and uses it for openGraph.url, replace
the canonical value from path to url to ensure consistency in how absolute URLs
are used throughout the metadata object.

In `@apps/club/src/app/sponsors/sponsors-client.tsx`:
- Around line 222-227: The anchor tag with href={sponsorUrl} in the
sponsors-client.tsx file is missing security attributes required for external
links. Add target="_blank" and rel="noopener noreferrer" attributes to the <a>
element that wraps the "Become a Sponsor" text and ArrowRight icon to prevent
security vulnerabilities when opening external URLs, especially user-controlled
ones from PUBLIC_LINKS.blade.

---

Nitpick comments:
In `@apps/club/src/app/_components/footer.tsx`:
- Line 73: The social links container div element uses aria-label without a
proper semantic role, which reduces accessibility for assistive technologies.
Either add role="list" to the existing div with className and aria-label="Social
links", or replace the div element with a more semantic element such as nav or
ul depending on the structure of the social links content within it.
- Around line 176-179: Replace the hardcoded year "2026" in both the mobile view
span (className="md:hidden") and the desktop view span (className="hidden
md:inline") in the footer.tsx footer component with a dynamic calculation using
new Date().getFullYear() to ensure the copyright year automatically updates
without requiring annual maintenance.

In `@apps/club/src/app/_components/json-ld.tsx`:
- Around line 1-10: The JsonLd component currently escapes only the `<`
character to prevent script tag breakout attacks, which is safe for
server-controlled JSON-LD data. For enhanced defense-in-depth security, modify
the dangerouslySetInnerHTML escaping logic in the JsonLd function to also escape
the `>` character as `\u003e` in addition to the existing `<` to `\u003c`
replacement, providing an extra layer of protection against potential edge
cases.

In `@apps/club/src/app/_lib/club-events.ts`:
- Around line 48-52: Replace the broad `as BladeEventRecord` cast on the line
assigning the bladeEvent variable with a proper type guard function. Create a
type guard function that validates the event object has the required properties
of BladeEventRecord (such as startDateTime and endDateTime fields) using
property checks, then use that type guard instead of the cast. This maintains
the same runtime safety checks while preserving stronger TypeScript type safety
and complying with the coding guidelines that restrict broad casts in TypeScript
files.

In `@apps/club/src/app/events/events-client.tsx`:
- Around line 253-275: The calendar day button in the events-client.tsx file
needs improved accessibility attributes. Add an aria-label attribute to the
button element that includes the date information from cell.dateKey and
indicates whether there is a visible event (using hasVisibleEvent), and add an
aria-pressed attribute set to the cell.isSelected value to properly communicate
the selected state to assistive technologies. This will make the semantic
meaning of each calendar day button clear to screen readers.
- Around line 408-418: The pagination button component needs accessibility
improvements for screen readers. When a page button is the active page (when
page === currentPage), add the aria-current attribute with value "page" to
explicitly mark the current page button. This should be added conditionally to
the button element alongside the existing className prop, so screen readers can
announce which page is currently selected.

In `@apps/club/src/app/events/page.tsx`:
- Around line 12-18: The file imports BLADE_URL from the seo module, but uses
env.BLADE_URL in other places (lines 72-75, 148-149, 168-169), creating
inconsistency between structured data and UI links. Replace all instances of
env.BLADE_URL with the imported BLADE_URL from the seo module to ensure a
single, unified source for the Blade URL throughout the entire file, preventing
schema and link drift.

In `@apps/club/src/app/global-error.tsx`:
- Around line 3-21: The GlobalError component is not accepting the error and
reset props that Next.js provides to global error boundaries, which prevents
users from recovering from errors without manual page refresh. Update the
GlobalError function signature to accept error and reset as parameters, then add
a clickable button element within the component that calls the reset function
when clicked to allow users to retry without requiring a full page reload.

In `@apps/club/src/app/globals.css`:
- Line 94: The text-rendering property value in the globals.css file uses
camelCase casing (geometricPrecision) but stylelint requires lowercase casing
(geometricprecision) for CI compliance. Locate the text-rendering property and
change its value from geometricPrecision to geometricprecision to match
stylelint's expected formatting rules.

In `@apps/club/src/app/hackathons/hackathon-history.tsx`:
- Around line 47-104: The baseTimelineRows array contains coordinates with
sub-pixel precision that lack documentation about their origin. Add a comment
block above the baseTimelineRows constant explaining that these precise
coordinate values come from Figma or design specifications, and clarify that
this precision should be maintained and not rounded to simpler values to ensure
accurate layout positioning.

In `@apps/club/src/app/sponsors/sponsors-config.ts`:
- Around line 150-366: The PAST_SPONSORS array uses inconsistent patterns to
access ONLINE_SPONSOR_LOGOS, mixing direct key access like
ONLINE_SPONSOR_LOGOS.github with bracket notation like
ONLINE_SPONSOR_LOGOS["lockheed martin"]. Create a helper function (e.g.,
getLogo) that takes a sponsor ID and returns the normalized logo URL from
ONLINE_SPONSOR_LOGOS by handling the key normalization internally. Then replace
all logoUrl assignments in the PAST_SPONSORS array to use this helper function
instead of direct ONLINE_SPONSOR_LOGOS access, ensuring consistent and
maintainable code.
🪄 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: bf61c18d-e4a3-4836-9b46-69ebd834d40e

📥 Commits

Reviewing files that changed from the base of the PR and between 3206fdf and 27020ef.

⛔ Files ignored due to path filters (33)
  • apps/bloomknights/public/event-banner.png is excluded by !**/*.png
  • apps/club/public/community.jpg is excluded by !**/*.jpg
  • apps/club/public/community.png is excluded by !**/*.png
  • apps/club/public/hackathon.JPG is excluded by !**/*.jpg
  • apps/club/public/hackathons/figma-card-real-0.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-1.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-2.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-3.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-4.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-5.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-card-real-6.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-footer-logo.png is excluded by !**/*.png
  • apps/club/public/hackathons/figma-timeline-extended.png is excluded by !**/*.png
  • apps/club/public/hackathons/icon-email.png is excluded by !**/*.png
  • apps/club/public/hackathons/icon-instagram.png is excluded by !**/*.png
  • apps/club/public/hackathons/icon-linkedin.png is excluded by !**/*.png
  • apps/club/public/hackathons/icon-twitter.png is excluded by !**/*.png
  • apps/club/public/hackathons/logo-130.svg is excluded by !**/*.svg
  • apps/club/public/hero/club-hero.mp4 is excluded by !**/*.mp4
  • apps/club/public/hero/club-hero.webm is excluded by !**/*.webm
  • apps/club/public/kh-icon.svg is excluded by !**/*.svg
  • apps/club/public/khtext.svg is excluded by !**/*.svg
  • apps/club/public/knight-hacks-sets-you-apart.png is excluded by !**/*.png
  • apps/club/public/knighthacks.svg is excluded by !**/*.svg
  • apps/club/public/logos/microsoft.svg is excluded by !**/*.svg
  • apps/club/public/members.JPG is excluded by !**/*.jpg
  • apps/club/public/officers/bobda.png is excluded by !**/*.png
  • apps/club/public/officers/daniel.png is excluded by !**/*.png
  • apps/club/public/officers/ricky.png is excluded by !**/*.png
  • apps/club/public/projects1.JPG is excluded by !**/*.jpg
  • apps/club/public/projects2.jpg is excluded by !**/*.jpg
  • apps/club/public/sigilKH.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (116)
  • apps/club/next.config.js
  • apps/club/package.json
  • apps/club/public/community-card.webp
  • apps/club/public/hackathons/figma-background.webp
  • apps/club/public/hackathons/kh-history-hero.webp
  • apps/club/public/hero/club-hero-poster.webp
  • apps/club/public/projects-card.webp
  • apps/club/public/workshop-card.webp
  • apps/club/src/app/_components/club-content-page.tsx
  • apps/club/src/app/_components/club-motion-runtime.tsx
  • apps/club/src/app/_components/contact/assets/abstract-left.tsx
  • apps/club/src/app/_components/contact/assets/abstract-right.tsx
  • apps/club/src/app/_components/contact/assets/shield.tsx
  • apps/club/src/app/_components/contact/assets/sword-left.tsx
  • apps/club/src/app/_components/contact/assets/sword-right.tsx
  • apps/club/src/app/_components/contact/contact-form.tsx
  • apps/club/src/app/_components/contact/header.tsx
  • apps/club/src/app/_components/contact/left-side.tsx
  • apps/club/src/app/_components/contact/right-side.tsx
  • apps/club/src/app/_components/footer.tsx
  • apps/club/src/app/_components/home-community-carousel.tsx
  • apps/club/src/app/_components/home-events.tsx
  • apps/club/src/app/_components/json-ld.tsx
  • apps/club/src/app/_components/landing/about.tsx
  • apps/club/src/app/_components/landing/assets/coolbutton.tsx
  • apps/club/src/app/_components/landing/assets/coolbutton2.tsx
  • apps/club/src/app/_components/landing/assets/group.tsx
  • apps/club/src/app/_components/landing/assets/neon-tk.tsx
  • apps/club/src/app/_components/landing/assets/sword.tsx
  • apps/club/src/app/_components/landing/assets/terminal.tsx
  • apps/club/src/app/_components/landing/calendar.tsx
  • apps/club/src/app/_components/landing/discover-assets/counter.tsx
  • apps/club/src/app/_components/landing/discover-assets/discover-button.tsx
  • apps/club/src/app/_components/landing/discover.tsx
  • apps/club/src/app/_components/landing/hero-assets/hero-icon.tsx
  • apps/club/src/app/_components/landing/hero-assets/typing-text.tsx
  • apps/club/src/app/_components/landing/hero.tsx
  • apps/club/src/app/_components/landing/impact-assets/expandable.tsx
  • apps/club/src/app/_components/landing/impact-assets/wave-reveal.tsx
  • apps/club/src/app/_components/landing/impact.tsx
  • apps/club/src/app/_components/landing/sponsors-assets/sponsor-card.tsx
  • apps/club/src/app/_components/landing/sponsors-assets/sponsor-marquee.tsx
  • apps/club/src/app/_components/landing/sponsors.tsx
  • apps/club/src/app/_components/legacy-redirect-page.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-left-1.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-left-2.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-right-1.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-right-2.tsx
  • apps/club/src/app/_components/links/assets/binary-icon.tsx
  • apps/club/src/app/_components/links/assets/blank-calendar.tsx
  • apps/club/src/app/_components/links/assets/chat-bubble.tsx
  • apps/club/src/app/_components/links/assets/facebook.tsx
  • apps/club/src/app/_components/links/assets/instagram.tsx
  • apps/club/src/app/_components/links/assets/kh-logo.tsx
  • apps/club/src/app/_components/links/assets/knighthacks-text.tsx
  • apps/club/src/app/_components/links/assets/laptop-charging.tsx
  • apps/club/src/app/_components/links/assets/linkedin.tsx
  • apps/club/src/app/_components/links/assets/mail.tsx
  • apps/club/src/app/_components/links/assets/menu-horizontal.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/linkedin-icon.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/major.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/round-major.tsx
  • apps/club/src/app/_components/links/assets/outline.tsx
  • apps/club/src/app/_components/links/assets/terminal-icon.tsx
  • apps/club/src/app/_components/links/assets/tk-neon-sign.tsx
  • apps/club/src/app/_components/links/assets/tk-neon.tsx
  • apps/club/src/app/_components/links/assets/twitter.tsx
  • apps/club/src/app/_components/links/assets/youtube.tsx
  • apps/club/src/app/_components/links/button.tsx
  • apps/club/src/app/_components/links/links-header.tsx
  • apps/club/src/app/_components/navbar.tsx
  • apps/club/src/app/_components/navigation/mobile/desktop.tsx
  • apps/club/src/app/_components/navigation/mobile/mobile-navbar.tsx
  • apps/club/src/app/_components/navigation/mobile/nav-sheet.tsx
  • apps/club/src/app/_components/navigation/navbar.tsx
  • apps/club/src/app/_components/navigation/navlink.tsx
  • apps/club/src/app/_components/not-found-redirect.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/linkedin-icon.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/major.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/round-major.tsx
  • apps/club/src/app/_components/officers/assets/officer-card.tsx
  • apps/club/src/app/_components/officers/assets/officer-header-svg.tsx
  • apps/club/src/app/_components/officers/header.tsx
  • apps/club/src/app/_components/officers/officers.tsx
  • apps/club/src/app/_lib/assets.ts
  • apps/club/src/app/_lib/blade-trpc.ts
  • apps/club/src/app/_lib/club-events.ts
  • apps/club/src/app/_lib/site-config.ts
  • apps/club/src/app/about/page.tsx
  • apps/club/src/app/contact/page.tsx
  • apps/club/src/app/events/events-client.tsx
  • apps/club/src/app/events/page.tsx
  • apps/club/src/app/global-error.tsx
  • apps/club/src/app/globals.css
  • apps/club/src/app/hackathons/hackathon-history.tsx
  • apps/club/src/app/hackathons/page.tsx
  • apps/club/src/app/layout.tsx
  • apps/club/src/app/links/page.tsx
  • apps/club/src/app/not-found.tsx
  • apps/club/src/app/officers/page.tsx
  • apps/club/src/app/page.tsx
  • apps/club/src/app/robots.ts
  • apps/club/src/app/seo.ts
  • apps/club/src/app/sitemap.ts
  • apps/club/src/app/sponsors/page.tsx
  • apps/club/src/app/sponsors/sponsors-client.tsx
  • apps/club/src/app/sponsors/sponsors-config.ts
  • apps/club/src/app/teams/page.tsx
  • apps/club/src/app/teams/team-roster.ts
  • apps/club/src/app/teams/teams-client.tsx
  • apps/club/src/app/teams/teams-config.ts
  • apps/club/src/app/trpc/server.ts
  • apps/club/tailwind.config.ts
  • packages/api/src/routers/event.ts
  • packages/api/src/routers/guild.ts
  • packages/consts/src/team.ts
💤 Files with no reviewable changes (63)
  • apps/club/src/app/_components/contact/assets/shield.tsx
  • apps/club/src/app/_components/contact/assets/abstract-left.tsx
  • apps/club/src/app/_components/landing/sponsors-assets/sponsor-marquee.tsx
  • apps/club/src/app/_components/links/assets/linkedin.tsx
  • apps/club/src/app/_components/links/assets/instagram.tsx
  • apps/club/src/app/_components/contact/right-side.tsx
  • apps/club/src/app/_components/contact/header.tsx
  • apps/club/src/app/_components/officers/officers.tsx
  • apps/club/src/app/_components/links/button.tsx
  • apps/club/src/app/_components/landing/sponsors.tsx
  • apps/club/src/app/_components/links/assets/laptop-charging.tsx
  • apps/club/src/app/_components/contact/assets/abstract-right.tsx
  • apps/club/src/app/_components/landing/about.tsx
  • apps/club/src/app/_components/landing/hero-assets/typing-text.tsx
  • apps/club/src/app/_components/landing/assets/terminal.tsx
  • apps/club/src/app/_components/navigation/navbar.tsx
  • apps/club/src/app/_components/navigation/navlink.tsx
  • apps/club/src/app/_components/links/assets/menu-horizontal.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/linkedin-icon.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-left-1.tsx
  • apps/club/src/app/_components/landing/impact.tsx
  • apps/club/src/app/_components/landing/assets/neon-tk.tsx
  • apps/club/src/app/_components/landing/assets/coolbutton.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/round-major.tsx
  • apps/club/src/app/_components/landing/hero-assets/hero-icon.tsx
  • apps/club/src/app/_components/landing/assets/sword.tsx
  • apps/club/src/app/_components/contact/contact-form.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-right-1.tsx
  • apps/club/src/app/_components/officers/assets/officer-card.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/linkedin-icon.tsx
  • apps/club/src/app/_components/links/assets/tk-neon-sign.tsx
  • apps/club/src/app/_components/links/assets/mail.tsx
  • apps/club/src/app/_components/landing/discover-assets/counter.tsx
  • apps/club/src/app/_components/landing/calendar.tsx
  • apps/club/src/app/_components/navigation/mobile/desktop.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/major.tsx
  • apps/club/src/app/_components/links/assets/officer-card-assets/round-major.tsx
  • apps/club/src/app/_components/landing/discover.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-left-2.tsx
  • apps/club/src/app/_components/navigation/mobile/mobile-navbar.tsx
  • apps/club/src/app/_components/landing/discover-assets/discover-button.tsx
  • apps/club/src/app/_components/officers/assets/officer-header-svg.tsx
  • apps/club/src/app/_components/links/assets/chat-bubble.tsx
  • apps/club/src/app/_components/links/assets/kh-logo.tsx
  • apps/club/src/app/_components/officers/assets/officer-card-assets/major.tsx
  • apps/club/src/app/_components/landing/assets/group.tsx
  • apps/club/src/app/_components/links/assets/abstract-shape-right-2.tsx
  • apps/club/src/app/_components/links/assets/facebook.tsx
  • apps/club/src/app/_components/landing/impact-assets/expandable.tsx
  • apps/club/src/app/_components/landing/impact-assets/wave-reveal.tsx
  • apps/club/src/app/_components/links/assets/knighthacks-text.tsx
  • apps/club/src/app/_components/links/links-header.tsx
  • apps/club/src/app/_components/landing/sponsors-assets/sponsor-card.tsx
  • apps/club/src/app/_components/landing/assets/coolbutton2.tsx
  • apps/club/src/app/_components/links/assets/twitter.tsx
  • apps/club/src/app/_components/officers/header.tsx
  • apps/club/src/app/_components/landing/hero.tsx
  • apps/club/src/app/_components/links/assets/youtube.tsx
  • apps/club/src/app/_components/navigation/mobile/nav-sheet.tsx
  • apps/club/src/app/_components/links/assets/blank-calendar.tsx
  • apps/club/src/app/_components/links/assets/terminal-icon.tsx
  • apps/club/src/app/_components/contact/left-side.tsx
  • apps/club/src/app/_components/links/assets/tk-neon.tsx

Comment thread apps/club/src/app/_components/home-events.tsx Outdated
Comment thread apps/club/src/app/_lib/club-events.ts
Comment on lines +30 to +37
export const CLUB_NAV_ITEMS = [
{ label: "Home", href: "/" },
{ label: "About", href: "/about" },
{ label: "Events", href: "/events" },
{ label: "Hackathons", href: "/hackathons" },
{ label: "Teams", href: "/teams" },
{ label: "Sponsors", href: "/sponsors" },
] as const;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the primary nav aligned with the header design.

Navbar consumes CLUB_NAV_ITEMS directly, so About and Hackathons will render in the primary header wherever this constant is used. If the 4-link desktop/mobile spec from the PR is still the target, move those routes to a secondary/footer-only list instead.

🛠 Suggested split
 export const CLUB_NAV_ITEMS = [
   { label: "Home", href: "/" },
-  { label: "About", href: "/about" },
   { label: "Events", href: "/events" },
-  { label: "Hackathons", href: "/hackathons" },
   { label: "Teams", href: "/teams" },
   { label: "Sponsors", href: "/sponsors" },
 ] as const;
🤖 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/club/src/app/_lib/site-config.ts` around lines 30 - 37, The
CLUB_NAV_ITEMS constant currently contains 6 navigation items, but the design
spec calls for a 4-link primary navigation in the header. Split this constant
into two separate navigation lists: keep Home, Events, Teams, and Sponsors in
the primary CLUB_NAV_ITEMS (which the Navbar component consumes), and create a
new secondary navigation constant for About and Hackathons that can be used in
footer or secondary navigation areas. This ensures the primary header navigation
aligns with the 4-link desktop/mobile specification.

Comment thread apps/club/src/app/layout.tsx Outdated
Comment thread apps/club/src/app/seo.ts
Comment thread apps/club/src/app/sponsors/sponsors-client.tsx
@DVidal1205

Copy link
Copy Markdown
Contributor

Follow-up review notes

This is much cleaner than the previous version — the unrelated DB/email/QR/resume/security changes are gone, and CI is green. I was also able to pass local checks after rebuilding @forge/api types:

pnpm --filter=@forge/api build
pnpm --filter=@forge/club typecheck
pnpm --filter=@forge/club lint
BLADE_URL=https://blade.knighthacks.org pnpm --filter=@forge/club build
pnpm --filter=@forge/api typecheck
pnpm --filter=@forge/consts typecheck
git diff --check origin/main...HEAD

Remaining concerns I noticed before merge:

  1. This PR is still not purely Club frontend. It adds public tRPC procedures in packages/api and team constants in packages/consts. If that is intentional, the PR description should explicitly call out the new public Club data API surface.
  2. guild.getPublicClubTeamRoster exposes member names, role labels, profile pictures, and LinkedIn URLs publicly. It does respect guildProfileVisible, which is good, but this privacy/ownership contract should be explicit.
  3. /contact, /officers, and /links use client-side redirects via window.location.replace. If static export prevents server redirects, that limitation should be documented or moved to host-level redirects.
  4. The 404 page auto-redirects home after 4.5s. I would remove that and keep the manual “Go home” button.
  5. Calendar and pagination controls need small accessibility fixes (aria-label, aria-current, selected-day state).
  6. TEAM_ROLE_OVERRIDES hard-codes individual names in the frontend. That should either be a temporary documented workaround or fixed in the source role/member data.
  7. PR body still has Closes: #ISSUE_NUMBER and malformed checklist boxes.

Overall: this is a massive improvement and looks close, but there are still a few cleanup/ownership items before I would call it fully merge-ready.

Dylan and I will address these ourselves tomorrow, so this is mostly a tracking note rather than a request for immediate changes tonight.

@Adr1an04

Copy link
Copy Markdown
Contributor

hello dylan yaya here are the responses / fixes

  1. this is not purely club frontend since it adds public procedures for the club site to read Blade/Guild data like profile pictures and team info. we called that out in the pr body so the public club data api surface is explicit.

  2. the intent here is that guildProfileVisible is the opt-in for showing up publicly on the club site. so if someone has that off, they should not be returned at all. the fields coming back are only what we need for the team page: name, role, pfp, and linkedin. that contract is now more explicit in the code / naming so it’s clear this is public-facing and tied to that visibility flag.

  3. fixed the legacy redirects. /contact, /officers, and /links no longer use window.location.replace. since club is static export, they now use a static redirect fallback with a manual continue link.

  4. removed the 404 auto redirect. now it just keeps the manual “Go home” button.

  5. fixed the small accessibility stuff for calendar/pagination. calendar days now have labels, selected state, and today state. pagination now has labels and current page state.

  6. documented TEAM_ROLE_OVERRIDES as an election-cycle workaround that should be reviewed/changed after every Knight Hacks election.

  7. since i can’t directly edit the pr body because this isn’t my pr, here’s the full pr body / history that should replace the placeholder stuff:

Summary

This PR is the cleaned-up Club site refresh. It replaces the old static/officer/link/contact-heavy Club pages with the new public Club site experience, including the new home page, about page, events page, teams page, sponsors page, hackathon history timeline, shared nav/footer, SEO metadata, sitemap/robots, and static-export-safe legacy route handling.

It is not purely frontend-only anymore because the Club site now needs public Blade/Guild-backed data to avoid hardcoding everything. This adds a small public Club data API surface:

  • event.getPublicClubEvents for public upcoming Club events
  • guild.getPublicClubTeamRoster for the public team roster

The team roster endpoint only returns members with guildProfileVisible = true, and only returns the fields needed for the public team page: name, role label, profile picture, LinkedIn URL, and role color. That public visibility contract is now documented in the code.

Cleanup / scope history

This branch was cleaned up from the earlier oversized version. The unrelated DB/email/QR/resume/security changes were removed so the remaining scope is focused on the Club site and the API/const surfaces needed by the Club site.

The remaining cross-package changes are intentional:

  • packages/api exposes the Club site’s public event and roster data.
  • packages/consts stores shared Club team definitions / role matching constants used by the public roster logic.
  • apps/club consumes that public data to render events and teams.

Review fixes addressed

  • documented the public roster privacy contract around guildProfileVisible
  • replaced /contact, /officers, and /links client-side window.location.replace redirects with a static-export-safe redirect fallback and manual continue link
  • removed the 404 auto-redirect and kept the manual “Go home” button
  • added calendar/pagination accessibility fixes:
    • calendar day labels
    • selected day state
    • today state
    • pagination labels
    • current page state
  • documented TEAM_ROLE_OVERRIDES as an election-cycle workaround that should be reviewed/changed after every Knight Hacks election
  • updated hackathon timeline links for KH VIII and KH V to point at the actual KH sites instead of Devpost
  • cleaned up the PR placeholder issue/checklist text

Checks

The branch has passing local/CI-equivalent checks for the Club/API work, including format/lint/typecheck/build after rebuilding the API types when needed.

@DVidal1205 DVidal1205 dismissed stale reviews from farisAwheel, DanielJEfres, and coderabbitai[bot] June 21, 2026 18:56

resolved

@DVidal1205 DVidal1205 merged commit 0e67aac into main Jun 21, 2026
11 checks passed
@DVidal1205 DVidal1205 deleted the club/new-site branch June 21, 2026 19:01
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.

10 participants