Skip to content
Merged
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
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 19 additions & 9 deletions packages/frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# Database (PostgreSQL - Railway, Neon, Vercel Postgres, etc.)
DATABASE_URL=postgres://user:password@host:5432/database
# Database (PostgreSQL)
# For local development, you can use a local PostgreSQL instance or a free cloud provider:
# - Local: postgresql://user:password@localhost:5432/tokscale
# - Railway: postgresql://...@...railway.app:5432/railway
# - Neon: postgresql://...@...neon.tech/tokscale
# - Vercel Postgres: postgres://...@...vercel-storage.com/verceldb
DATABASE_URL=postgresql://user:password@localhost:5432/tokscale

# GitHub OAuth
# Create an OAuth App at: https://github.com/settings/developers
# Authorization callback URL: https://your-domain.com/api/auth/github/callback
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
# GitHub OAuth (for authentication)
# Create a GitHub OAuth App at: https://github.com/settings/developers
# - Homepage URL: http://localhost:3000
# - Authorization callback URL: http://localhost:3000/api/auth/github/callback
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret

# Application URL (no trailing slash)
# Used for OAuth redirects and generating absolute URLs
# Public URL (used for OAuth redirects)
# For local development: http://localhost:3000
# For production: https://your-domain.com
NEXT_PUBLIC_URL=http://localhost:3000

# Optional: Session secret (auto-generated if not set)
# AUTH_SECRET=your_random_secret_string
1 change: 1 addition & 0 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@emotion/styled": "^11.14.1",
"@junhoyeo/blackhole": "^1.0.1",
"@neondatabase/serverless": "^0.10.4",
"@primer/octicons-react": "^19.21.1",
"@primer/primitives": "^11.3.1",
"@primer/react": "^38.3.0",
"date-fns": "^4.1.0",
Expand Down
82 changes: 41 additions & 41 deletions packages/frontend/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,62 +62,62 @@ export default function LeaderboardPage() {
}, [period, page]);

