Skip to content

Conversation

CodeMaverick-143
Copy link

@CodeMaverick-143 CodeMaverick-143 commented Oct 12, 2025

Description

This PR adds proper loading states to improve the user experience while fetching tournament data.

Changes

  • Introduced isLoading state in TournamentHub.tsx and TournamentBracketPage.tsx.
  • Added animated skeleton loaders for tournament cards in TournamentHub.tsx.
  • Added skeleton placeholders for champion, finalists, and first-round participants in TournamentBracketPage.tsx.
  • Smooth transition from loading state to actual data once the fetch is complete.

Benefits

  • Users get visual feedback that data is being loaded.
  • Consistent UI experience across tournament pages.
  • Prepares the app for real API integration in the future.

Related Issue

#106

Summary by CodeRabbit

  • New Features
    • Dynamic tournament bracket built from fetched participants.
    • Tournament Hub now loads data asynchronously with animated loading skeletons.
    • Added dark and high-contrast theme support.
    • New pulse and floating animations enhance loading and UI accents.
  • Style
    • Updated global design tokens for colors, borders, and backgrounds; consistent borders applied app-wide.
  • Chores
    • Updated page title to “DebateAI”.

Copy link

coderabbitai bot commented Oct 12, 2025

Walkthrough

Introduces loading states and skeleton UIs in TournamentHub and TournamentBracket pages with simulated async data fetching; converts tournament bracket to data-driven rendering using a Participant interface; adds global CSS variables for theming, base styles, and animations; updates HTML title to “DebateAI.”

Changes

Cohort / File(s) Summary of changes
Async loading and skeletons
frontend/src/Pages/TournamentHub.tsx, frontend/src/Pages/TournamentBracketPage.tsx
Added isLoading state and useEffect-based simulated fetches; introduced skeleton UI paths; transitioned bracket to compute rounds dynamically from fetched participants; added Participant interface; minor UI attribute adjustment.
Theming and animations
frontend/src/index.css
Added CSS variables for light/dark/contrast themes; global border color application; base body styling; introduced pulse and floating animations; reorganized base layer rules.
HTML metadata
frontend/index.html
Changed document title from “Vite + React + TS” to “DebateAI.”

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Hub as TournamentHub
  participant Timer as Simulated API (setTimeout)
  participant State as React State
  participant UI as UI

  User->>Hub: Navigate to TournamentHub
  Hub->>State: set isLoading = true, tournaments = []
  Hub->>Timer: request tournaments (simulate 1s)
  alt Loading
    State-->>UI: Render skeleton cards/title (animate-pulse)
  end
  Timer-->>Hub: Return initialTournaments
  Hub->>State: set tournaments, isLoading = false
  State-->>UI: Render tournament cards and Create form
Loading
sequenceDiagram
  autonumber
  actor User
  participant Bracket as TournamentBracketPage
  participant Timer as Simulated API (setTimeout)
  participant State as React State
  participant UI as UI

  User->>Bracket: Open Bracket Page
  Bracket->>State: set isLoading = true, participants = []
  Bracket->>Timer: request participants (simulate delay)
  alt Loading
    State-->>UI: Render bracket skeletons (animate-pulse)
  end
  Timer-->>Bracket: Return participants[]
  Bracket->>Bracket: Compute round1Winners → semiFinalWinners → champion
  Bracket->>State: set participants, isLoading = false
  State-->>UI: Render dynamic bracket (R1, SF, Final, Champion)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

A rabbit taps the loading drum—thump, thump—
Skeletons dance while data comes up.
Brackets bloom from fields of names,
CSS moonlight shifts its frames.
Title crowned: DebateAI’s the show—
Hop, compile, and off we go! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title does describe the addition of loading states for tournament data, but it includes specific component filenames and the issue number, which adds noise and reduces conciseness; a clear, single-sentence summary without file lists or issue references would better meet the guidelines. Please shorten the title to a single concise sentence that captures the main change—such as “Add loading states and skeleton UIs for tournament pages”—and remove file names and issue numbers for clarity.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
frontend/src/Pages/TournamentHub.tsx (1)

