feat(pages): add pretest screen
This commit is contained in:
199
src/pages/student/practice/Pretest.tsx
Normal file
199
src/pages/student/practice/Pretest.tsx
Normal file
@ -0,0 +1,199 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { 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, Tag } from "lucide-react";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
type CarouselApi,
|
||||
} from "../../../components/ui/carousel";
|
||||
import { Button } from "../../../components/ui/button";
|
||||
|
||||
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 [practiceSheet, setPracticeSheet] = useState<PracticeSheet | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
function handleStartTest(sheetId: string) {
|
||||
console.log("Starting test for Practice Sheet. ID: ", sheetId);
|
||||
}
|
||||
|
||||
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>
|
||||
<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>
|
||||
<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">
|
||||
<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"
|
||||
>
|
||||
Start Test
|
||||
</Button>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user