231 lines
8.6 KiB
TypeScript
231 lines
8.6 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Outlet, replace, useParams } from "react-router-dom";
|
|
import { api } from "../../../utils/api";
|
|
import { useAuthStore } from "../../../stores/authStore";
|
|
import type { PracticeSheet } from "../../../types/sheet";
|
|
import {
|
|
CircleQuestionMark,
|
|
Clock,
|
|
Layers,
|
|
Loader,
|
|
Loader2,
|
|
Tag,
|
|
} from "lucide-react";
|
|
import {
|
|
Carousel,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
type CarouselApi,
|
|
} from "../../../components/ui/carousel";
|
|
import { Button } from "../../../components/ui/button";
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
export const Pretest = () => {
|
|
const user = useAuthStore((state) => state.user);
|
|
const { sheetId } = useParams<{ sheetId: string }>();
|
|
const [carouselApi, setCarouselApi] = useState<CarouselApi>();
|
|
const [current, setCurrent] = useState(0);
|
|
const [count, setCount] = useState(0);
|
|
const navigate = useNavigate();
|
|
|
|
const [practiceSheet, setPracticeSheet] = useState<PracticeSheet | null>(
|
|
null,
|
|
);
|
|
|
|
function handleStartTest(sheetId: string) {
|
|
if (!sheetId) {
|
|
console.error("Sheet ID is required to start the test.");
|
|
return;
|
|
}
|
|
navigate(`/student/practice/${sheetId}/test`, { replace: true });
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!user) return;
|
|
async function fetchPracticeSheet(sheetId: string) {
|
|
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 data = await api.getPracticeSheetById(token, sheetId);
|
|
setPracticeSheet(data);
|
|
}
|
|
fetchPracticeSheet(sheetId!);
|
|
}, [sheetId]);
|
|
|
|
useEffect(() => {
|
|
if (!carouselApi) {
|
|
return;
|
|
}
|
|
setCount(carouselApi.scrollSnapList().length);
|
|
setCurrent(carouselApi.selectedScrollSnap() + 1);
|
|
carouselApi.on("select", () => {
|
|
setCurrent(carouselApi.selectedScrollSnap() + 1);
|
|
});
|
|
}, [carouselApi]);
|
|
|
|
return (
|
|
<main className="max-w-full mx-auto px-8 sm:px-6 lg:px-8 py-8 space-y-6">
|
|
<header className="space-y-2">
|
|
<h1 className="text-4xl font-satoshi-bold">{practiceSheet?.title}</h1>
|
|
<p className="text-lg font-satoshi text-gray-700">
|
|
{practiceSheet?.description}
|
|
</p>
|
|
</header>
|
|
{practiceSheet ? (
|
|
<section className="flex flex-col gap-6 rounded-4xl bg-white border p-8">
|
|
<div className="flex items-center gap-4">
|
|
<Clock size={65} color="black" />
|
|
<div>
|
|
<h3 className="text-3xl font-satoshi-bold ">
|
|
{practiceSheet?.time_limit}
|
|
</h3>
|
|
<p className="text-xl font-satoshi ">Minutes</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<Layers size={65} color="black" />
|
|
<div>
|
|
<h3 className="text-3xl font-satoshi-bold ">
|
|
{practiceSheet?.modules.length}
|
|
</h3>
|
|
<p className="text-xl font-satoshi">Modules</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<CircleQuestionMark size={65} color="black" />
|
|
<div>
|
|
<h3 className="text-3xl font-satoshi-bold ">
|
|
{practiceSheet?.questions_count}
|
|
</h3>
|
|
<p className="text-xl font-satoshi ">Questions</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
) : (
|
|
<section className="flex flex-col items-center gap-6 rounded-4xl bg-white border p-8">
|
|
<div>
|
|
<Loader size={30} className="transition animate-spin" />
|
|
</div>
|
|
</section>
|
|
)}
|
|
<Carousel setApi={setCarouselApi}>
|
|
<CarouselContent className="">
|
|
{practiceSheet ? (
|
|
practiceSheet.modules.length > 0 ? (
|
|
practiceSheet.modules.map((module, index) => (
|
|
<CarouselItem key={index} className="">
|
|
<section className="flex flex-col gap-6 rounded-4xl p-8 bg-white border">
|
|
<h1 className="text-2xl font-satoshi-bold">
|
|
Section {Math.floor(index / 2) + 1}
|
|
</h1>
|
|
<p className="text-lg font-satoshi text-gray-700">
|
|
{module.title}
|
|
</p>
|
|
<section className="flex justify-between">
|
|
<div className="flex flex-col justify-center items-center gap-4">
|
|
<div className="w-fit bg-cyan-100 p-2 rounded-full">
|
|
<Clock size={30} color="oklch(60.9% 0.126 221.723)" />
|
|
</div>
|
|
<div className="flex flex-col justify-center items-center">
|
|
<h3 className="text-xl font-satoshi-bold ">
|
|
{module.duration}
|
|
</h3>
|
|
<p className="text-md font-satoshi ">Minutes</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col justify-center items-center gap-4">
|
|
<div className="w-fit bg-lime-100 p-2 rounded-full">
|
|
<CircleQuestionMark
|
|
size={30}
|
|
color="oklch(64.8% 0.2 131.684)"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col justify-center items-center">
|
|
<h3 className="text-xl font-satoshi-bold ">
|
|
{module.questions.length}
|
|
</h3>
|
|
<p className="text-md font-satoshi">Questions</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col justify-center items-center gap-4">
|
|
<div className="w-fit bg-amber-100 p-2 rounded-full">
|
|
<Tag size={30} color="oklch(66.6% 0.179 58.318)" />
|
|
</div>
|
|
<div className="flex flex-col justify-center items-center">
|
|
<h3 className="text-xl font-satoshi-bold ">
|
|
{module.section}
|
|
</h3>
|
|
<p className="text-md font-satoshi">Type</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
</CarouselItem>
|
|
))
|
|
) : (
|
|
<CarouselItem>
|
|
<section className="w-full rounded-4xl p-8 bg-red-100 border border-red-300">
|
|
<h1 className="text-lg text-center font-satoshi-bold text-red-500">
|
|
No modules available.
|
|
</h1>
|
|
</section>
|
|
</CarouselItem>
|
|
)
|
|
) : (
|
|
<CarouselItem>
|
|
<section className="flex flex-col w-full rounded-4xl p-8 bg-yellow-100 border items-center justify-between gap-4">
|
|
<div>
|
|
<Loader size={30} className="transition animate-spin" />
|
|
</div>
|
|
<h1 className="text-center text-xl font-satoshi-bold text-yellow-500">
|
|
Loading...
|
|
</h1>
|
|
</section>
|
|
</CarouselItem>
|
|
)}
|
|
</CarouselContent>
|
|
|
|
<div className="flex justify-center mt-4">
|
|
{practiceSheet?.modules.map((_, index) => (
|
|
<div
|
|
key={index}
|
|
className={`w-2 h-2 mx-1 rounded-full ${
|
|
index + 1 === current ? "bg-purple-500" : "bg-gray-300"
|
|
}`}
|
|
></div>
|
|
))}
|
|
</div>
|
|
</Carousel>
|
|
<section className="w-full rounded-4xl p-8 bg-white border flex flex-col justify-between gap-4">
|
|
<h1 className="text-lg font-satoshi-bold ">
|
|
This practice sheet will help you prepare for the SAT. Take your time
|
|
and do your best!
|
|
</h1>
|
|
</section>
|
|
<Button
|
|
onClick={() => handleStartTest(practiceSheet?.id!)}
|
|
variant="outline"
|
|
className="font-satoshi rounded-3xl w-full text-lg py-8 bg-linear-to-br from-purple-500 to-purple-600 text-white active:bg-linear-to-br active:from-purple-600 active:to-purple-700 active:translate-y-1"
|
|
disabled={!practiceSheet}
|
|
>
|
|
{practiceSheet ? (
|
|
"Start Test"
|
|
) : (
|
|
<div>
|
|
<Loader size={60} className="transition animate-spin" />
|
|
</div>
|
|
)}
|
|
</Button>
|
|
</main>
|
|
);
|
|
};
|