117-121: Add error handling for external avatar images.

Similar to the bracket page, avatar URLs use an external service without fallback handling. This duplicates the issue identified in TournamentBracketPage.tsx.

Consider extracting avatar rendering logic into a shared component to avoid duplication and ensure consistent error handling across both tournament pages.

Example shared component:

// components/ParticipantAvatar.tsx
interface Props {
  src: string;
  alt: string;
  className?: string;
}

export function ParticipantAvatar({ src, alt, className }: Props) {
  const [imgSrc, setImgSrc] = useState(src);
  
  return (
    <img
      src={imgSrc}
      alt={alt}
      onError={() => setImgSrc('/default-avatar.png')}
      className={className}
    />
  );
}
🧹 Nitpick comments (3)
frontend/src/index.css (1)

115-129: Remove unused animation or document its purpose.

The floatUp animation and .animate-floating class are defined but not referenced in any of the changed files in this PR.

If this animation is intended for future use, consider adding a comment explaining its purpose. Otherwise, remove it to keep the codebase clean:

-/* Floating animation */
-@keyframes floatUp {
-  0% {
-    transform: translateY(0);
-    opacity: 1;
-  }
-  100% {
-    transform: translateY(-150px);
-    opacity: 0;
-  }
-}
-
-.animate-floating {
-  animation: floatUp 2s ease-out forwards;
-}
frontend/src/Pages/TournamentBracketPage.tsx (1)

18-25: Handle potential failures when loading external avatar images.

