Skip to content

Conversation

@Ammaar-Alam
Copy link
Member

@Ammaar-Alam Ammaar-Alam commented Oct 31, 2025

Just some frontend updates:

  • Leaderboards show user selected titles
  • Updated "No Recent Race History" text styling
  • Revamped private lobby UI (AI-generated)

Copilot AI review requested due to automatic review settings October 31, 2025 19:31
@Ammaar-Alam Ammaar-Alam merged commit bbea817 into master Oct 31, 2025
4 checks passed
@Ammaar-Alam Ammaar-Alam deleted the feature/UI-polish branch October 31, 2025 19:32
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR enhances the UI/UX of the lobby and leaderboard components with comprehensive styling improvements and adds equipped title badge display functionality to leaderboards. The changes focus on visual polish with gradients, animations, and improved spacing throughout the lobby interface.

  • Extensive visual enhancements to lobby components with gradients, shadows, and hover effects
  • New equipped title badge display on leaderboard entries for authenticated users
  • Improved responsive styling and component layouts across mobile and desktop views

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
client/src/pages/Lobby.css Comprehensive styling overhaul with gradients, transitions, hover effects, and new read-only settings display
client/src/components/TestConfigurator.css Updated subject filter transitions with cubic-bezier easing and adjusted dimensions
client/src/components/ProfileModal.css Added no-matches message styling for empty states
client/src/components/Leaderboard.jsx Added title fetching logic and display of equipped titles on leaderboard entries
client/src/components/Leaderboard.css New styling for title badges and updated leaderboard player layout structure

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

});
}
});
}, [leaderboard, user, authenticated, leaderboardTitlesMap]);
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The useEffect has leaderboardTitlesMap in its dependency array, but the effect also updates leaderboardTitlesMap. This creates an infinite re-render loop. Remove leaderboardTitlesMap from the dependencies array since the effect checks !(netid in leaderboardTitlesMap) which will see the updated state correctly without it being a dependency.

Suggested change
}, [leaderboard, user, authenticated, leaderboardTitlesMap]);
}, [leaderboard, user, authenticated]);

Copilot uses AI. Check for mistakes.
) : (
<p className="no-results">No results found for this leaderboard.</p>
)}
{leaderboard.length > 0 ? ( leaderboard.map((entry, index) => ( <div key={`${entry.user_id}-${entry.created_at}`} className={`leaderboard-item ${user && entry.netid === user.netid ? 'current-user' : ''}`}> <span className="leaderboard-rank">{index + 1}</span> <div className="leaderboard-player"> <div className="leaderboard-avatar" onClick={() => handleAvatarClick(entry.avatar_url, entry.netid)} title={`View ${entry.netid}\'s avatar`}> <img src={entry.avatar_url || defaultProfileImage} alt={`${entry.netid} avatar`} onError={(e) => { e.target.onerror = null; e.target.src=defaultProfileImage; }} /> </div> <div className="leaderboard-player-text"> <span className="leaderboard-netid">{entry.netid}</span> {authenticated && (() => { const titles = leaderboardTitlesMap[entry.netid]; const titleToShow = titles?.find(t => t.is_equipped); return titleToShow ? ( <div className="leaderboard-titles"> <span className="leaderboard-title-badge">{titleToShow.name}</span> </div> ) : null; })()} </div> </div> <div className="leaderboard-stats"> <span className="leaderboard-wpm">{parseFloat(entry.adjusted_wpm).toFixed(0)} WPM</span> <span className="leaderboard-accuracy">{parseFloat(entry.accuracy).toFixed(1)}%</span> <span className="leaderboard-date">{period === 'daily' ? formatRelativeTime(entry.created_at) : new Date(entry.created_at).toLocaleDateString()}</span> </div> </div> )) ) : ( <p className="no-results">No results found for this leaderboard.</p> )}
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

This entire leaderboard rendering logic is condensed into a single line, making it extremely difficult to read and maintain. The JSX should be properly formatted across multiple lines with appropriate indentation.

