Removed unnecessary components
This commit is contained in:
+290
-143
@@ -5,154 +5,301 @@ import LeaderboardRow from "@/components/app/LeaderboardRow";
|
||||
import { useState } from "react";
|
||||
|
||||
const Leaderboards = () => {
|
||||
const [activeTab, setActiveTab] = useState<"global" | "weekly">("global");
|
||||
const [activeTab, setActiveTab] = useState<"global" | "weekly">("global");
|
||||
|
||||
const globalLeaderboard = [
|
||||
{ rank: 1, username: "speedmaster_x", pb: "2:34.21", runsCount: 2341, category: "Any%" },
|
||||
{ rank: 2, username: "algo_ninja", pb: "2:38.54", runsCount: 1876, category: "Any%" },
|
||||
{ rank: 3, username: "clockwork", pb: "2:41.02", runsCount: 3102, category: "Any%" },
|
||||
{ rank: 4, username: "fastfingers99", pb: "2:45.33", runsCount: 987, category: "Any%" },
|
||||
{ rank: 5, username: "recursion_queen", pb: "2:47.89", runsCount: 1543, category: "Any%" },
|
||||
{ rank: 6, username: "bitshifter", pb: "2:52.11", runsCount: 2109, category: "Any%" },
|
||||
{ rank: 7, username: "nullpointer", pb: "2:54.67", runsCount: 876, category: "Any%" },
|
||||
{ rank: 8, username: "stackattack", pb: "2:58.23", runsCount: 1234, category: "Any%" },
|
||||
{ rank: 9, username: "looplegend", pb: "3:01.45", runsCount: 654, category: "Any%" },
|
||||
{ rank: 10, username: "coderunner", pb: "3:05.78", runsCount: 1098, category: "Any%" },
|
||||
];
|
||||
const globalLeaderboard = [
|
||||
{
|
||||
rank: 1,
|
||||
username: "speedmaster_x",
|
||||
pb: "2:34.21",
|
||||
runsCount: 2341,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 2,
|
||||
username: "algo_ninja",
|
||||
pb: "2:38.54",
|
||||
runsCount: 1876,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 3,
|
||||
username: "clockwork",
|
||||
pb: "2:41.02",
|
||||
runsCount: 3102,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 4,
|
||||
username: "fastfingers99",
|
||||
pb: "2:45.33",
|
||||
runsCount: 987,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 5,
|
||||
username: "recursion_queen",
|
||||
pb: "2:47.89",
|
||||
runsCount: 1543,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 6,
|
||||
username: "bitshifter",
|
||||
pb: "2:52.11",
|
||||
runsCount: 2109,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 7,
|
||||
username: "nullpointer",
|
||||
pb: "2:54.67",
|
||||
runsCount: 876,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 8,
|
||||
username: "stackattack",
|
||||
pb: "2:58.23",
|
||||
runsCount: 1234,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 9,
|
||||
username: "looplegend",
|
||||
pb: "3:01.45",
|
||||
runsCount: 654,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 10,
|
||||
username: "coderunner",
|
||||
pb: "3:05.78",
|
||||
runsCount: 1098,
|
||||
category: "Any%",
|
||||
},
|
||||
];
|
||||
|
||||
const weeklyLeaderboard = [
|
||||
{ rank: 1, username: "weekly_warrior", pb: "2:51.33", runsCount: 156, category: "Any%" },
|
||||
{ rank: 2, username: "speedmaster_x", pb: "2:53.12", runsCount: 89, category: "Any%" },
|
||||
{ rank: 3, username: "newbie_grinder", pb: "2:58.67", runsCount: 234, category: "Any%" },
|
||||
{ rank: 4, username: "algo_ninja", pb: "3:02.45", runsCount: 67, category: "Any%" },
|
||||
{ rank: 5, username: "fresh_runner", pb: "3:05.89", runsCount: 45, category: "Any%" },
|
||||
{ rank: 6, username: "practice_makes", pb: "3:08.12", runsCount: 112, category: "Any%" },
|
||||
{ rank: 7, username: "splitmaster", pb: "3:12.34", runsCount: 78, category: "Any%" },
|
||||
{ rank: 8, username: "grindtime", pb: "3:15.56", runsCount: 92, category: "Any%" },
|
||||
{ rank: 9, username: "pb_hunter", pb: "3:18.90", runsCount: 56, category: "Any%" },
|
||||
{ rank: 10, username: "reset_king", pb: "3:22.11", runsCount: 187, category: "Any%" },
|
||||
];
|
||||
const weeklyLeaderboard = [
|
||||
{
|
||||
rank: 1,
|
||||
username: "weekly_warrior",
|
||||
pb: "2:51.33",
|
||||
runsCount: 156,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 2,
|
||||
username: "speedmaster_x",
|
||||
pb: "2:53.12",
|
||||
runsCount: 89,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 3,
|
||||
username: "newbie_grinder",
|
||||
pb: "2:58.67",
|
||||
runsCount: 234,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 4,
|
||||
username: "algo_ninja",
|
||||
pb: "3:02.45",
|
||||
runsCount: 67,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 5,
|
||||
username: "fresh_runner",
|
||||
pb: "3:05.89",
|
||||
runsCount: 45,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 6,
|
||||
username: "practice_makes",
|
||||
pb: "3:08.12",
|
||||
runsCount: 112,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 7,
|
||||
username: "splitmaster",
|
||||
pb: "3:12.34",
|
||||
runsCount: 78,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 8,
|
||||
username: "grindtime",
|
||||
pb: "3:15.56",
|
||||
runsCount: 92,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 9,
|
||||
username: "pb_hunter",
|
||||
pb: "3:18.90",
|
||||
runsCount: 56,
|
||||
category: "Any%",
|
||||
},
|
||||
{
|
||||
rank: 10,
|
||||
username: "reset_king",
|
||||
pb: "3:22.11",
|
||||
runsCount: 187,
|
||||
category: "Any%",
|
||||
},
|
||||
];
|
||||
|
||||
const categories = ["Any%", "100%", "Easy Only", "No DP", "Blind"];
|
||||
const categories = ["Any%", "100%", "Easy Only", "No DP", "Blind"];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Header */}
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-bold text-foreground mb-2">Leaderboards</h1>
|
||||
<p className="text-muted-foreground">The fastest runners in the game. Will you claim a spot?</p>
|
||||
</div>
|
||||
|
||||
{/* Stats Row */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Globe className="h-5 w-5 text-primary mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">12,453</p>
|
||||
<p className="text-xs text-muted-foreground">Total Runners</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Trophy className="h-5 w-5 text-gold mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">2:34.21</p>
|
||||
<p className="text-xs text-muted-foreground">World Record</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<TrendingUp className="h-5 w-5 text-emerald-400 mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">847K</p>
|
||||
<p className="text-xs text-muted-foreground">Runs This Week</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Calendar className="h-5 w-5 text-primary mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">5d 12h</p>
|
||||
<p className="text-xs text-muted-foreground">Until Reset</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs and Filters */}
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6">
|
||||
{/* Global/Weekly Toggle */}
|
||||
<div className="flex gap-1 bg-card border border-border rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setActiveTab("global")}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all ${activeTab === "global"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Globe className="h-4 w-4" />
|
||||
Global
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("weekly")}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all ${activeTab === "weekly"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Calendar className="h-4 w-4" />
|
||||
Weekly
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{categories.map((cat, i) => (
|
||||
<button
|
||||
key={cat}
|
||||
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${i === 0
|
||||
? "bg-primary/10 text-primary border border-primary/30"
|
||||
: "bg-card text-muted-foreground hover:text-foreground border border-border hover:border-primary/30"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Leaderboard Table */}
|
||||
<div className="bg-card border border-border rounded-xl overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-secondary/50">
|
||||
<tr>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">Rank</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">Runner</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">PB</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">Runs</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">Category</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(activeTab === "global" ? globalLeaderboard : weeklyLeaderboard).map((entry, i) => (
|
||||
<LeaderboardRow key={entry.username} {...entry} index={i} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<div className="flex items-center justify-center gap-2 mt-6">
|
||||
<button className="px-3 py-1.5 rounded-lg bg-secondary text-muted-foreground hover:text-foreground transition-all">
|
||||
Previous
|
||||
</button>
|
||||
{[1, 2, 3, '...', 50].map((page, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`px-3 py-1.5 rounded-lg transition-all ${page === 1
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-secondary text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
<button className="px-3 py-1.5 rounded-lg bg-secondary text-muted-foreground hover:text-foreground transition-all">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Header */}
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-bold text-foreground mb-2">
|
||||
Leaderboards
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
The fastest runners in the game. Will you claim a spot?
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
{/* Stats Row */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Globe className="h-5 w-5 text-primary mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">
|
||||
12,453
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">Total Runners</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Trophy className="h-5 w-5 text-gold mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">
|
||||
2:34.21
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">World Record</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<TrendingUp className="h-5 w-5 text-emerald-400 mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">847K</p>
|
||||
<p className="text-xs text-muted-foreground">Runs This Week</p>
|
||||
</div>
|
||||
<div className="bg-card border border-border rounded-xl p-4 text-center">
|
||||
<Calendar className="h-5 w-5 text-primary mx-auto mb-2" />
|
||||
<p className="text-2xl font-bold font-mono text-foreground">
|
||||
5d 12h
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">Until Reset</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs and Filters */}
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6">
|
||||
{/* Global/Weekly Toggle */}
|
||||
<div className="flex gap-1 bg-card border border-border rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setActiveTab("global")}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all ${
|
||||
activeTab === "global"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Globe className="h-4 w-4" />
|
||||
Global
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("weekly")}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all ${
|
||||
activeTab === "weekly"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Calendar className="h-4 w-4" />
|
||||
Weekly
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{categories.map((cat, i) => (
|
||||
<button
|
||||
key={cat}
|
||||
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${
|
||||
i === 0
|
||||
? "bg-primary/10 text-primary border border-primary/30"
|
||||
: "bg-card text-muted-foreground hover:text-foreground border border-border hover:border-primary/30"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Leaderboard Table */}
|
||||
<div className="bg-card border border-border rounded-xl overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-secondary/50">
|
||||
<tr>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
Rank
|
||||
</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
Runner
|
||||
</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
PB
|
||||
</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
Runs
|
||||
</th>
|
||||
<th className="py-3 px-4 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
Category
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(activeTab === "global"
|
||||
? globalLeaderboard
|
||||
: weeklyLeaderboard
|
||||
).map((entry, i) => (
|
||||
<LeaderboardRow key={entry.username} {...entry} index={i} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<div className="flex items-center justify-center gap-2 mt-6">
|
||||
<button className="px-3 py-1.5 rounded-lg bg-secondary text-muted-foreground hover:text-foreground transition-all">
|
||||
Previous
|
||||
</button>
|
||||
{[1, 2, 3, "...", 50].map((page, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className={`px-3 py-1.5 rounded-lg transition-all ${
|
||||
page === 1
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-secondary text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
<button className="px-3 py-1.5 rounded-lg bg-secondary text-muted-foreground hover:text-foreground transition-all">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Leaderboards;
|
||||
|
||||
+190
-105
@@ -5,115 +5,200 @@ import ProblemCard from "@/components/app/ProblemCard";
|
||||
import { useState } from "react";
|
||||
|
||||
const Problems = () => {
|
||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
||||
|
||||
const problems = [
|
||||
{ name: "Two Sum Dash", difficulty: "easy" as const, tags: ["arrays", "hash-map"], bestTime: "0:42", attempts: 12453 },
|
||||
{ name: "Binary Sprint", difficulty: "easy" as const, tags: ["binary-search"], bestTime: "0:38", attempts: 8921 },
|
||||
{ name: "Linked List Rush", difficulty: "medium" as const, tags: ["linked-list", "pointers"], bestTime: "1:24", attempts: 6234 },
|
||||
{ name: "Tree Traverse Blitz", difficulty: "medium" as const, tags: ["trees", "recursion"], bestTime: "2:01", attempts: 5102 },
|
||||
{ name: "DP Gauntlet", difficulty: "hard" as const, tags: ["dynamic-programming"], bestTime: "4:32", attempts: 2341 },
|
||||
{ name: "Graph Navigator", difficulty: "hard" as const, tags: ["graphs", "bfs", "dfs"], bestTime: "3:45", attempts: 3456 },
|
||||
{ name: "String Scramble", difficulty: "easy" as const, tags: ["strings", "manipulation"], bestTime: "0:55", attempts: 9876 },
|
||||
{ name: "Stack Overflow", difficulty: "medium" as const, tags: ["stacks", "queues"], bestTime: "1:42", attempts: 4567 },
|
||||
{ name: "Heap Hero", difficulty: "hard" as const, tags: ["heaps", "priority-queue"], bestTime: "3:21", attempts: 1890 },
|
||||
{ name: "Sliding Window Slam", difficulty: "medium" as const, tags: ["sliding-window", "arrays"], bestTime: "1:56", attempts: 5678 },
|
||||
{ name: "Trie Triumph", difficulty: "hard" as const, tags: ["trie", "strings"], bestTime: "4:12", attempts: 1234 },
|
||||
{ name: "Backtrack Blaster", difficulty: "hard" as const, tags: ["backtracking", "recursion"], bestTime: "5:01", attempts: 987 },
|
||||
];
|
||||
const problems = [
|
||||
{
|
||||
name: "Two Sum Dash",
|
||||
difficulty: "easy" as const,
|
||||
tags: ["arrays", "hash-map"],
|
||||
bestTime: "0:42",
|
||||
attempts: 12453,
|
||||
},
|
||||
{
|
||||
name: "Binary Sprint",
|
||||
difficulty: "easy" as const,
|
||||
tags: ["binary-search"],
|
||||
bestTime: "0:38",
|
||||
attempts: 8921,
|
||||
},
|
||||
{
|
||||
name: "Linked List Rush",
|
||||
difficulty: "medium" as const,
|
||||
tags: ["linked-list", "pointers"],
|
||||
bestTime: "1:24",
|
||||
attempts: 6234,
|
||||
},
|
||||
{
|
||||
name: "Tree Traverse Blitz",
|
||||
difficulty: "medium" as const,
|
||||
tags: ["trees", "recursion"],
|
||||
bestTime: "2:01",
|
||||
attempts: 5102,
|
||||
},
|
||||
{
|
||||
name: "DP Gauntlet",
|
||||
difficulty: "hard" as const,
|
||||
tags: ["dynamic-programming"],
|
||||
bestTime: "4:32",
|
||||
attempts: 2341,
|
||||
},
|
||||
{
|
||||
name: "Graph Navigator",
|
||||
difficulty: "hard" as const,
|
||||
tags: ["graphs", "bfs", "dfs"],
|
||||
bestTime: "3:45",
|
||||
attempts: 3456,
|
||||
},
|
||||
{
|
||||
name: "String Scramble",
|
||||
difficulty: "easy" as const,
|
||||
tags: ["strings", "manipulation"],
|
||||
bestTime: "0:55",
|
||||
attempts: 9876,
|
||||
},
|
||||
{
|
||||
name: "Stack Overflow",
|
||||
difficulty: "medium" as const,
|
||||
tags: ["stacks", "queues"],
|
||||
bestTime: "1:42",
|
||||
attempts: 4567,
|
||||
},
|
||||
{
|
||||
name: "Heap Hero",
|
||||
difficulty: "hard" as const,
|
||||
tags: ["heaps", "priority-queue"],
|
||||
bestTime: "3:21",
|
||||
attempts: 1890,
|
||||
},
|
||||
{
|
||||
name: "Sliding Window Slam",
|
||||
difficulty: "medium" as const,
|
||||
tags: ["sliding-window", "arrays"],
|
||||
bestTime: "1:56",
|
||||
attempts: 5678,
|
||||
},
|
||||
{
|
||||
name: "Trie Triumph",
|
||||
difficulty: "hard" as const,
|
||||
tags: ["trie", "strings"],
|
||||
bestTime: "4:12",
|
||||
attempts: 1234,
|
||||
},
|
||||
{
|
||||
name: "Backtrack Blaster",
|
||||
difficulty: "hard" as const,
|
||||
tags: ["backtracking", "recursion"],
|
||||
bestTime: "5:01",
|
||||
attempts: 987,
|
||||
},
|
||||
];
|
||||
|
||||
const categories = ["All", "Arrays", "Trees", "Graphs", "DP", "Strings"];
|
||||
const difficulties = ["All", "Easy", "Medium", "Hard"];
|
||||
const categories = ["All", "Arrays", "Trees", "Graphs", "DP", "Strings"];
|
||||
const difficulties = ["All", "Easy", "Medium", "Hard"];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Header */}
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-bold text-foreground mb-2">Problems</h1>
|
||||
<p className="text-muted-foreground">Choose your battleground. Each run brings you closer to your PB.</p>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-col lg:flex-row gap-4 mb-8">
|
||||
{/* Search */}
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search problems..."
|
||||
className="w-full pl-10 pr-4 py-2.5 bg-card border border-border rounded-lg text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${cat === "All"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-secondary text-muted-foreground hover:text-foreground hover:bg-secondary/80"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Difficulty Filter */}
|
||||
<div className="flex gap-2">
|
||||
{difficulties.map((diff) => (
|
||||
<button
|
||||
key={diff}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all ${diff === "All"
|
||||
? "bg-primary/10 text-primary border border-primary/30"
|
||||
: "bg-card text-muted-foreground hover:text-foreground border border-border hover:border-primary/30"
|
||||
}`}
|
||||
>
|
||||
{diff}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* View Toggle */}
|
||||
<div className="flex gap-1 bg-card border border-border rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setViewMode("grid")}
|
||||
className={`p-2 rounded-md transition-all ${viewMode === "grid" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Grid className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("list")}
|
||||
className={`p-2 rounded-md transition-all ${viewMode === "list" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<List className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Problem Grid */}
|
||||
<div className={`grid gap-4 ${viewMode === "grid"
|
||||
? "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
||||
: "grid-cols-1"
|
||||
}`}>
|
||||
{problems.map((problem, i) => (
|
||||
<ProblemCard key={problem.name} {...problem} index={i} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Load More */}
|
||||
<div className="mt-12 text-center">
|
||||
<button className="px-6 py-3 bg-secondary text-secondary-foreground rounded-lg font-medium hover:bg-secondary/80 transition-all">
|
||||
Load More Problems
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Header */}
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-bold text-foreground mb-2">Problems</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Choose your battleground. Each run brings you closer to your PB.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-col lg:flex-row gap-4 mb-8">
|
||||
{/* Search */}
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search problems..."
|
||||
className="w-full pl-10 pr-4 py-2.5 bg-card border border-border rounded-lg text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
cat === "All"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-secondary text-muted-foreground hover:text-foreground hover:bg-secondary/80"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Difficulty Filter */}
|
||||
<div className="flex gap-2">
|
||||
{difficulties.map((diff) => (
|
||||
<button
|
||||
key={diff}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
diff === "All"
|
||||
? "bg-primary/10 text-primary border border-primary/30"
|
||||
: "bg-card text-muted-foreground hover:text-foreground border border-border hover:border-primary/30"
|
||||
}`}
|
||||
>
|
||||
{diff}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* View Toggle */}
|
||||
<div className="flex gap-1 bg-card border border-border rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setViewMode("grid")}
|
||||
className={`p-2 rounded-md transition-all ${
|
||||
viewMode === "grid"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Grid className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("list")}
|
||||
className={`p-2 rounded-md transition-all ${
|
||||
viewMode === "list"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<List className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Problem Grid */}
|
||||
<div
|
||||
className={`grid gap-4 ${
|
||||
viewMode === "grid"
|
||||
? "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
||||
: "grid-cols-1"
|
||||
}`}
|
||||
>
|
||||
{problems.map((problem, i) => (
|
||||
<ProblemCard key={problem.name} {...problem} index={i} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Load More */}
|
||||
<div className="mt-12 text-center">
|
||||
<button className="px-6 py-3 bg-secondary text-secondary-foreground rounded-lg font-medium hover:bg-secondary/80 transition-all">
|
||||
Load More Problems
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Problems;
|
||||
|
||||
+338
-178
@@ -1,190 +1,350 @@
|
||||
import { Trophy, Timer, Zap, Target, Calendar, TrendingUp, Award } from "lucide-react";
|
||||
import {
|
||||
Trophy,
|
||||
Timer,
|
||||
Zap,
|
||||
Target,
|
||||
Calendar,
|
||||
TrendingUp,
|
||||
Award,
|
||||
} from "lucide-react";
|
||||
import StatCard from "@/components/app/StatCard";
|
||||
|
||||
const Profile = () => {
|
||||
const user = {
|
||||
username: "speedmaster_x",
|
||||
joinDate: "Jan 2024",
|
||||
rank: 1,
|
||||
totalRuns: 2341,
|
||||
pb: "2:34.21",
|
||||
cleanRuns: 892,
|
||||
avgTime: "3:12.45",
|
||||
favoriteCategory: "Any%",
|
||||
};
|
||||
const user = {
|
||||
username: "speedmaster_x",
|
||||
joinDate: "Jan 2024",
|
||||
rank: 1,
|
||||
totalRuns: 2341,
|
||||
pb: "2:34.21",
|
||||
cleanRuns: 892,
|
||||
avgTime: "3:12.45",
|
||||
favoriteCategory: "Any%",
|
||||
};
|
||||
|
||||
const recentRuns = [
|
||||
{ id: 1, category: "Any%", time: "2:38.54", date: "2 hours ago", status: "clean" },
|
||||
{ id: 2, category: "Any%", time: "2:45.12", date: "3 hours ago", status: "clean" },
|
||||
{ id: 3, category: "Easy Only", time: "1:23.45", date: "5 hours ago", status: "clean" },
|
||||
{ id: 4, category: "Any%", time: "--:--", date: "5 hours ago", status: "reset" },
|
||||
{ id: 5, category: "Any%", time: "2:51.33", date: "Yesterday", status: "clean" },
|
||||
{ id: 6, category: "No DP", time: "3:45.67", date: "Yesterday", status: "clean" },
|
||||
];
|
||||
const recentRuns = [
|
||||
{
|
||||
id: 1,
|
||||
category: "Any%",
|
||||
time: "2:38.54",
|
||||
date: "2 hours ago",
|
||||
status: "clean",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category: "Any%",
|
||||
time: "2:45.12",
|
||||
date: "3 hours ago",
|
||||
status: "clean",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category: "Easy Only",
|
||||
time: "1:23.45",
|
||||
date: "5 hours ago",
|
||||
status: "clean",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "Any%",
|
||||
time: "--:--",
|
||||
date: "5 hours ago",
|
||||
status: "reset",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category: "Any%",
|
||||
time: "2:51.33",
|
||||
date: "Yesterday",
|
||||
status: "clean",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
category: "No DP",
|
||||
time: "3:45.67",
|
||||
date: "Yesterday",
|
||||
status: "clean",
|
||||
},
|
||||
];
|
||||
|
||||
const achievements = [
|
||||
{ name: "First Blood", description: "Complete your first run", icon: Zap, unlocked: true },
|
||||
{ name: "Sub-3 Club", description: "Finish a run under 3 minutes", icon: Timer, unlocked: true },
|
||||
{ name: "World Record", description: "Hold a world record", icon: Trophy, unlocked: true },
|
||||
{ name: "Century", description: "Complete 100 runs", icon: Target, unlocked: true },
|
||||
{ name: "Perfectionist", description: "100 clean runs", icon: Award, unlocked: false },
|
||||
{ name: "Marathon", description: "10 hours of running", icon: Calendar, unlocked: true },
|
||||
];
|
||||
const achievements = [
|
||||
{
|
||||
name: "First Blood",
|
||||
description: "Complete your first run",
|
||||
icon: Zap,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
name: "Sub-3 Club",
|
||||
description: "Finish a run under 3 minutes",
|
||||
icon: Timer,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
name: "World Record",
|
||||
description: "Hold a world record",
|
||||
icon: Trophy,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
name: "Century",
|
||||
description: "Complete 100 runs",
|
||||
icon: Target,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
name: "Perfectionist",
|
||||
description: "100 clean runs",
|
||||
icon: Award,
|
||||
unlocked: false,
|
||||
},
|
||||
{
|
||||
name: "Marathon",
|
||||
description: "10 hours of running",
|
||||
icon: Calendar,
|
||||
unlocked: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Profile Header */}
|
||||
<div className="bg-card border border-border rounded-2xl p-8 mb-8 relative overflow-hidden">
|
||||
{/* Background gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary/10 via-transparent to-transparent pointer-events-none" />
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Profile Header */}
|
||||
<div className="bg-card border border-border rounded-2xl p-8 mb-8 relative overflow-hidden">
|
||||
{/* Background gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary/10 via-transparent to-transparent pointer-events-none" />
|
||||
|
||||
<div className="relative flex flex-col md:flex-row items-start md:items-center gap-6">
|
||||
{/* Avatar */}
|
||||
<div className="relative">
|
||||
<div className="w-24 h-24 rounded-2xl bg-gradient-to-br from-primary to-primary/50 flex items-center justify-center text-3xl font-bold text-primary-foreground glow-purple">
|
||||
{user.username.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className="absolute -bottom-2 -right-2 w-8 h-8 rounded-lg rank-gold flex items-center justify-center text-sm font-bold glow-gold">
|
||||
#1
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User Info */}
|
||||
<div className="flex-1">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-1">{user.username}</h1>
|
||||
<p className="text-muted-foreground mb-3">Runner since {user.joinDate}</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<span className="px-3 py-1 rounded-lg bg-primary/10 text-primary text-sm font-medium border border-primary/30">
|
||||
World Record Holder
|
||||
</span>
|
||||
<span className="px-3 py-1 rounded-lg bg-secondary text-secondary-foreground text-sm">
|
||||
{user.favoriteCategory} Main
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="text-right">
|
||||
<p className="text-4xl font-bold font-mono text-primary text-glow">{user.pb}</p>
|
||||
<p className="text-sm text-muted-foreground">Personal Best</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard label="Total Runs" value={user.totalRuns.toLocaleString()} icon={Zap} index={0} />
|
||||
<StatCard label="Clean Runs" value={user.cleanRuns.toString()} icon={Target} trend="+12 this week" index={1} />
|
||||
<StatCard label="Global Rank" value={`#${user.rank}`} icon={Trophy} index={2} />
|
||||
<StatCard label="Avg Time" value={user.avgTime} icon={Timer} index={3} />
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{/* Recent Runs */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-card border border-border rounded-xl p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold text-foreground">Recent Runs</h2>
|
||||
<button className="text-sm text-primary hover:underline">View All</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{recentRuns.map((run, i) => (
|
||||
<div
|
||||
key={run.id}
|
||||
className="flex items-center justify-between py-3 px-4 rounded-lg bg-secondary/30 hover:bg-secondary/50 transition-colors animate-fade-in opacity-0"
|
||||
style={{ animationDelay: `${i * 0.1}s`, animationFillMode: 'forwards' }}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-2 h-2 rounded-full ${run.status === 'clean' ? 'bg-emerald-400' : 'bg-red-400'
|
||||
}`} />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{run.category}</p>
|
||||
<p className="text-xs text-muted-foreground">{run.date}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className={`font-mono font-semibold ${run.status === 'reset' ? 'text-red-400' : 'text-foreground'
|
||||
}`}>
|
||||
{run.time}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground capitalize">{run.status}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Achievements */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-card border border-border rounded-xl p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold text-foreground">Achievements</h2>
|
||||
<span className="text-sm text-muted-foreground">5/6</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{achievements.map((achievement, i) => (
|
||||
<div
|
||||
key={achievement.name}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg transition-all animate-fade-in opacity-0 ${achievement.unlocked
|
||||
? 'bg-primary/10 border border-primary/20'
|
||||
: 'bg-secondary/30 opacity-50'
|
||||
}`}
|
||||
style={{ animationDelay: `${i * 0.1}s`, animationFillMode: 'forwards' }}
|
||||
>
|
||||
<div className={`p-2 rounded-lg ${achievement.unlocked ? 'bg-primary/20 text-primary' : 'bg-secondary text-muted-foreground'
|
||||
}`}>
|
||||
<achievement.icon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className={`text-sm font-medium ${achievement.unlocked ? 'text-foreground' : 'text-muted-foreground'
|
||||
}`}>
|
||||
{achievement.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{achievement.description}</p>
|
||||
</div>
|
||||
{achievement.unlocked && (
|
||||
<div className="w-5 h-5 rounded-full bg-primary flex items-center justify-center">
|
||||
<svg className="w-3 h-3 text-primary-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* PB History */}
|
||||
<div className="bg-card border border-border rounded-xl p-6 mt-4">
|
||||
<h2 className="text-xl font-bold text-foreground mb-4">PB History</h2>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:34.21</span>
|
||||
<span className="text-xs text-muted-foreground">Today</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:38.54</span>
|
||||
<span className="text-xs text-muted-foreground">3 days ago</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:45.12</span>
|
||||
<span className="text-xs text-muted-foreground">1 week ago</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-muted-foreground text-sm">2:52.33</span>
|
||||
<span className="text-xs text-muted-foreground">2 weeks ago</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex flex-col md:flex-row items-start md:items-center gap-6">
|
||||
{/* Avatar */}
|
||||
<div className="relative">
|
||||
<div className="w-24 h-24 rounded-2xl bg-gradient-to-br from-primary to-primary/50 flex items-center justify-center text-3xl font-bold text-primary-foreground glow-purple">
|
||||
{user.username.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className="absolute -bottom-2 -right-2 w-8 h-8 rounded-lg rank-gold flex items-center justify-center text-sm font-bold glow-gold">
|
||||
#1
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User Info */}
|
||||
<div className="flex-1">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-1">
|
||||
{user.username}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mb-3">
|
||||
Runner since {user.joinDate}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<span className="px-3 py-1 rounded-lg bg-primary/10 text-primary text-sm font-medium border border-primary/30">
|
||||
World Record Holder
|
||||
</span>
|
||||
<span className="px-3 py-1 rounded-lg bg-secondary text-secondary-foreground text-sm">
|
||||
{user.favoriteCategory} Main
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="text-right">
|
||||
<p className="text-4xl font-bold font-mono text-primary text-glow">
|
||||
{user.pb}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Personal Best</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard
|
||||
label="Total Runs"
|
||||
value={user.totalRuns.toLocaleString()}
|
||||
icon={Zap}
|
||||
index={0}
|
||||
/>
|
||||
<StatCard
|
||||
label="Clean Runs"
|
||||
value={user.cleanRuns.toString()}
|
||||
icon={Target}
|
||||
trend="+12 this week"
|
||||
index={1}
|
||||
/>
|
||||
<StatCard
|
||||
label="Global Rank"
|
||||
value={`#${user.rank}`}
|
||||
icon={Trophy}
|
||||
index={2}
|
||||
/>
|
||||
<StatCard
|
||||
label="Avg Time"
|
||||
value={user.avgTime}
|
||||
icon={Timer}
|
||||
index={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{/* Recent Runs */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-card border border-border rounded-xl p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold text-foreground">
|
||||
Recent Runs
|
||||
</h2>
|
||||
<button className="text-sm text-primary hover:underline">
|
||||
View All
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{recentRuns.map((run, i) => (
|
||||
<div
|
||||
key={run.id}
|
||||
className="flex items-center justify-between py-3 px-4 rounded-lg bg-secondary/30 hover:bg-secondary/50 transition-colors animate-fade-in opacity-0"
|
||||
style={{
|
||||
animationDelay: `${i * 0.1}s`,
|
||||
animationFillMode: "forwards",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full ${
|
||||
run.status === "clean"
|
||||
? "bg-emerald-400"
|
||||
: "bg-red-400"
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
{run.category}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{run.date}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p
|
||||
className={`font-mono font-semibold ${
|
||||
run.status === "reset"
|
||||
? "text-red-400"
|
||||
: "text-foreground"
|
||||
}`}
|
||||
>
|
||||
{run.time}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground capitalize">
|
||||
{run.status}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Achievements */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-card border border-border rounded-xl p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold text-foreground">
|
||||
Achievements
|
||||
</h2>
|
||||
<span className="text-sm text-muted-foreground">5/6</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{achievements.map((achievement, i) => (
|
||||
<div
|
||||
key={achievement.name}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg transition-all animate-fade-in opacity-0 ${
|
||||
achievement.unlocked
|
||||
? "bg-primary/10 border border-primary/20"
|
||||
: "bg-secondary/30 opacity-50"
|
||||
}`}
|
||||
style={{
|
||||
animationDelay: `${i * 0.1}s`,
|
||||
animationFillMode: "forwards",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`p-2 rounded-lg ${
|
||||
achievement.unlocked
|
||||
? "bg-primary/20 text-primary"
|
||||
: "bg-secondary text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
<achievement.icon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p
|
||||
className={`text-sm font-medium ${
|
||||
achievement.unlocked
|
||||
? "text-foreground"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{achievement.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{achievement.description}
|
||||
</p>
|
||||
</div>
|
||||
{achievement.unlocked && (
|
||||
<div className="w-5 h-5 rounded-full bg-primary flex items-center justify-center">
|
||||
<svg
|
||||
className="w-3 h-3 text-primary-foreground"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={3}
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* PB History */}
|
||||
<div className="bg-card border border-border rounded-xl p-6 mt-4">
|
||||
<h2 className="text-xl font-bold text-foreground mb-4">
|
||||
PB History
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:34.21</span>
|
||||
<span className="text-xs text-muted-foreground">Today</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:38.54</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
3 days ago
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-border">
|
||||
<span className="text-muted-foreground text-sm">2:45.12</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
1 week ago
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-muted-foreground text-sm">2:52.33</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
2 weeks ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
|
||||
+222
-155
@@ -3,166 +3,233 @@ import SplitRow from "@/components/app/SplitRow";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
const Run = () => {
|
||||
const splits = [
|
||||
{ problemName: "Two Sum Dash", splitTime: "0:38", delta: "-0:04", isCompleted: true, pbTime: "0:42" },
|
||||
{ problemName: "Binary Sprint", splitTime: "1:12", delta: "+0:02", isCompleted: true, pbTime: "0:32" },
|
||||
{ problemName: "String Scramble", splitTime: "--:--", delta: undefined, isCompleted: false, isCurrent: true, pbTime: "0:55" },
|
||||
{ problemName: "Linked List Rush", splitTime: "--:--", delta: undefined, isCompleted: false, pbTime: "1:24" },
|
||||
{ problemName: "Stack Overflow", splitTime: "--:--", delta: undefined, isCompleted: false, pbTime: "1:42" },
|
||||
];
|
||||
const splits = [
|
||||
{
|
||||
problemName: "Two Sum Dash",
|
||||
splitTime: "0:38",
|
||||
delta: "-0:04",
|
||||
isCompleted: true,
|
||||
pbTime: "0:42",
|
||||
},
|
||||
{
|
||||
problemName: "Binary Sprint",
|
||||
splitTime: "1:12",
|
||||
delta: "+0:02",
|
||||
isCompleted: true,
|
||||
pbTime: "0:32",
|
||||
},
|
||||
{
|
||||
problemName: "String Scramble",
|
||||
splitTime: "--:--",
|
||||
delta: undefined,
|
||||
isCompleted: false,
|
||||
isCurrent: true,
|
||||
pbTime: "0:55",
|
||||
},
|
||||
{
|
||||
problemName: "Linked List Rush",
|
||||
splitTime: "--:--",
|
||||
delta: undefined,
|
||||
isCompleted: false,
|
||||
pbTime: "1:24",
|
||||
},
|
||||
{
|
||||
problemName: "Stack Overflow",
|
||||
splitTime: "--:--",
|
||||
delta: undefined,
|
||||
isCompleted: false,
|
||||
pbTime: "1:42",
|
||||
},
|
||||
];
|
||||
|
||||
const runInfo = {
|
||||
category: "Any% Easy",
|
||||
problems: 5,
|
||||
pbTime: "4:35",
|
||||
worldRecord: "3:58",
|
||||
currentAttempt: 47,
|
||||
};
|
||||
const runInfo = {
|
||||
category: "Any% Easy",
|
||||
problems: 5,
|
||||
pbTime: "4:35",
|
||||
worldRecord: "3:58",
|
||||
currentAttempt: 47,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{/* Main Timer Area */}
|
||||
<div className="lg:col-span-2">
|
||||
{/* Category Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground text-sm mb-1">
|
||||
<span>Category</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<span className="text-primary">{runInfo.category}</span>
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Current Run</h1>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm text-muted-foreground">Attempt #{runInfo.currentAttempt}</p>
|
||||
<p className="text-xs text-muted-foreground">WR: <span className="text-primary font-mono">{runInfo.worldRecord}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timer Display */}
|
||||
<div className="bg-card border border-border rounded-2xl p-8 mb-6 text-center relative overflow-hidden">
|
||||
{/* Background glow */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-primary/5 to-transparent pointer-events-none" />
|
||||
|
||||
<div className="relative">
|
||||
{/* Timer */}
|
||||
<div className="mb-6">
|
||||
<p className="timer-display text-primary">1:52.34</p>
|
||||
<p className="text-muted-foreground mt-2 font-mono text-sm">
|
||||
PB: {runInfo.pbTime} | Diff: <span className="text-red-400">+0:12</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="w-full bg-secondary rounded-full h-2 mb-6">
|
||||
<div
|
||||
className="bg-primary h-2 rounded-full transition-all duration-300 glow-purple"
|
||||
style={{ width: '40%' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<Button variant="outline" size="lg" className="gap-2">
|
||||
<RotateCcw className="h-5 w-5" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant="hero" size="xl" className="gap-2">
|
||||
<Pause className="h-6 w-6" />
|
||||
Pause
|
||||
</Button>
|
||||
<Button variant="destructive" size="lg" className="gap-2">
|
||||
<Flag className="h-5 w-5" />
|
||||
End Run
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Current Problem */}
|
||||
<div className="bg-card border border-primary/30 rounded-xl p-6 glow-purple">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<p className="text-sm text-primary font-medium">Problem 3 of 5</p>
|
||||
<h2 className="text-2xl font-bold text-foreground">String Scramble</h2>
|
||||
</div>
|
||||
<span className="px-3 py-1 rounded-lg diff-easy text-sm font-medium">Easy</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
<span className="px-2 py-1 rounded-md bg-secondary text-muted-foreground text-xs">strings</span>
|
||||
<span className="px-2 py-1 rounded-md bg-secondary text-muted-foreground text-xs">manipulation</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<span>PB Split: <span className="text-primary font-mono">0:55</span></span>
|
||||
<span>|</span>
|
||||
<span>Best: <span className="text-emerald-400 font-mono">0:38</span></span>
|
||||
</div>
|
||||
|
||||
<Button className="w-full mt-6 gap-2" variant="default">
|
||||
<Play className="h-5 w-5" />
|
||||
Complete Split
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Splits Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-foreground">Splits</h3>
|
||||
<span className="text-xs text-muted-foreground">2/5 Complete</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{splits.map((split) => (
|
||||
<SplitRow key={split.problemName} {...split} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Sum of Best */}
|
||||
<div className="mt-6 pt-4 border-t border-border">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-muted-foreground">Sum of Best</span>
|
||||
<span className="font-mono text-emerald-400">3:51</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm mt-2">
|
||||
<span className="text-muted-foreground">Personal Best</span>
|
||||
<span className="font-mono text-primary">{runInfo.pbTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="bg-card border border-border rounded-xl p-4 mt-4">
|
||||
<h3 className="font-semibold text-foreground mb-4">Session Stats</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">Runs Today</span>
|
||||
<span className="font-mono text-foreground">12</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">Clean Runs</span>
|
||||
<span className="font-mono text-emerald-400">3</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">Resets</span>
|
||||
<span className="font-mono text-red-400">9</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">Time Played</span>
|
||||
<span className="font-mono text-foreground">1:42:31</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="min-h-screen pt-24 pb-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{/* Main Timer Area */}
|
||||
<div className="lg:col-span-2">
|
||||
{/* Category Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground text-sm mb-1">
|
||||
<span>Category</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<span className="text-primary">{runInfo.category}</span>
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Current Run
|
||||
</h1>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Attempt #{runInfo.currentAttempt}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
WR:{" "}
|
||||
<span className="text-primary font-mono">
|
||||
{runInfo.worldRecord}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timer Display */}
|
||||
<div className="bg-card border border-border rounded-2xl p-8 mb-6 text-center relative overflow-hidden">
|
||||
{/* Background glow */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-primary/5 to-transparent pointer-events-none" />
|
||||
|
||||
<div className="relative">
|
||||
{/* Timer */}
|
||||
<div className="mb-6">
|
||||
<p className="timer-display text-primary">1:52.34</p>
|
||||
<p className="text-muted-foreground mt-2 font-mono text-sm">
|
||||
PB: {runInfo.pbTime} | Diff:{" "}
|
||||
<span className="text-red-400">+0:12</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="w-full bg-secondary rounded-full h-2 mb-6">
|
||||
<div
|
||||
className="bg-primary h-2 rounded-full transition-all duration-300 glow-purple"
|
||||
style={{ width: "40%" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<Button variant="outline" size="lg" className="gap-2">
|
||||
<RotateCcw className="h-5 w-5" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant="hero" size="xl" className="gap-2">
|
||||
<Pause className="h-6 w-6" />
|
||||
Pause
|
||||
</Button>
|
||||
<Button variant="destructive" size="lg" className="gap-2">
|
||||
<Flag className="h-5 w-5" />
|
||||
End Run
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Current Problem */}
|
||||
<div className="bg-card border border-primary/30 rounded-xl p-6 glow-purple">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<p className="text-sm text-primary font-medium">
|
||||
Problem 3 of 5
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-foreground">
|
||||
String Scramble
|
||||
</h2>
|
||||
</div>
|
||||
<span className="px-3 py-1 rounded-lg diff-easy text-sm font-medium">
|
||||
Easy
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
<span className="px-2 py-1 rounded-md bg-secondary text-muted-foreground text-xs">
|
||||
strings
|
||||
</span>
|
||||
<span className="px-2 py-1 rounded-md bg-secondary text-muted-foreground text-xs">
|
||||
manipulation
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<span>
|
||||
PB Split: <span className="text-primary font-mono">0:55</span>
|
||||
</span>
|
||||
<span>|</span>
|
||||
<span>
|
||||
Best: <span className="text-emerald-400 font-mono">0:38</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button className="w-full mt-6 gap-2" variant="default">
|
||||
<Play className="h-5 w-5" />
|
||||
Complete Split
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Splits Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-foreground">Splits</h3>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
2/5 Complete
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{splits.map((split) => (
|
||||
<SplitRow key={split.problemName} {...split} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Sum of Best */}
|
||||
<div className="mt-6 pt-4 border-t border-border">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-muted-foreground">Sum of Best</span>
|
||||
<span className="font-mono text-emerald-400">3:51</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm mt-2">
|
||||
<span className="text-muted-foreground">Personal Best</span>
|
||||
<span className="font-mono text-primary">
|
||||
{runInfo.pbTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="bg-card border border-border rounded-xl p-4 mt-4">
|
||||
<h3 className="font-semibold text-foreground mb-4">
|
||||
Session Stats
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">
|
||||
Runs Today
|
||||
</span>
|
||||
<span className="font-mono text-foreground">12</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">
|
||||
Clean Runs
|
||||
</span>
|
||||
<span className="font-mono text-emerald-400">3</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">Resets</span>
|
||||
<span className="font-mono text-red-400">9</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground text-sm">
|
||||
Time Played
|
||||
</span>
|
||||
<span className="font-mono text-foreground">1:42:31</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Run;
|
||||
|
||||
Reference in New Issue
Block a user