working Problems
This commit is contained in:
@@ -1,23 +1,18 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Clock, Zap, Users } from "lucide-react";
|
import { Problem } from "@/modules/problems/types";
|
||||||
|
import { Clock, Zap, Users, Code } from "lucide-react";
|
||||||
interface ProblemCardProps {
|
|
||||||
name: string;
|
|
||||||
difficulty: "easy" | "medium" | "hard";
|
|
||||||
tags: string[];
|
|
||||||
bestTime?: string;
|
|
||||||
attempts: number;
|
|
||||||
index?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProblemCard = ({
|
const ProblemCard = ({
|
||||||
name,
|
problem,
|
||||||
difficulty,
|
|
||||||
tags,
|
|
||||||
bestTime,
|
|
||||||
attempts,
|
|
||||||
index = 0,
|
index = 0,
|
||||||
}: ProblemCardProps) => {
|
bestTime,
|
||||||
|
attempts = 0,
|
||||||
|
}: {
|
||||||
|
problem: Problem;
|
||||||
|
index?: number;
|
||||||
|
bestTime?: string;
|
||||||
|
attempts?: number;
|
||||||
|
}) => {
|
||||||
const difficultyStyles = {
|
const difficultyStyles = {
|
||||||
easy: "diff-easy",
|
easy: "diff-easy",
|
||||||
medium: "diff-medium",
|
medium: "diff-medium",
|
||||||
@@ -46,29 +41,30 @@ const ProblemCard = ({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
<h3 className="font-semibold text-foreground group-hover:text-primary transition-colors">
|
<h3 className="font-semibold text-foreground group-hover:text-primary transition-colors">
|
||||||
{name}
|
{problem.title}
|
||||||
</h3>
|
</h3>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-md text-xs font-medium ${difficultyStyles[difficulty]}`}
|
className={`px-2 py-1 rounded-md text-xs font-medium ${
|
||||||
|
difficultyStyles[problem.difficulty]
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{difficultyLabels[difficulty]}
|
{difficultyLabels[problem.difficulty]}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tags */}
|
{/* Tags / Meta */}
|
||||||
<div className="flex flex-wrap gap-2 mb-4">
|
<div className="flex flex-wrap gap-2 mb-4 text-xs text-muted-foreground">
|
||||||
{tags.map((tag) => (
|
{problem.language && (
|
||||||
<span
|
<span className="px-2 py-1 rounded-md bg-secondary">
|
||||||
key={tag}
|
<Code className="w-3 h-3 inline-block mr-1" /> {problem.language}
|
||||||
className="px-2 py-1 rounded-md bg-secondary text-muted-foreground text-xs"
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</span>
|
</span>
|
||||||
))}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Spacer */}
|
{/* Description */}
|
||||||
<div className="flex-1" />
|
<p className="text-sm text-muted-foreground flex-1">
|
||||||
|
{problem.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* Stats + Action */}
|
{/* Stats + Action */}
|
||||||
<div className="flex items-center justify-between mt-4 text-sm text-muted-foreground">
|
<div className="flex items-center justify-between mt-4 text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { getProblems } from "@/modules/problems/actions";
|
|||||||
export default function Problems() {
|
export default function Problems() {
|
||||||
const [problems, problemsLoaded, problemsError] = useAction(getProblems);
|
const [problems, problemsLoaded, problemsError] = useAction(getProblems);
|
||||||
|
|
||||||
|
console.log(problems);
|
||||||
|
|
||||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
||||||
|
|
||||||
const difficulties = ["All", "Easy", "Medium", "Hard"];
|
const difficulties = ["All", "Easy", "Medium", "Hard"];
|
||||||
@@ -84,7 +86,7 @@ export default function Problems() {
|
|||||||
>
|
>
|
||||||
{problems &&
|
{problems &&
|
||||||
problems.map((problem, i) => (
|
problems.map((problem, i) => (
|
||||||
<ProblemCard key={problem.name} {...problem} index={i} />
|
<ProblemCard key={problem.title} index={i} problem={problem} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,11 +45,15 @@ export default function Run() {
|
|||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<ProblemCard
|
<ProblemCard
|
||||||
name="Idk"
|
|
||||||
difficulty="hard"
|
|
||||||
tags={[]}
|
|
||||||
attempts={0}
|
|
||||||
index={4}
|
index={4}
|
||||||
|
problem={{
|
||||||
|
id: "fix-auth-callback",
|
||||||
|
title: "Fix the Broken Auth Callback",
|
||||||
|
description:
|
||||||
|
"Users can log in with OAuth, but sessions aren't persisted. Fix it.",
|
||||||
|
language: "typescript",
|
||||||
|
difficulty: "easy",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,90 +4,5 @@ import * as Core from "./core";
|
|||||||
import { Problem } from "./types";
|
import { Problem } from "./types";
|
||||||
|
|
||||||
export async function getProblems(): Promise<Problem[]> {
|
export async function getProblems(): Promise<Problem[]> {
|
||||||
return [
|
return await Core.getProblems();
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,46 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import "server-only";
|
import "server-only";
|
||||||
|
import { ProblemDefinition } from "./types";
|
||||||
|
|
||||||
|
const problems: ProblemDefinition[] = [
|
||||||
|
{
|
||||||
|
id: "fix-auth-callback",
|
||||||
|
title: "Fix the Broken Auth Callback",
|
||||||
|
description:
|
||||||
|
"Users can log in with OAuth, but sessions aren't persisted. Fix it.",
|
||||||
|
language: "typescript",
|
||||||
|
difficulty: "easy",
|
||||||
|
validate100: () => true,
|
||||||
|
validateAny: () => true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "fix-prod-build",
|
||||||
|
title: "Patch a Failing Production Build",
|
||||||
|
description:
|
||||||
|
"Production build fails while development works. Fix the build.",
|
||||||
|
language: "javascript",
|
||||||
|
difficulty: "easy",
|
||||||
|
validate100: () => true,
|
||||||
|
validateAny: () => true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "add-user-profile-endpoint",
|
||||||
|
title: "Implement Missing API Endpoint",
|
||||||
|
description: "The frontend expects `/api/me`. Implement it correctly.",
|
||||||
|
language: "go",
|
||||||
|
difficulty: "easy",
|
||||||
|
validate100: () => true,
|
||||||
|
validateAny: () => true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function getProblems() {
|
||||||
|
return problems.map((def) => ({
|
||||||
|
id: def.id,
|
||||||
|
title: def.title,
|
||||||
|
description: def.description,
|
||||||
|
difficulty: def.difficulty,
|
||||||
|
language: def.language,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
|
export type ProblemValidator = (submission: any) => boolean;
|
||||||
|
|
||||||
export interface Problem {
|
export interface Problem {
|
||||||
name: string;
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
difficulty: "easy" | "medium" | "hard";
|
difficulty: "easy" | "medium" | "hard";
|
||||||
tags: string[];
|
language:
|
||||||
bestTime?: string;
|
| "rust"
|
||||||
attempts: number;
|
| "go"
|
||||||
|
| "java"
|
||||||
|
| "javascript"
|
||||||
|
| "typescript"
|
||||||
|
| "python"
|
||||||
|
| null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProblemDefinition extends Problem {
|
||||||
|
/* Any% validator */
|
||||||
|
validateAny: ProblemValidator;
|
||||||
|
/* 100% validator */
|
||||||
|
validate100: ProblemValidator;
|
||||||
|
metadata?: Record<string, any>; // optional extra info like time limits, refs
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user