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
102 changes: 102 additions & 0 deletions src/app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use client";

import { useState } from "react";

interface NavbarProps {
onLogout?: () => void;
isSidebarOpen?: boolean;
}

export default function Navbar({
onLogout,
isSidebarOpen = true,
}: NavbarProps) {
const [isDarkMode, setIsDarkMode] = useState(false);

const handleLogout = () => {
if (onLogout) {
onLogout();
} else {
console.log("Logout clicked");
}
};

return (
<nav
className={`h-16 bg-white border-b border-gray-200 fixed top-0 right-0 z-10 px-6 flex items-center justify-end transition-all duration-300 ${
isSidebarOpen ? "left-60" : "left-0"
}`}
>
<div className="flex items-center gap-3">
<button
onClick={() => setIsDarkMode(!isDarkMode)}
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
aria-label="Toggle dark mode"
>
{isDarkMode ? (
<svg
className="w-5 h-5 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
) : (
<svg
className="w-5 h-5 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
)}
</button>

<button
className="p-2 hover:bg-gray-100 rounded-lg transition-colors relative"
aria-label="Notifications"
>
<svg
className="w-5 h-5 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
/>
</svg>
<span className="absolute top-1.5 right-1.5 w-2 h-2 bg-red-500 rounded-full"></span>
</button>

<button className="flex items-center gap-2 p-1.5 hover:bg-gray-100 rounded-lg transition-colors">
<div className="w-8 h-8 bg-purple-600 rounded-full flex items-center justify-center text-white font-semibold text-sm">
A
</div>
</button>

<button
onClick={handleLogout}
className="px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 transition-colors font-medium text-sm"
>
Logout
</button>
</div>
</nav>
);
}
156 changes: 156 additions & 0 deletions src/app/components/ProblemsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"use client";

import { Problem } from "@/types";

interface ProblemsTableProps {
problems: Problem[];
onProblemClick?: (problemId: string) => void;
}

export default function ProblemsTable({
problems,
onProblemClick,
}: ProblemsTableProps) {
const getDifficultyColor = (difficulty: string) => {
switch (difficulty) {
case "Easy":
return "bg-green-50 text-green-700 border border-green-200";
case "Medium":
return "bg-yellow-50 text-yellow-700 border border-yellow-200";
case "Hard":
return "bg-red-50 text-red-700 border border-red-200";
default:
return "bg-gray-50 text-gray-700 border border-gray-200";
}
};

const getStatusIcon = (status: string) => {
switch (status) {
case "solved":
return (
<div className="w-5 h-5 rounded-full bg-green-500 flex items-center justify-center">
<svg
className="w-3 h-3 text-white"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M5 13l4 4L19 7"
/>
</svg>
</div>
);
case "attempted":
return (
<div className="w-5 h-5 rounded-full border-2 border-orange-500"></div>
);
default:
return (
<div className="w-5 h-5 rounded-full border-2 border-gray-300"></div>
);
}
};

const handleProblemClick = (problemId: string) => {
if (onProblemClick) {
onProblemClick(problemId);
}
};

return (
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-20">
Status
</th>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
Problem
</th>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-32">
Difficulty
</th>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-48">
Category
</th>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-32">
Acceptance
</th>
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-32">
Action
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{problems.map((problem, index) => (
<tr
key={problem.id}
className={`hover:bg-gray-100 transition-colors cursor-pointer ${
index % 2 === 0 ? "bg-white" : "bg-gray-50"
}`}
onClick={() => handleProblemClick(problem.id)}
>
<td className="px-6 py-4">{getStatusIcon(problem.status)}</td>
<td className="px-6 py-4">
<div>
<div className="font-medium text-gray-900 hover:text-blue-600 mb-1">
{problem.title}
</div>
<div className="flex gap-2 flex-wrap">
{problem.tags.map((tag, index) => (
<span
key={index}
className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded"
>
{tag}
</span>
))}
</div>
</div>
</td>
<td className="px-6 py-4">
<span
className={`inline-flex px-3 py-1 text-xs font-medium rounded ${getDifficultyColor(
problem.difficulty
)}`}
>
{problem.difficulty}
</span>
</td>
<td className="px-6 py-4 text-sm text-gray-700">
{problem.category}
</td>
<td className="px-6 py-4 text-sm font-medium text-gray-900">
{problem.acceptance}%
</td>
<td className="px-6 py-4">
<button
onClick={(e) => {
e.stopPropagation();
handleProblemClick(problem.id);
}}
className="text-sm font-medium text-gray-900 hover:text-blue-600"
>
Solve
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>

{problems.length === 0 && (
<div className="text-center py-12 text-gray-500">
No problems found. Try adjusting your filters.
</div>
)}
</div>
);
}
67 changes: 67 additions & 0 deletions src/app/components/RecentActivity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

interface ActivityItem {
id: string;
title: string;
difficulty: "Easy" | "Medium" | "Hard";
status: "Completed" | "In Progress";
progress: number;
}

interface RecentActivityProps {
activities: ActivityItem[];
}

export default function RecentActivity({ activities }: RecentActivityProps) {
const getDifficultyColor = (difficulty: string) => {
switch (difficulty) {
case "Easy":
return "bg-green-50 text-green-700 border border-green-200";
case "Medium":
return "bg-yellow-50 text-yellow-700 border border-yellow-200";
case "Hard":
return "bg-red-50 text-red-700 border border-red-200";
default:
return "bg-gray-50 text-gray-700 border border-gray-200";
}
};

return (
<div className="bg-white rounded-lg border border-gray-200 p-6">
<h2 className="text-xl font-bold text-gray-900 mb-6">Recent Activity</h2>
<div className="space-y-0">
{activities.map((activity, index) => (
<div
key={activity.id}
className={`border-b border-gray-200 last:border-b-0 p-3 ${
index % 2 === 0 ? "bg-white" : "bg-gray-50"
}`}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<h3 className="text-sm font-semibold text-gray-900 mb-1">
{activity.title}
</h3>
<div className="flex items-center gap-3">
<span
className={`inline-flex px-2 py-0.5 text-xs font-medium rounded ${getDifficultyColor(
activity.difficulty
)}`}
>
{activity.difficulty}
</span>
<span className="text-sm text-gray-600">
{activity.status}
</span>
</div>
</div>
<button className="px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-50 rounded-lg transition-colors">
Continue
</button>
</div>
</div>
))}
</div>
</div>
);
}
47 changes: 47 additions & 0 deletions src/app/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";

import { useState } from "react";

interface SearchBarProps {
onSearch?: (query: string) => void;
placeholder?: string;
}

export default function SearchBar({
onSearch,
placeholder = "Search problems, topics, or algorithms...",
}: SearchBarProps) {
const [searchQuery, setSearchQuery] = useState("");

const handleSearch = (value: string) => {
setSearchQuery(value);
if (onSearch) {
onSearch(value);
}
};

return (
<div className="relative">
<svg
className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<input
type="text"
placeholder={placeholder}
value={searchQuery}
onChange={(e) => handleSearch(e.target.value)}
className="w-full pl-12 pr-4 py-3 bg-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
/>
</div>
);
}
Loading