Suggested change
{leaderboard.length > 0 ? ( leaderboard.map((entry, index) => ( <div key={`${entry.user_id}-${entry.created_at}`} className={`leaderboard-item ${user && entry.netid === user.netid ? 'current-user' : ''}`}> <span className="leaderboard-rank">{index + 1}</span> <div className="leaderboard-player"> <div className="leaderboard-avatar" onClick={() => handleAvatarClick(entry.avatar_url, entry.netid)} title={`View ${entry.netid}\'s avatar`}> <img src={entry.avatar_url || defaultProfileImage} alt={`${entry.netid} avatar`} onError={(e) => { e.target.onerror = null; e.target.src=defaultProfileImage; }} /> </div> <div className="leaderboard-player-text"> <span className="leaderboard-netid">{entry.netid}</span> {authenticated && (() => { const titles = leaderboardTitlesMap[entry.netid]; const titleToShow = titles?.find(t => t.is_equipped); return titleToShow ? ( <div className="leaderboard-titles"> <span className="leaderboard-title-badge">{titleToShow.name}</span> </div> ) : null; })()} </div> </div> <div className="leaderboard-stats"> <span className="leaderboard-wpm">{parseFloat(entry.adjusted_wpm).toFixed(0)} WPM</span> <span className="leaderboard-accuracy">{parseFloat(entry.accuracy).toFixed(1)}%</span> <span className="leaderboard-date">{period === 'daily' ? formatRelativeTime(entry.created_at) : new Date(entry.created_at).toLocaleDateString()}</span> </div> </div> )) ) : ( <p className="no-results">No results found for this leaderboard.</p> )}
{leaderboard.length > 0 ? (
leaderboard.map((entry, index) => (
<div
key={`${entry.user_id}-${entry.created_at}`}
className={`leaderboard-item ${user && entry.netid === user.netid ? 'current-user' : ''}`}
>
<span className="leaderboard-rank">{index + 1}</span>
<div className="leaderboard-player">
<div
className="leaderboard-avatar"
onClick={() => handleAvatarClick(entry.avatar_url, entry.netid)}
title={`View ${entry.netid}'s avatar`}
>
<img
src={entry.avatar_url || defaultProfileImage}
alt={`${entry.netid} avatar`}
onError={(e) => {
e.target.onerror = null;
e.target.src = defaultProfileImage;
}}
/>
</div>
<div className="leaderboard-player-text">
<span className="leaderboard-netid">{entry.netid}</span>
{authenticated && (() => {
const titles = leaderboardTitlesMap[entry.netid];
const titleToShow = titles?.find(t => t.is_equipped);
return titleToShow ? (
<div className="leaderboard-titles">
<span className="leaderboard-title-badge">{titleToShow.name}</span>
</div>
) : null;
})()}
</div>
</div>
<div className="leaderboard-stats">
<span className="leaderboard-wpm">
{parseFloat(entry.adjusted_wpm).toFixed(0)} WPM
</span>
<span className="leaderboard-accuracy">
{parseFloat(entry.accuracy).toFixed(1)}%
</span>
<span className="leaderboard-date">
{period === 'daily'
? formatRelativeTime(entry.created_at)
: new Date(entry.created_at).toLocaleDateString()}
</span>
</div>
</div>
))
) : (
<p className="no-results">No results found for this leaderboard.</p>
)}

Copilot uses AI. Check for mistakes.
<div
className={`leaderboard-avatar ${!authenticated ? 'disabled' : ''}`}
onClick={authenticated ? () => handleAvatarClick(entry.avatar_url, entry.netid) : undefined}
title={authenticated ? `View ${entry.netid}\'s profile` : 'Log in to view profiles'}
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

Corrected escaped apostrophe from \'s to 's (use proper quote character instead of escape sequence).

Suggested change
title={authenticated ? `View ${entry.netid}\'s profile` : 'Log in to view profiles'}
title={authenticated ? `View ${entry.netid}'s profile` : 'Log in to view profiles'}

Copilot uses AI. Check for mistakes.
Comment on lines +363 to +365
onClick={authenticated ? () => handleAvatarClick(entry.avatar_url, entry.netid) : undefined}
title={authenticated ? `View ${entry.netid}\'s profile` : 'Log in to view profiles'}
>
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The avatar element is clickable but lacks proper accessibility attributes. Add role=\"button\", tabIndex={authenticated ? 0 : -1}, and keyboard event handlers for Enter/Space keys to ensure keyboard navigation support, similar to the code that was removed from the previous implementation.

Suggested change
onClick={authenticated ? () => handleAvatarClick(entry.avatar_url, entry.netid) : undefined}
title={authenticated ? `View ${entry.netid}\'s profile` : 'Log in to view profiles'}
>
role="button"
tabIndex={authenticated ? 0 : -1}
onClick={authenticated ? () => handleAvatarClick(entry.avatar_url, entry.netid) : undefined}
onKeyDown={authenticated ? (e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAvatarClick(entry.avatar_url, entry.netid);
}
}) : undefined}
title={authenticated ? `View ${entry.netid}\'s profile` : 'Log in to view profiles'}

Copilot uses AI. Check for mistakes.
Ammaar-Alam added a commit that referenced this pull request Dec 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants