diff options
author | polwex <polwex@sortug.com> | 2025-05-29 14:08:02 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-05-29 14:08:02 +0700 |
commit | f243847216279cbd43879de8b5ef6dcceb3a2f1d (patch) | |
tree | 1e0be878f164d327762c7bc54f37077d9410dafe /src/pages | |
parent | 4ed3994fb0f6a2a09eb6ac433a62daee2fc01686 (diff) |
lets see
Diffstat (limited to 'src/pages')
-rw-r--r-- | src/pages/lessons.tsx | 126 | ||||
-rw-r--r-- | src/pages/study.tsx | 128 |
2 files changed, 254 insertions, 0 deletions
diff --git a/src/pages/lessons.tsx b/src/pages/lessons.tsx new file mode 100644 index 0000000..ef8aa49 --- /dev/null +++ b/src/pages/lessons.tsx @@ -0,0 +1,126 @@ +import { useState } from "react"; +import { getState } from "@/lib/db"; +import { getUserLessons, getLessonProgress } from "@/actions/srs"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; + +// This is a server component that gets the initial data +export default async function LessonsPage() { + const state = getState(null); + const userId = state.user?.id; + + // If not logged in, show login required message + if (!userId) { + return ( + <div className="container mx-auto py-8"> + <Card className="p-6 text-center"> + <h1 className="text-2xl font-bold mb-4">Login Required</h1> + <p className="mb-4">You need to be logged in to view your lessons.</p> + <Button asChild> + <a href="/login">Login</a> + </Button> + </Card> + </div> + ); + } + + // Get user lessons data + let lessons; + try { + lessons = await getUserLessons(userId); + } catch (error) { + console.error("Error fetching lessons:", error); + } + + return ( + <div className="container mx-auto py-8"> + <div className="flex justify-between items-center mb-6"> + <h1 className="text-3xl font-bold">Your Lessons</h1> + <Button asChild> + <a href="/">Back to Home</a> + </Button> + </div> + + {!lessons || lessons.length === 0 ? ( + <NoLessonsFound /> + ) : ( + <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> + {lessons.map((lesson) => ( + <LessonCard key={lesson.id} lesson={lesson} userId={userId} /> + ))} + </div> + )} + </div> + ); +} + +// Component to display when no lessons are found +function NoLessonsFound() { + return ( + <Card className="p-6 text-center"> + <h2 className="text-xl font-semibold mb-3">No Lessons Found</h2> + <p className="text-gray-500 mb-4"> + You don't have any lessons available yet. + </p> + </Card> + ); +} + +// Component to display a lesson card +function LessonCard({ lesson, userId }: { lesson: any; userId: number }) { + const [isHovered, setIsHovered] = useState(false); + + // Calculate progress percentage + const progressPercentage = lesson.progress || 0; + + // Determine progress color + const getProgressColor = (percentage: number) => { + if (percentage >= 80) return "bg-green-500"; + if (percentage >= 50) return "bg-yellow-500"; + return "bg-blue-500"; + }; + + return ( + <Card + className="overflow-hidden transition-shadow duration-300 hover:shadow-lg" + onMouseEnter={() => setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + <div className="p-6"> + <h3 className="text-xl font-bold mb-2">{lesson.name}</h3> + <p className="text-gray-500 text-sm mb-4 line-clamp-2"> + {lesson.description || "No description available."} + </p> + + <div className="flex items-center justify-between mb-4"> + <div className="text-sm"> + <span className="font-medium">{lesson.masteredCards}</span> + <span className="text-gray-500"> / {lesson.totalCards} cards</span> + </div> + <div className="text-sm"> + <span className="font-medium text-amber-600"> + {lesson.dueCards} due + </span> + </div> + </div> + + <div className="mb-6"> + <Progress + value={progressPercentage} + className={`h-2 ${getProgressColor(progressPercentage)}`} + /> + </div> + + <div className="flex gap-3"> + <Button asChild className="flex-1"> + <a href={`/study?lessonId=${lesson.id}`}>Study Now</a> + </Button> + <Button variant="outline" asChild className="flex-1"> + <a href={`/lesson/${lesson.id}`}>View Details</a> + </Button> + </div> + </div> + </Card> + ); +}
\ No newline at end of file diff --git a/src/pages/study.tsx b/src/pages/study.tsx new file mode 100644 index 0000000..db7dde7 --- /dev/null +++ b/src/pages/study.tsx @@ -0,0 +1,128 @@ +import { useState } from "react"; +import { getState } from "@/lib/db"; +import { startStudySession } from "@/actions/srs"; +import StudySession from "@/components/Flashcard/StudySession"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +// This is a server component that gets the initial data +export default async function StudyPage({ searchParams }: { searchParams: { lessonId?: string } }) { + const state = getState(null); + const userId = state.user?.id; + + // If not logged in, show login required message + if (!userId) { + return ( + <div className="container mx-auto py-8"> + <Card className="p-6 text-center"> + <h1 className="text-2xl font-bold mb-4">Login Required</h1> + <p className="mb-4">You need to be logged in to use the study session feature.</p> + <Button asChild> + <a href="/login">Login</a> + </Button> + </Card> + </div> + ); + } + + const lessonId = searchParams.lessonId ? parseInt(searchParams.lessonId, 10) : null; + + // If no lesson ID provided, show lesson selector + if (!lessonId) { + return <LessonSelector userId={userId} />; + } + + // Get initial data for the study session + let initialData; + try { + initialData = await startStudySession(userId, lessonId, true); + } catch (error) { + console.error("Error starting study session:", error); + } + + return ( + <div className="container mx-auto py-8"> + <StudySession + userId={userId} + lessonId={lessonId} + initialData={initialData && !('error' in initialData) ? initialData : undefined} + /> + </div> + ); +} + +// Client component for selecting a lesson +function LessonSelector({ userId }: { userId: number }) { + const [lessonId, setLessonId] = useState<string>(""); + + return ( + <div className="container mx-auto py-8"> + <Card className="p-6 max-w-md mx-auto"> + <h1 className="text-2xl font-bold mb-6">Start Study Session</h1> + + <form action={`/study?lessonId=${lessonId}`}> + <div className="space-y-4"> + <div> + <Label htmlFor="lessonId">Lesson ID</Label> + <Input + id="lessonId" + value={lessonId} + onChange={(e) => setLessonId(e.target.value)} + placeholder="Enter lesson ID" + type="number" + required + /> + </div> + + <Button type="submit" className="w-full"> + Start Study Session + </Button> + </div> + </form> + + <div className="mt-6 pt-6 border-t border-gray-200"> + <h2 className="text-lg font-medium mb-3">Available Lessons</h2> + <p className="text-sm text-gray-500 mb-4"> + Here are some example lesson IDs you can use: + </p> + <div className="flex flex-wrap gap-2"> + <Button + variant="outline" + size="sm" + onClick={() => setLessonId("1")} + > + Lesson 1 + </Button> + <Button + variant="outline" + size="sm" + onClick={() => setLessonId("2")} + > + Lesson 2 + </Button> + <Button + variant="outline" + size="sm" + onClick={() => setLessonId("5")} + > + Lesson 5 + </Button> + </div> + + <div className="mt-4"> + <Button + variant="ghost" + size="sm" + asChild + className="text-blue-500" + > + <a href="/">Back to Home</a> + </Button> + </div> + </div> + </Card> + </div> + ); +}
\ No newline at end of file |