Leaderboard

This commit is contained in:
2025-08-07 08:05:10 -05:00
parent 2fa4f67021
commit aee75f8d26
18 changed files with 2048 additions and 133 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+26
View File
@@ -0,0 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
+34
View File
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
+19
View File
@@ -0,0 +1,19 @@
import SpeedrunTable, { Speedrun } from "@/components/Table";
export default function Home() {
const users: Speedrun[] = [
{ rank: 1, name: 'Kleo', time: 63, problem: 'Two Sum', date: 1651600541000 },
];
return (
<>
<div className="w-full h-14 bg-neutral-900"></div>
<div className="mt-5 mx-[35rem]">
<div className="w-full">
<SpeedrunTable data={users} />
</div>
</div>
</>
);
}
+49
View File
@@ -0,0 +1,49 @@
import { format, timeAgo } from '@/lib/utils';
import { formatDate } from 'date-fns';
import React from 'react';
export interface Speedrun {
rank: number;
name: string;
time: number;
date: number;
problem: string;
}
interface TableProps {
data: Speedrun[];
}
const SpeedrunTable: React.FC<TableProps> = ({ data }) => {
return (
<div className="overflow-x-auto p-4 bg-[#121212] rounded-lg shadow-inner">
<table className="min-w-full text-sm text-[#e0e0e0]">
<thead>
<tr className="border-b border-[#2a2a2a]">
<th className="px-6 py-3 text-left font-medium tracking-wide text-[#aaaaaa] uppercase w-14">#</th>
<th className="px-6 py-3 text-left font-medium tracking-wide text-[#aaaaaa] uppercase w-52">Player</th>
<th className="px-6 py-3 text-left font-medium tracking-wide text-[#aaaaaa] uppercase w-52">Time</th>
<th className="px-6 py-3 text-left font-medium tracking-wide text-[#aaaaaa] uppercase w-52">Date</th>
<th className="px-6 py-3 text-left font-medium tracking-wide text-[#aaaaaa] uppercase">Problem</th>
</tr>
</thead>
<tbody>
{data.map((user) => (
<tr
key={user.rank}
className="hover:bg-[#1f1f1f] transition-colors border-b border-[#1e1e1e]"
>
<td className="px-6 py-4 whitespace-nowrap">{user.rank}</td>
<td className="px-6 py-4 whitespace-nowrap">{user.name}</td>
<td className="px-6 py-4 whitespace-nowrap">{format(user.time)}</td>
<td className="px-6 py-4 whitespace-nowrap" title={new Date(user.date).toLocaleString()}>{timeAgo(user.date)}</td>
<td className="px-6 py-4 whitespace-nowrap">{user.problem}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default SpeedrunTable;
+36
View File
@@ -0,0 +1,36 @@
export function format(timeSeconds: number): string {
const days = Math.floor(timeSeconds / 86400);
const hours = Math.floor((timeSeconds % 86400) / 3600);
const minutes = Math.floor((timeSeconds % 3600) / 60);
const seconds = Math.floor(timeSeconds % 60);
if (days > 0) {
return `${days}d ${hours}h ${minutes}m ${seconds}s`;
} else if (hours > 0) {
return `${hours}h ${minutes}m ${seconds}s`;
} else if (minutes > 0) {
return `${minutes}m ${seconds}s`;
} else {
return `${seconds}s`;
}
}
export function timeAgo(timestamp: number) {
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const now = Date.now();
const diff = timestamp - now; // positive if in the future
const seconds = Math.round(diff / 1000);
const minutes = Math.round(diff / (1000 * 60));
const hours = Math.round(diff / (1000 * 60 * 60));
const days = Math.round(diff / (1000 * 60 * 60 * 24));
const months = Math.round(diff / (1000 * 60 * 60 * 24 * 30));
const years = Math.round(diff / (1000 * 60 * 60 * 24 * 365));
if (Math.abs(years) >= 1) return rtf.format(years, 'year');
if (Math.abs(months) >= 1) return rtf.format(months, 'month');
if (Math.abs(days) >= 1) return rtf.format(days, 'day');
if (Math.abs(hours) >= 1) return rtf.format(hours, 'hour');
if (Math.abs(minutes) >= 1) return rtf.format(minutes, 'minute');
return rtf.format(seconds, 'second');
}