Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default async function Login(props: {
return (
<div className="min-h-screen flex bg-[#0a0a1a] text-white">
{/* Left Side - Visual / Branding */}
<div className="hidden lg:flex lg:w-1/2 relative flex-col justify-between p-12 md:p-16 xl:p-24 border-r border-white/5 bg-gradient-to-br from-[#0a0a1a] to-[#0a0a1a] overflow-hidden">
<div className="hidden lg:flex lg:w-1/2 relative flex-col justify-between p-12 md:p-16 xl:p-24 border-r border-white/5 bg-linear-to-br from-[#0a0a1a] to-[#0a0a1a] overflow-hidden">
{/* Background elements */}
<div className="absolute inset-0 grid-bg opacity-30" />

Expand All @@ -33,7 +33,7 @@ export default async function Login(props: {
</div>

<div className="relative z-10 max-w-md">
<h1 className="text-4xl font-extrabold mb-5 leading-tight text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-400">
<h1 className="text-4xl font-extrabold mb-5 leading-tight text-transparent bg-clip-text bg-linear-to-r from-white to-gray-400">
Welcome back to your dashboard.
</h1>
<p className="text-gray-400 text-lg leading-relaxed mb-8">
Expand Down
3 changes: 2 additions & 1 deletion app/(public)/leaderboard/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LeaderboardHeader from "@/app/components/leaderboard/Header";
import BackButton from "@/app/components/leaderboard/BackButton";
import Footer from "@/app/components/layout/Footer";
import CTA from "@/app/components/layout/CTA";
import { Member } from "@/app/types/LeaderboardMember";

export default async function LeaderboardPage(props: {
params: Promise<{ slug: string }>;
Expand All @@ -28,7 +29,7 @@ export default async function LeaderboardPage(props: {
);
}

const { data: members, error } = await supabase
const { data: members, error }: { data: Member[] | null; error: any } = await supabase
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Typing the Supabase response as { error: any } loses useful type safety and can hide real handling issues. Prefer using PostgrestError | null (from @supabase/supabase-js) or unknown and narrowing, so error handling remains strongly typed.

Suggested change
const { data: members, error }: { data: Member[] | null; error: any } = await supabase
const { data: members, error } = await supabase

Copilot uses AI. Check for mistakes.
.from("leaderboard_members_view")
.select("*")
.eq("leaderboard_id", leaderboard.id);
Expand Down
37 changes: 15 additions & 22 deletions app/components/LeaderboardTable.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClock, faCode, faTerminal, faServer, faBolt, faFire, faStar, faRocket } from "@fortawesome/free-solid-svg-icons";
import { Member } from "@/app/types/LeaderboardMember";

type Member = {
user_id: string;
role: string;
email: string;
total_seconds: number;
languages: { name: string }[] | null;
operating_systems: { name: string }[] | null;
editors: { name: string }[] | null;
};

function LeaderboardStats({ members }: { members: Member[] }) {
const totalHours = Math.round(
Expand Down Expand Up @@ -114,6 +106,7 @@ export default function LeaderboardTable({
.map((member, index) => ({
user_id: member.user_id,
rank: index + 1,
name: member.name,
email: member.email,
hours: Math.round((member.total_seconds || 0) / 3600),
role: member.role,
Expand Down Expand Up @@ -200,7 +193,7 @@ export default function LeaderboardTable({
) : (
<div className="glass-card overflow-hidden">
{/* Header Row (Desktop) */}
<div className="hidden md:flex items-center px-4 sm:px-6 py-4 border-b border-white/5 bg-white/[0.01] text-[10px] font-bold text-gray-500 uppercase tracking-widest">
<div className="hidden md:flex items-center px-4 sm:px-6 py-4 border-b border-white/5 bg-white/1 text-[10px] font-bold text-gray-500 uppercase tracking-widest">
<div className="w-12 shrink-0 text-center">Rank</div>
<div className="flex-1 ml-4">Developer</div>
<div className="w-48 lg:w-72 xl:w-80">Tech Stack</div>
Expand All @@ -219,16 +212,16 @@ export default function LeaderboardTable({
<div
key={user.user_id}
className={`group relative flex flex-col md:flex-row items-start md:items-center px-4 sm:px-6 py-4 md:py-4 transition-colors hover:bg-white/[0.02] ${
isCurrentUser ? "bg-indigo-500/[0.02]" : "bg-transparent"
isCurrentUser ? "bg-indigo-500/2" : "bg-transparent"
}`}
>
{/* Background Progress Bar */}
<div
className="absolute left-0 bottom-0 h-[1px] bg-gradient-to-r from-indigo-500/50 to-transparent"
className="absolute left-0 bottom-0 h-px bg-linear-to-r from-indigo-500/50 to-transparent"
style={{ width: `${pct}%` }}
/>
{isCurrentUser && (
<div className="absolute left-0 top-0 bottom-0 w-[2px] bg-indigo-500" />
<div className="absolute left-0 top-0 bottom-0 w-0.5 bg-indigo-500" />
)}

{/* MOBILE TOP ROW / DESKTOP LEFT FLEX */}
Expand All @@ -242,13 +235,13 @@ export default function LeaderboardTable({

{/* Profile + Badges */}
<div className="flex-1 ml-3 sm:ml-4 min-w-0 flex items-center gap-3">
<div className="w-8 h-8 sm:w-10 sm:h-10 shrink-0 rounded-lg bg-gradient-to-br from-white/5 to-white/10 border border-white/10 flex items-center justify-center text-[10px] sm:text-sm font-semibold text-gray-300 shadow-sm uppercase">
{user.email.charAt(0)}
<div className="w-8 h-8 sm:w-10 sm:h-10 shrink-0 rounded-lg bg-linear-to-br from-white/5 to-white/10 border border-white/10 flex items-center justify-center text-[10px] sm:text-sm font-semibold text-gray-300 shadow-sm uppercase">
{user.name ? user.name : user.email.charAt(0)}
</div>
<div className="flex flex-col min-w-0 gap-1 sm:gap-1.5">
<div className="flex items-center gap-2">
<p className="font-semibold text-gray-200 tracking-tight text-sm sm:text-[15px] truncate max-w-[120px] xs:max-w-[160px] sm:max-w-[180px] lg:max-w-[200px] leading-none">
{user.email.split("@")[0]}
<p className="font-semibold text-gray-200 tracking-tight text-sm sm:text-[15px] truncate max-w-30 xs:max-w-[160px] sm:max-w-45 lg:max-w-50 leading-none">
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Tailwind utility classes like max-w-30, sm:max-w-45, and lg:max-w-50 are not part of Tailwind’s default maxWidth/spacing scales, and there’s no custom spacing/theme tokens defined in app/globals.css to support them. These will likely compile to no CSS and break truncation/layout. Consider reverting to the previous arbitrary values (e.g. max-w-[120px]) or add explicit theme tokens for these sizes.

Suggested change
<p className="font-semibold text-gray-200 tracking-tight text-sm sm:text-[15px] truncate max-w-30 xs:max-w-[160px] sm:max-w-45 lg:max-w-50 leading-none">
<p className="font-semibold text-gray-200 tracking-tight text-sm sm:text-[15px] truncate max-w-[120px] xs:max-w-[160px] sm:max-w-[180px] lg:max-w-[200px] leading-none">

Copilot uses AI. Check for mistakes.
{user.name ?? user.email.split("@")[0]}
</p>
{isCurrentUser && (
<span className="px-1.5 py-0.5 rounded border border-indigo-500/30 bg-indigo-500/10 text-indigo-400 text-[8px] sm:text-[9px] uppercase font-bold tracking-widest leading-none">
Expand All @@ -275,12 +268,12 @@ export default function LeaderboardTable({
</div>

{/* MOBILE BOTTOM STACK / DESKTOP RIGHT ROW */}
<div className="flex flex-col md:flex-row items-start md:items-center w-full md:w-auto mt-4 md:mt-0 pl-[2.75rem] sm:pl-[4.25rem] md:pl-0 gap-2.5 md:gap-0">
<div className="flex flex-col md:flex-row items-start md:items-center w-full md:w-auto mt-4 md:mt-0 pl-11 sm:pl-17 md:pl-0 gap-2.5 md:gap-0">
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

sm:pl-17 is not a default Tailwind spacing step (and no custom spacing tokens are defined), so this padding likely won’t be applied. The previous sm:pl-[4.25rem] was explicit and reliable; consider using an arbitrary value again or define a spacing token for 4.25rem.

Suggested change
<div className="flex flex-col md:flex-row items-start md:items-center w-full md:w-auto mt-4 md:mt-0 pl-11 sm:pl-17 md:pl-0 gap-2.5 md:gap-0">
<div className="flex flex-col md:flex-row items-start md:items-center w-full md:w-auto mt-4 md:mt-0 pl-11 sm:pl-[4.25rem] md:pl-0 gap-2.5 md:gap-0">

Copilot uses AI. Check for mistakes.
{/* Tech Stack */}
<div className="flex flex-wrap items-center gap-1.5 w-full md:w-48 lg:w-72 xl:w-80 md:shrink-0 md:pr-4">
{user.languages.length > 0 ? (
user.languages.map((lang, i) => (
<span key={i} className="px-1.5 sm:px-2 py-0.5 sm:py-1 rounded bg-white/[0.03] border border-white/5 text-[9px] sm:text-[10px] text-gray-300 font-medium tracking-wide truncate max-w-[70px] sm:max-w-[80px]">
<span key={i} className="px-1.5 sm:px-2 py-0.5 sm:py-1 rounded bg-white/3 border border-white/5 text-[9px] sm:text-[10px] text-gray-300 font-medium tracking-wide truncate max-w-[70px] sm:max-w-[80px]">
{lang}
</span>
))
Expand All @@ -292,15 +285,15 @@ export default function LeaderboardTable({
{/* Environment */}
<div className="flex items-center gap-1.5 sm:gap-2 w-full md:w-32 lg:w-48 md:shrink-0">
{user.editor !== "N/A" && (
<span className="text-[10px] sm:text-[11px] text-gray-400 font-medium truncate max-w-[70px] lg:max-w-[90px]">
<span className="text-[10px] sm:text-[11px] text-gray-400 font-medium truncate max-w-17.5 lg:max-w-22.5">
{user.editor}
</span>
)}
{user.editor !== "N/A" && user.os !== "N/A" && (
<span className="w-[3px] h-[3px] rounded-full bg-gray-600 shrink-0"></span>
<span className="w-0.75 h-0.75 rounded-full bg-gray-600 shrink-0"></span>
)}
Comment on lines 287 to 294
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Utilities like max-w-17.5, lg:max-w-22.5, and w-0.75 are not part of Tailwind’s default scales. Without custom theme tokens, these will compile to no CSS, impacting truncation and the separator dot size. Use arbitrary values (e.g. max-w-[70px], w-[3px]) or add theme tokens that define these steps.

Copilot uses AI. Check for mistakes.
{user.os !== "N/A" && (
<span className="text-[10px] sm:text-[11px] text-gray-400 font-medium truncate max-w-[70px] lg:max-w-[90px]">
<span className="text-[10px] sm:text-[11px] text-gray-400 font-medium truncate max-w-17.5 lg:max-w-22.5">
{user.os}
</span>
)}
Expand Down
2 changes: 1 addition & 1 deletion app/components/leaderboard/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default function LeaderboardHeader({
/>

<textarea
className="input-field mb-4 min-h-[80px] resize-none"
className="input-field mb-4 min-h-20 resize-none"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Description (optional)"
Expand Down
10 changes: 10 additions & 0 deletions app/types/LeaderboardMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface Member {
user_id: string;
role: string;
name: string | null;
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Member includes a name field, but the leaderboard_members_view SQL definition (sql/public.leaderboards.sql) currently only selects u.email and does not project any name. This makes the type misleading and will mask missing data at compile time. Either update the view/query to return a name (e.g., from user metadata) or make name optional/remove it from the interface.

Suggested change
name: string | null;
name?: string | null;

Copilot uses AI. Check for mistakes.
email: string;
total_seconds: number;
languages: { name: string }[] | null;
operating_systems: { name: string }[] | null;
editors: { name: string }[] | null;
};
Loading
Loading