return (
<div className="min-h-screen flex flex-col" style={{ backgroundColor: "#141415" }}>
<div className="min-h-screen flex flex-col" style={{ backgroundColor: "var(--color-bg-default)" }}>
<Navigation />

<main className="flex-1 max-w-7xl mx-auto px-6 py-10 w-full">
<BlackholeHero />

<div className="mb-10">
<h1 className="text-3xl font-bold mb-2" style={{ color: "#FFFFFF" }}>
<h1 className="text-3xl font-bold mb-2" style={{ color: "var(--color-fg-default)" }}>
Leaderboard
</h1>
<p className="mb-6" style={{ color: "#696969" }}>
<p className="mb-6" style={{ color: "var(--color-fg-muted)" }}>
See who&apos;s using the most AI tokens
</p>

<div className="grid grid-cols-2 md:grid-cols-4 gap-3 sm:gap-4">
<div
className="rounded-xl border p-3 sm:p-4"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
<p className="text-xs sm:text-sm" style={{ color: "#696969" }}>
<p className="text-xs sm:text-sm" style={{ color: "var(--color-fg-muted)" }}>
Total Tokens
</p>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "#FFFFFF" }}>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "var(--color-fg-default)" }}>
{data ? formatNumber(data.stats.totalTokens) : "-"}
</p>
</div>
<div
className="rounded-xl border p-3 sm:p-4"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
<p className="text-xs sm:text-sm" style={{ color: "#696969" }}>
<p className="text-xs sm:text-sm" style={{ color: "var(--color-fg-muted)" }}>
Total Cost
</p>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "#53d1f3" }}>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "var(--color-primary)" }}>
{data ? formatCurrency(data.stats.totalCost) : "-"}
</p>
</div>
<div
className="rounded-xl border p-3 sm:p-4"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
<p className="text-xs sm:text-sm" style={{ color: "#696969" }}>
<p className="text-xs sm:text-sm" style={{ color: "var(--color-fg-muted)" }}>
Users
</p>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "#FFFFFF" }}>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "var(--color-fg-default)" }}>
{data ? data.stats.uniqueUsers : "-"}
</p>
</div>
<div
className="rounded-xl border p-3 sm:p-4"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
<p className="text-xs sm:text-sm" style={{ color: "#696969" }}>
<p className="text-xs sm:text-sm" style={{ color: "var(--color-fg-muted)" }}>
Submissions
</p>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "#FFFFFF" }}>
<p className="text-xl sm:text-2xl font-bold" style={{ color: "var(--color-fg-default)" }}>
{data ? data.stats.totalSubmissions : "-"}
</p>
</div>
Expand All @@ -144,18 +144,18 @@ export default function LeaderboardPage() {
) : (
<div
className="rounded-2xl border overflow-hidden"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
{!data || data.users.length === 0 ? (
<div className="p-8 text-center">
<p className="mb-4" style={{ color: "#696969" }}>
<p className="mb-4" style={{ color: "var(--color-fg-muted)" }}>
No submissions yet. Be the first!
</p>
<p className="text-sm" style={{ color: "#525252" }}>
<p className="text-sm" style={{ color: "var(--color-fg-subtle)" }}>
Run{" "}
<code
className="px-2 py-1 rounded"
style={{ backgroundColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-subtle)" }}
>
tokscale login && tokscale submit
</code>
Expand All @@ -167,36 +167,36 @@ export default function LeaderboardPage() {
<table className="w-full min-w-[500px]">
<thead
className="border-b"
style={{ backgroundColor: "#1F1F20", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-elevated)", borderColor: "var(--color-border-default)" }}
>
<tr>
<th
className="px-3 sm:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
Rank
</th>
<th
className="px-3 sm:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
User
</th>
<th
className="px-3 sm:px-6 py-3 text-right text-xs font-medium uppercase tracking-wider"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
Tokens
</th>
<th
className="px-3 sm:px-6 py-3 text-right text-xs font-medium uppercase tracking-wider"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
Cost
</th>
<th
className="px-3 sm:px-6 py-3 text-right text-xs font-medium uppercase tracking-wider hidden md:table-cell"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
Submissions
</th>
Expand All @@ -208,7 +208,7 @@ export default function LeaderboardPage() {
key={user.userId}
className="transition-colors hover:opacity-80"
style={{
borderBottom: index < data.users.length - 1 ? "1px solid #262627" : "none",
borderBottom: index < data.users.length - 1 ? "1px solid var(--color-border-default)" : "none",
}}
>
<td className="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap">
Expand All @@ -222,7 +222,7 @@ export default function LeaderboardPage() {
? "#9CA3AF"
: user.rank === 3
? "#D97706"
: "#696969",
: "var(--color-fg-muted)",
}}
>
#{user.rank}
Expand All @@ -241,13 +241,13 @@ export default function LeaderboardPage() {
<div className="min-w-0">
<p
className="font-medium text-sm sm:text-base truncate max-w-[120px] sm:max-w-none group-hover:opacity-80 transition-opacity"
style={{ color: "#FFFFFF" }}
style={{ color: "var(--color-fg-default)" }}
>
{user.displayName || user.username}
</p>
<p
className="text-xs sm:text-sm truncate max-w-[120px] sm:max-w-none"
style={{ color: "#696969" }}
style={{ color: "var(--color-fg-muted)" }}
>
@{user.username}
</p>
Expand All @@ -257,21 +257,21 @@ export default function LeaderboardPage() {
<td className="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap text-right">
<span
className="font-medium text-sm sm:text-base"
style={{ color: "#FFFFFF" }}
style={{ color: "var(--color-fg-default)" }}
>
{formatNumber(user.totalTokens)}
</span>
</td>
<td className="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap text-right">
<span
className="font-medium text-sm sm:text-base"
style={{ color: "#53d1f3" }}
style={{ color: "var(--color-primary)" }}
>
{formatCurrency(user.totalCost)}
</span>
</td>
<td className="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap text-right hidden md:table-cell">
<span style={{ color: "#696969" }}>{user.submissionCount}</span>
<span style={{ color: "var(--color-fg-muted)" }}>{user.submissionCount}</span>
</td>
</tr>
))}
Expand All @@ -282,9 +282,9 @@ export default function LeaderboardPage() {
{data.pagination.totalPages > 1 && (
<div
className="px-3 sm:px-6 py-3 sm:py-4 border-t flex flex-col sm:flex-row items-center justify-between gap-3"
style={{ borderColor: "#262627" }}
style={{ borderColor: "var(--color-border-default)" }}
>
<p className="text-xs sm:text-sm text-center sm:text-left" style={{ color: "#696969" }}>
<p className="text-xs sm:text-sm text-center sm:text-left" style={{ color: "var(--color-fg-muted)" }}>
Showing {(data.pagination.page - 1) * data.pagination.limit + 1}-
{Math.min(data.pagination.page * data.pagination.limit, data.pagination.totalUsers)} of{" "}
{data.pagination.totalUsers}
Expand All @@ -304,20 +304,20 @@ export default function LeaderboardPage() {

<div
className="mt-8 p-6 rounded-2xl border"
style={{ backgroundColor: "#141415", borderColor: "#262627" }}
style={{ backgroundColor: "var(--color-bg-default)", borderColor: "var(--color-border-default)" }}
>
<h2 className="text-lg font-semibold mb-3" style={{ color: "#FFFFFF" }}>
<h2 className="text-lg font-semibold mb-3" style={{ color: "var(--color-fg-default)" }}>
Join the Leaderboard
</h2>
<p className="mb-4" style={{ color: "#9CA3AF" }}>
<p className="mb-4" style={{ color: "var(--color-fg-muted)" }}>
Install Tokscale CLI and submit your usage data:
</p>
<div className="space-y-2 font-mono text-sm">
<div className="p-3 rounded-lg" style={{ backgroundColor: "#1F1F20", color: "#9CA3AF" }}>
<span style={{ color: "#53d1f3" }}>$</span> npx tokscale login
<div className="p-3 rounded-lg" style={{ backgroundColor: "var(--color-bg-elevated)", color: "var(--color-fg-muted)" }}>
<span style={{ color: "var(--color-primary)" }}>$</span> npx tokscale login
</div>
<div className="p-3 rounded-lg" style={{ backgroundColor: "#1F1F20", color: "#9CA3AF" }}>
<span style={{ color: "#53d1f3" }}>$</span> npx tokscale submit
<div className="p-3 rounded-lg" style={{ backgroundColor: "var(--color-bg-elevated)", color: "var(--color-fg-muted)" }}>
<span style={{ color: "var(--color-primary)" }}>$</span> npx tokscale submit
</div>
</div>
</div>
Expand Down
Loading