diff --git a/package-lock.json b/package-lock.json
index 0240847..ea2cd87 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,6 +35,8 @@
"@radix-ui/react-toggle": "^1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
+ "@supabase/ssr": "^0.8.0",
+ "@supabase/supabase-js": "^2.94.1",
"@uiw/codemirror-theme-vscode": "^4.25.4",
"@uiw/react-codemirror": "^4.25.4",
"class-variance-authority": "^0.7.1",
@@ -2744,6 +2746,98 @@
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
+ "node_modules/@supabase/auth-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.94.1.tgz",
+ "integrity": "sha512-Wt/SdmAtNNiqrcBbPlzWojLcE1bQ9OYb8PTaYF6QccFX5JeXZI0sZ01MLNE+E83UK6cK0lw4YznX0D2g08UQng==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@supabase/functions-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.94.1.tgz",
+ "integrity": "sha512-A7Bx0gnclDNZ4m8+mnO2IEEzMxtUSg7cpPEBF6Ek1LpjIQkC7vvoidiV/RuntnKX43IiVcWV1f2FsAppMagEmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@supabase/postgrest-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.94.1.tgz",
+ "integrity": "sha512-N6MTghjHnMZddT48rAj8dIFgedCU97cc1ahQM74Tc+DF4UH7y2+iEfdYV3unJsylpaiWlu92Fy8Lj14Jbrmxog==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@supabase/realtime-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.94.1.tgz",
+ "integrity": "sha512-Wq8olpCAGmN4y2DH2kUdlcakdzNHRCde72BFS8zK5ub46bBeSUoE9DqrfeNFWKaF2gCE/cmK8aTUTorZD9jdtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/phoenix": "^1.6.6",
+ "@types/ws": "^8.18.1",
+ "tslib": "2.8.1",
+ "ws": "^8.18.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@supabase/ssr": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.8.0.tgz",
+ "integrity": "sha512-/PKk8kNFSs8QvvJ2vOww1mF5/c5W8y42duYtXvkOSe+yZKRgTTZywYG2l41pjhNomqESZCpZtXuWmYjFRMV+dw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.2"
+ },
+ "peerDependencies": {
+ "@supabase/supabase-js": "^2.76.1"
+ }
+ },
+ "node_modules/@supabase/storage-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.94.1.tgz",
+ "integrity": "sha512-/Mi18LGyrugPwtfqETfAqEGcBQotY/7IMsTGYgEFdqr8cQq280BVQWjN2wI9KibWtshPp0Ryvil5Uzd5YfM7kA==",
+ "license": "MIT",
+ "dependencies": {
+ "iceberg-js": "^0.8.1",
+ "tslib": "2.8.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@supabase/supabase-js": {
+ "version": "2.94.1",
+ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.94.1.tgz",
+ "integrity": "sha512-87vOY8n3WHB3m+a/KeySj07djOQVuRA5qgX5E7db1eDkaZ1of5M+3t/tv6eYYy4BfqxuHMZuCe5uVrO/oyvoow==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/auth-js": "2.94.1",
+ "@supabase/functions-js": "2.94.1",
+ "@supabase/postgrest-js": "2.94.1",
+ "@supabase/realtime-js": "2.94.1",
+ "@supabase/storage-js": "2.94.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -3091,12 +3185,17 @@
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
+ "node_modules/@types/phoenix": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
+ "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
+ "license": "MIT"
+ },
"node_modules/@types/react": {
"version": "19.2.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
@@ -3117,6 +3216,15 @@
"@types/react": "^19.2.0"
}
},
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@uiw/codemirror-extensions-basic-setup": {
"version": "4.25.4",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.4.tgz",
@@ -3300,6 +3408,19 @@
"@codemirror/view": "^6.0.0"
}
},
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
@@ -3554,6 +3675,15 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/iceberg-js": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
+ "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
@@ -4613,7 +4743,6 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/use-callback-ref": {
@@ -4695,6 +4824,27 @@
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
+ },
+ "node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index db0abb3..e721d45 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,8 @@
"@radix-ui/react-toggle": "^1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
+ "@supabase/ssr": "^0.8.0",
+ "@supabase/supabase-js": "^2.94.1",
"@uiw/codemirror-theme-vscode": "^4.25.4",
"@uiw/react-codemirror": "^4.25.4",
"class-variance-authority": "^0.7.1",
diff --git a/src/app/leaderboards/page.tsx b/src/app/leaderboards/page.tsx
index fd28720..74f43d1 100644
--- a/src/app/leaderboards/page.tsx
+++ b/src/app/leaderboards/page.tsx
@@ -25,158 +25,19 @@ import {
import LeaderboardRow from "@/app/leaderboards/components/LeaderboardRow";
import StatCard from "@/components/app/StatCard";
-
-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%",
- },
-];
+import useAction from "@/hook/use-action";
+import {
+ getGlobalLeaderboard,
+ getWeeklyLeaderboard,
+} from "@/modules/leaderboard/actions";
export default function Leaderboards() {
+ const [global, globalLoaded, globalError] = useAction(getGlobalLeaderboard);
+ const [weekly, weeklyLoaded, weeklyError] = useAction(getWeeklyLeaderboard);
+
const [activeTab, setActiveTab] = useState<"global" | "weekly">("global");
-
const categories = ["Any%", "100%", "Easy Only", "No DP", "Blind"];
-
- const data = activeTab === "global" ? globalLeaderboard : weeklyLeaderboard;
+ const data = (activeTab === "global" ? global : weekly) || [];
return (
diff --git a/src/app/problems/page.tsx b/src/app/problems/page.tsx
index 35b15cd..1992627 100644
--- a/src/app/problems/page.tsx
+++ b/src/app/problems/page.tsx
@@ -8,8 +8,12 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
+import useAction from "@/hook/use-action";
+import { getProblems } from "@/modules/problems/actions";
export default function Problems() {
+ const [problems, problemsLoaded, problemsError] = useAction(getProblems);
+
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const categories = ["All", "Arrays", "Trees", "Graphs", "DP", "Strings"];
@@ -83,100 +87,12 @@ export default function Problems() {
: "grid-cols-1"
}`}
>
- {problems.map((problem, i) => (
-
- ))}
+ {problems &&
+ problems.map((problem, i) => (
+
+ ))}
);
}
-
-/* ---------------- data ---------------- */
-
-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,
- },
-];
diff --git a/src/app/runs/page.tsx b/src/app/runs/page.tsx
index 7ba7a3c..35a1c37 100644
--- a/src/app/runs/page.tsx
+++ b/src/app/runs/page.tsx
@@ -16,53 +16,14 @@ import {
import { cn } from "@/lib/utils";
import LiveRunRow from "./components/LiveRunNow";
import StatCard from "@/components/app/StatCard";
-
-/* ---------------- mock data ---------------- */
-
-const liveRuns = [
- {
- id: 1,
- username: "speedmaster_x",
- problem: "Two Sum Dash",
- category: "Any%",
- time: "1:52.34",
- pace: "-0:03",
- status: "pb",
- },
- {
- id: 2,
- username: "algo_ninja",
- problem: "Binary Sprint",
- category: "Any%",
- time: "2:04.11",
- pace: "+0:05",
- status: "normal",
- },
- {
- id: 3,
- username: "stackattack",
- problem: "String Scramble",
- category: "Easy",
- time: "0:48.92",
- pace: "-0:01",
- status: "pb",
- },
- {
- id: 4,
- username: "reset_king",
- problem: "Linked List Rush",
- category: "Any%",
- time: "1:31.77",
- pace: "+0:12",
- status: "danger",
- },
-];
-
-/* ---------------- page ---------------- */
+import useAction from "@/hook/use-action";
+import { getLiveRuns } from "@/modules/live-run/actions";
export default function LiveRuns() {
+ const [liveRuns, liveRunsLoaded, liveRunsError] = useAction(getLiveRuns);
+
// Sort top runners (status === 'pb') to the top
- const sortedRuns = [...liveRuns].sort((a, b) => {
+ const sortedRuns = [...(liveRuns || [])].sort((a, b) => {
if (a.status === "pb" && b.status !== "pb") return -1;
if (a.status !== "pb" && b.status === "pb") return 1;
return 0;
diff --git a/src/components/auth/ContinueWith.tsx b/src/components/auth/ContinueWith.tsx
index 9e94783..d3c85d1 100644
--- a/src/components/auth/ContinueWith.tsx
+++ b/src/components/auth/ContinueWith.tsx
@@ -1,7 +1,15 @@
+"use client";
+
import { Github, Mail } from "lucide-react";
import { Button } from "../ui/button";
+import { signInWithGitHub } from "@/modules/account/actions";
export default function ContinueWith() {
+ const continueWithGithub = () => {
+ signInWithGitHub();
+ };
+ const continueWithGoogle = () => {};
+
return (
<>
@@ -16,11 +24,15 @@ export default function ContinueWith() {
-