The avatar URLs use an external service (https://i.pravatar.cc/) without error handling. If this service is unavailable or rate-limited, avatars will fail to load, degrading the user experience.

Consider adding:

  1. Fallback avatars using the onError event handler
  2. A local default avatar image
  3. Or generate avatar placeholders using the user's initials

Example implementation:

<img 
  src={participant.avatar} 
  alt={participant.name}
  onError={(e) => {
    e.currentTarget.src = '/default-avatar.png';
  }}
  className="w-full h-full object-cover" 
/>

Or use a library like react-avatar for more robust avatar generation.

frontend/src/Pages/TournamentHub.tsx (1)

16-62: Move initialTournaments outside the component to prevent unnecessary re-renders.

The initialTournaments array is defined inside the component body, creating a new reference on every render. While the current useEffect has an empty dependency array, this pattern is inefficient and could cause issues if the data is later added as a dependency.

Move the constant outside the component:

+const initialTournaments: Tournament[] = [
+  {
+    id: '1',
+    name: 'Spring Showdown',
+    maxParticipants: 8,
+    currentParticipants: 6,
+    date: '2025-04-20',
+    description:
+      'Compete in our annual spring debate championship with top contenders from around the globe.',
+  },
+  // ... rest of tournaments
+];
+
 export default function TournamentPage() {
-  const initialTournaments: Tournament[] = [
-    {
-      id: '1',
-      name: 'Spring Showdown',
-      // ...
-    },
-    // ...
-  ];
-
   const [tournaments, setTournaments] = useState<Tournament[]>([]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f29be62 and 9597bfb.

📒 Files selected for processing (4)
  • frontend/index.html (1 hunks)
  • frontend/src/Pages/TournamentBracketPage.tsx (1 hunks)
  • frontend/src/Pages/TournamentHub.tsx (4 hunks)
  • frontend/src/index.css (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/Pages/TournamentBracketPage.tsx (1)
backend/routes/rooms.go (1)
  • Participant (28-32)
🔇 Additional comments (3)
frontend/index.html (1)

7-7: LGTM!

The title update appropriately reflects the application branding.

frontend/src/Pages/TournamentHub.tsx (1)

137-139: Verify consistent animation usage across loading states.

The title uses animate-pulse in both the loading skeleton (line 137) and the actual page (line 160), causing it to pulse indefinitely even after data loads. This might be intentional for visual flair, but if it's meant only for loading states, it should be removed from line 160.

If the pulsing title is intentional for the loaded state, consider adding a comment to clarify this design choice. Otherwise, remove it:

-      <h1 className="text-4xl sm:text-5xl font-extrabold mb-10 text-center text-primary animate-pulse">
+      <h1 className="text-4xl sm:text-5xl font-extrabold mb-10 text-center text-primary">
         Tournament Arena
       </h1>
frontend/src/index.css (1)

90-93: Retain global @apply border-border
This rule only sets the default border-color (no borders are added) and is intentionally used so all Tailwind border utilities inherit the theme’s border color.

Likely an incorrect or invalid review comment.

Comment on lines +4 to +8
interface Participant {
id: number;
name: string;
avatar: string;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Align the Participant interface with the backend schema.

The frontend Participant interface doesn't match the backend's Participant struct from backend/routes/rooms.go (lines 27-31). The backend defines:

  • id as string (frontend uses number)
  • username instead of name
  • elo (rating) instead of avatar

This mismatch will cause integration issues when connecting to the real API.

Apply this diff to align with the backend:

 interface Participant {
-  id: number;
-  name: string;
-  avatar: string;
+  id: string;
+  username: string;
+  elo: number;
+  avatar?: string;  // Optional if you plan to derive it from the username or ID
 }

Then update the mock data (lines 17-26) and all references to participant.name throughout the file to use participant.username.

📝 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
interface Participant {
id: number;
name: string;
avatar: string;
}
interface Participant {
id: string;
username: string;
elo: number;
avatar?: string; // Optional if you plan to derive it from the username or ID
}
🤖 Prompt for AI Agents
In frontend/src/Pages/TournamentBracketPage.tsx around lines 4 to 8 (and update
mock data at lines 17 to 26), the Participant interface must match
backend/routes/rooms.go: change id: number to id: string, rename name to
username (string), and replace avatar with elo: number; then update the mock
participant objects at lines 17-26 to use id as strings, username properties,
and an elo number, and replace all usages of participant.name in this file with
participant.username so types and runtime behavior align with the backend.

Comment on lines +62 to +64
const round1Winners = [participants[0], participants[2], participants[4], participants[7]];
const semiFinalWinners = [round1Winners[0], round1Winners[3]];
const champion = semiFinalWinners[1];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace hard-coded winners with actual match results.

The bracket logic hard-codes winners by selecting specific array indices rather than determining winners based on actual match results. This means the bracket will always show the same winners regardless of the tournament data.

For the simulated data, consider one of these approaches:

  1. Add a winner property to mock data: Include match results in your simulated API response
  2. Add match result state: Allow the UI to accept or simulate match outcomes
  3. Document as placeholder logic: If this is intentional for now, add a TODO comment

Example for approach 1:

   useEffect(() => {
     const timer = setTimeout(() => {
       const data: Participant[] = [
         // ... participant data ...
       ];
+      // Mock match results (in production, this comes from the API)
+      const matchResults = {
+        round1Winners: [0, 2, 4, 7], // indices of winners
+        semiFinalWinners: [0, 7],
+        championId: 7
+      };
       setParticipants(data);
+      setMatchResults(matchResults);
       setIsLoading(false);
     }, 1000);
     return () => clearTimeout(timer);
   }, []);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/src/Pages/TournamentBracketPage.tsx around lines 62-64 the code
currently hard-codes winners using specific participant indices which forces the
same bracket outcome; instead compute winners from the actual match data or
simulated results: update the mock API response to include a winner property on
each match (or add local matchResult state), then derive round1Winners by
mapping the round1 matches to their match.winner (or resolving matchResult for
that match) and similarly compute semiFinalWinners from semi-final matches and
champion from the final match winner; if this is intentionally temporary,
replace the hard-coded arrays with a clear TODO comment and/or fallback to using
match.winner when present.

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.

1 participant