Files
edbridge-scholars/src/pages/student/Home.tsx
2026-02-15 18:37:28 +06:00

368 lines
14 KiB
TypeScript

import { useEffect, useState } from "react";
import {
Tabs,
TabsTrigger,
TabsList,
TabsContent,
} from "../../components/ui/tabs";
import { useAuthStore } from "../../stores/authStore";
import { CheckCircle, Search } from "lucide-react";
import { api } from "../../utils/api";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "../../components/ui/card";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import type { PracticeSheet } from "../../types/sheet";
import { formatStatus } from "../../lib/utils";
import { useNavigate } from "react-router-dom";
import { SearchOverlay } from "../../components/SearchOverlay";
export const Home = () => {
const user = useAuthStore((state) => state.user);
const navigate = useNavigate();
const [practiceSheets, setPracticeSheets] = useState<PracticeSheet[]>([]);
const [notStartedSheets, setNotStartedSheets] = useState<PracticeSheet[]>([]);
const [inProgressSheets, setInProgressSheets] = useState<PracticeSheet[]>([]);
const [completedSheets, setCompletedSheets] = useState<PracticeSheet[]>([]);
const [isSearchOpen, setIsSearchOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
useEffect(() => {
const sortPracticeSheets = (sheets: PracticeSheet[]) => {
const notStarted = sheets.filter(
(sheet) => sheet.user_status === "NOT_STARTED",
);
const inProgress = sheets.filter(
(sheet) => sheet.user_status === "IN_PROGRESS",
);
const completed = sheets.filter(
(sheet) => sheet.user_status === "COMPLETED",
);
setNotStartedSheets(notStarted);
setInProgressSheets(inProgress);
setCompletedSheets(completed);
};
const fetchPracticeSheets = async () => {
if (!user) return;
try {
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) {
console.error("authStorage not found in local storage");
return;
}
const {
state: { token },
} = JSON.parse(authStorage);
if (!token) {
console.error("Token not found in authStorage");
return;
}
const sheets = await api.getPracticeSheets(token, 1, 10);
setPracticeSheets(sheets.data);
sortPracticeSheets(sheets.data);
} catch (error) {
console.error("Error fetching practice sheets:", error);
}
};
fetchPracticeSheets();
}, [user]);
const handleStartPracticeSheet = (sheetId: string) => {
navigate(`/student/practice/${sheetId}`);
};
return (
<main className="min-h-screen bg-gray-50 space-y-6 mx-auto px-8 sm:px-6 lg:px-90 py-12">
<h1 className="text-4xl font-satoshi-bold tracking-tight text-gray-800 text-center">
Welcome, {user?.name || "Student"}
</h1>
<h1 className="font-satoshi-bold text-2xl tracking-tight">
What are you looking for?
</h1>
<section className="relative w-full">
<input
onFocus={() => setIsSearchOpen(true)}
placeholder="Search practice sheets..."
readOnly
className="font-satoshi w-full pl-10 pr-4 py-3 border border-gray-300 rounded-2xl shadow-sm cursor-pointer"
/>
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<Search size={22} color="gray" />
</div>
</section>
<section className="space-y-4">
<h1 className="font-satoshi-bold text-2xl tracking-tight">
Pick up where you left off
</h1>
{inProgressSheets.length > 0 ? (
inProgressSheets.map((sheet) => (
<Card
key={sheet?.id}
className="rounded-4xl border bg-purple-50/70 border-purple-500"
>
<CardHeader>
<CardTitle className="font-satoshi-medium text-xl">
{sheet?.title}
</CardTitle>
<CardDescription className="font-satoshi">
{sheet?.description}
</CardDescription>
</CardHeader>
<CardContent className="flex justify-between">
<p className="font-satoshi text-sm border px-2 rounded-full bg-purple-500 text-white py-1">
{formatStatus(sheet?.user_status)}
</p>
<Badge
variant="secondary"
className="bg-indigo-100 text-indigo-500 font-satoshi tracking-wide"
>
{sheet?.modules_count} modules
</Badge>
</CardContent>
<CardContent>
<p className="font-satoshi text-gray-700">
{sheet?.time_limit} minutes
</p>
</CardContent>
<CardFooter>
<Button
onClick={() => handleStartPracticeSheet(sheet?.id)}
variant="outline"
className="font-satoshi rounded-3xl w-full text-lg py-6 bg-linear-to-br from-purple-500 to-purple-600 text-white"
>
Resume
</Button>
</CardFooter>
</Card>
))
) : (
<Card className="flex items-center justify-center py-4 rounded-4xl">
<h2 className="text-center font-satoshi text-lg text-gray-800">
You don't have any practice sheets in progress. Why not start one?
</h2>
</Card>
)}
</section>
<section className="w-full">
<Tabs defaultValue="all" className="w-full">
<TabsList className="bg-transparent p-0 w-full">
<TabsTrigger
value="all"
className="font-satoshi-regular tracking-wide text-md rounded-none border-b-3 data-[state=active]:font-satoshi-medium data-[state=active]:border-b-purple-800 data-[state=active]:text-purple-800"
>
All
</TabsTrigger>
<TabsTrigger
value="NOT_STARTED"
className="font-satoshi-regular tracking-wide text-md rounded-none border-b-3 data-[state=active]:border-b-purple-800 data-[state=active]:text-purple-800"
>
Not Started
</TabsTrigger>
<TabsTrigger
value="COMPLETED"
className="font-satoshi-regular tracking-wide text-md rounded-none border-b-3 data-[state=active]:border-b-purple-800 data-[state=active]:text-purple-800"
>
Completed
</TabsTrigger>
</TabsList>
<TabsContent value="all" className="pt-6">
<div className="gap-6 flex flex-col md:grid md:grid-cols-2">
{practiceSheets.length > 0 ? (
practiceSheets.map((sheet) => (
<Card key={sheet?.id} className="rounded-4xl">
<CardHeader>
<CardTitle className="font-satoshi-medium text-xl">
{sheet?.title}
</CardTitle>
<CardDescription className="font-satoshi">
{sheet?.description}
</CardDescription>
</CardHeader>
<CardContent className="flex justify-between">
<p className="font-satoshi text-gray-500">
{formatStatus(sheet?.user_status)}
</p>
<Badge
variant="secondary"
className="bg-indigo-100 text-indigo-500 font-satoshi tracking-wide"
>
{sheet?.modules_count} modules
</Badge>
</CardContent>
<CardContent>
<p className="font-satoshi text-gray-700">
{sheet?.time_limit} minutes
</p>
</CardContent>
<CardFooter>
<Button
onClick={() => handleStartPracticeSheet(sheet?.id)}
variant="outline"
className="font-satoshi rounded-3xl w-full text-lg py-6 bg-linear-to-br from-purple-500 to-purple-600 text-white"
>
Start
</Button>
</CardFooter>
</Card>
))
) : (
<div className="flex items-center justify-center py-4 rounded-full">
<h2 className="text-center font-satoshi text-lg text-gray-500">
No Practice Sheets available.
</h2>
</div>
)}
</div>
</TabsContent>
<TabsContent value="NOT_STARTED" className="pt-6">
<div className="gap-6 flex flex-col md:grid md:grid-cols-2">
{notStartedSheets.map((sheet) => (
<Card key={sheet?.id} className="rounded-4xl">
<CardHeader>
<CardTitle className="font-satoshi-medium text-xl">
{sheet?.title}
</CardTitle>
<CardDescription className="font-satoshi">
{sheet?.description}
</CardDescription>
</CardHeader>
<CardContent className="flex justify-between">
<p className="font-satoshi text-gray-700">Not Started</p>
<Badge
variant="secondary"
className="bg-indigo-100 text-indigo-500 font-satoshi tracking-wide "
>
{sheet?.modules_count} modules
</Badge>
</CardContent>
<CardContent>
<p className="font-satoshi text-gray-700">
{sheet?.time_limit} minutes
</p>
</CardContent>
<CardFooter>
<Button
variant="outline"
className="font-satoshi w-full text-lg py-6 bg-linear-to-br from-purple-500 to-purple-600 rounded-3xl text-white"
>
Start
</Button>
</CardFooter>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="COMPLETED" className="pt-6">
<div className="gap-6 flex flex-col md:grid md:grid-cols-2">
{completedSheets.length > 0 ? (
completedSheets.map((sheet) => (
<Card key={sheet?.id} className="rounded-4xl">
<CardHeader>
<CardTitle className="font-satoshi-medium text-xl">
{sheet?.title}
</CardTitle>
<CardDescription className="font-satoshi">
{sheet?.description}
</CardDescription>
</CardHeader>
<CardContent className="flex justify-between">
<p className="font-satoshi text-gray-500">
{formatStatus(sheet?.user_status)}
</p>
<Badge
variant="secondary"
className="bg-indigo-100 text-indigo-500 font-satoshi tracking-wide"
>
{sheet?.modules_count} modules
</Badge>
</CardContent>
<CardContent>
<p className="font-satoshi text-gray-700">
{sheet?.time_limit} minutes
</p>
</CardContent>
<CardFooter>
<Button
variant="outline"
className="font-satoshi w-full text-lg py-6 bg-linear-to-br from-purple-500 to-purple-600 rounded-3xl text-white"
>
Start
</Button>
</CardFooter>
</Card>
))
) : (
<div className="flex items-center justify-center py-4 rounded-full">
<h2 className="text-center font-satoshi text-lg text-gray-500">
You have not completed any practice sheets.
</h2>
</div>
)}
</div>
</TabsContent>
</Tabs>
</section>
<section className="space-y-4 ">
<h1 className="font-satoshi-bold text-2xl tracking-tight">
SAT Preparation Tips
</h1>
<section className="space-y-4 ">
<div className="flex gap-2">
<CheckCircle size={24} color="#AD45FF" />
<p className="font-satoshi text-md">
Practice regularly with official SAT materials
</p>
</div>
<div className="flex items-center gap-2">
<CheckCircle size={24} color="#AD45FF" />
<p className="font-satoshi text-md">
Review your mistakes and learn from them
</p>
</div>
<div className="flex items-center gap-2">
<CheckCircle size={24} color="#AD45FF" />
<p className="font-satoshi text-md">Focus on your weak areas</p>
</div>
<div className="flex items-center gap-2">
<CheckCircle size={24} color="#AD45FF" />
<p className="font-satoshi text-md">
Take full-length practice tests
</p>
</div>
<div className="flex items-center gap-2">
<CheckCircle size={24} color="#AD45FF" />
<p className="font-satoshi text-md">
Get plenty of rest before the test day
</p>
</div>
</section>
</section>
{isSearchOpen && (
<SearchOverlay
sheets={practiceSheets}
onClose={() => {
setIsSearchOpen(false);
setSearchQuery("");
}}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
)}
</main>
);
};