summaryrefslogtreecommitdiff
path: root/src/pages
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-05-29 14:08:02 +0700
committerpolwex <polwex@sortug.com>2025-05-29 14:08:02 +0700
commitf243847216279cbd43879de8b5ef6dcceb3a2f1d (patch)
tree1e0be878f164d327762c7bc54f37077d9410dafe /src/pages
parent4ed3994fb0f6a2a09eb6ac433a62daee2fc01686 (diff)
lets see
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/lessons.tsx126
-rw-r--r--src/pages/study.tsx128
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