From a03c92dc82ad527d7da6bbaa3c43000e2e5f0e69 Mon Sep 17 00:00:00 2001 From: polwex Date: Thu, 29 May 2025 16:25:31 +0700 Subject: better better --- src/pages/logintest/Form.tsx | 53 ---- src/pages/logintest/ServerForm.tsx | 67 ------ src/pages/logintest/funcs.ts | 24 -- src/pages/logintest/index.tsx | 6 +- src/pages/study.tsx | 480 +++++++++++++++++++++++++++++++++++++ 5 files changed, 483 insertions(+), 147 deletions(-) delete mode 100644 src/pages/logintest/Form.tsx delete mode 100644 src/pages/logintest/ServerForm.tsx delete mode 100644 src/pages/logintest/funcs.ts create mode 100644 src/pages/study.tsx (limited to 'src/pages') diff --git a/src/pages/logintest/Form.tsx b/src/pages/logintest/Form.tsx deleted file mode 100644 index a593acb..0000000 --- a/src/pages/logintest/Form.tsx +++ /dev/null @@ -1,53 +0,0 @@ -"use client"; - -import { useFormStatus } from "react-dom"; - -const SubmitButton = () => { - const { pending } = useFormStatus(); - return ( - <> - - - ); -}; - -export const Form = ({ - message, - greet, -}: { - message: Promise; - greet: (formData: FormData) => Promise; -}) => ( -
-

{message}

-
-
-
- Name:{" "} - -
-
- Email:{" "} - -
- -
-
-

This is a client component.

-
-); diff --git a/src/pages/logintest/ServerForm.tsx b/src/pages/logintest/ServerForm.tsx deleted file mode 100644 index 8e629b8..0000000 --- a/src/pages/logintest/ServerForm.tsx +++ /dev/null @@ -1,67 +0,0 @@ -async function submitUserProfile(formData: FormData) { - "use server"; - const name = formData.get("name"); - const age = formData.get("age"); - const favoriteColor = formData.get("favoriteColor"); - const hobby = formData.get("hobby"); - const isSubscribed = formData.get("newsletter") === "on"; - - console.log({ - name, - age, - favoriteColor, - hobby, - isSubscribed, - }); -} - -export const ServerForm = () => { - return ( -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
- - -
- ); -}; diff --git a/src/pages/logintest/funcs.ts b/src/pages/logintest/funcs.ts deleted file mode 100644 index 4ffd5ef..0000000 --- a/src/pages/logintest/funcs.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { readFile, writeFile } from "node:fs/promises"; -import { unstable_rerenderRoute } from "waku/router/server"; - -export const getMessage = async () => { - const data = await readFile("./message.txt", "utf8"); - return data; -}; - -export const greet = async (formData: FormData) => { - "use server"; - // simulate a slow server response - await new Promise((resolve) => setTimeout(resolve, 1000)); - const currentData = await getMessage(); - await writeFile( - "./message.txt", - currentData + "\n" + formData.get("name") + " from server!", - ); - unstable_rerenderRoute("/"); -}; - -export const increment = async (count: number) => { - "use server"; - return count + 1; -}; diff --git a/src/pages/logintest/index.tsx b/src/pages/logintest/index.tsx index df8bc08..5707c69 100644 --- a/src/pages/logintest/index.tsx +++ b/src/pages/logintest/index.tsx @@ -1,6 +1,6 @@ -import { Form } from "./Form"; -import { getMessage, greet } from "./funcs"; -import { ServerForm } from "./ServerForm"; +import { Form } from "../../components/logintest/Form"; +import { getMessage, greet } from "../../actions/logintest/funcs"; +import { ServerForm } from "../../components/logintest/ServerForm"; export default function HomePage() { return ( diff --git a/src/pages/study.tsx b/src/pages/study.tsx new file mode 100644 index 0000000..f9450a7 --- /dev/null +++ b/src/pages/study.tsx @@ -0,0 +1,480 @@ +import { getContextData } from "waku/middleware/context"; +import { Link } from "waku"; +import { getUserLessons, getUserStudyStats } from "@/actions/srs"; +import { + BookOpen, + GraduationCap, + Clock, + Star, + ChevronRight, + BrainCircuit, + Flame, + Layers, + CalendarDays +} from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Badge } from "@/components/ui/badge"; +import Navbar from "@/components/Navbar"; + +// This is a server component that gets the initial data +export default async function StudyPage() { + const { user } = getContextData() as any; + + // Redirect to login if not authenticated + if (!user) { + return ( +
+ +
+ +

Login Required

+

+ You need to be logged in to access your study dashboard. +

+
+ + +
+
+
+
+ ); + } + + // Fetch user study data + let userStats; + let userLessons; + + try { + // Get user stats and lessons in parallel + [userStats, userLessons] = await Promise.all([ + getUserStudyStats(user.id), + getUserLessons(user.id) + ]); + } catch (error) { + console.error("Error fetching study data:", error); + userStats = { + totalCards: 0, + masteredCards: 0, + dueCards: 0, + averageEaseFactor: 2.5, + successRate: 0, + streakDays: 0 + }; + userLessons = []; + } + + // Calculate overall progress + const overallProgress = userStats.totalCards > 0 + ? Math.round((userStats.masteredCards / userStats.totalCards) * 100) + : 0; + + // Sort lessons by different criteria + const dueLessons = [...userLessons].sort((a, b) => b.dueCards - a.dueCards).filter(l => l.dueCards > 0); + const inProgressLessons = userLessons.filter(lesson => lesson.progress > 0 && lesson.progress < 100); + const recentLessons = [...userLessons].sort((a, b) => b.id - a.id).slice(0, 4); + + return ( +
+ + +
+ {/* Dashboard Header */} +
+

Study Dashboard

+

+ Track your progress, review due cards, and continue your language learning journey +

+
+ + {/* Stats Overview */} +
+ + +
+
+ +
+ + Total + +
+

{userStats.totalCards}

+

Total Cards

+
+
+ + + +
+
+ +
+ + {Math.round(userStats.masteredCards / Math.max(userStats.totalCards, 1) * 100)}% + +
+

{userStats.masteredCards}

+

Mastered Cards

+
+
+ + + +
+
+ +
+ 0 ? "bg-red-100 text-red-800" : "bg-gray-100"}> + {userStats.dueCards > 0 ? "Due Today" : "All Caught Up"} + +
+

{userStats.dueCards}

+

Cards Due for Review

+
+
+ + + +
+
+ +
+ + Streak + +
+

{userStats.streakDays}

+

Days in a Row

+
+
+
+ + {/* Main Content Tabs */} + + + All Lessons + + Due for Review {dueLessons.length > 0 && `(${dueLessons.length})`} + + In Progress + Study Stats + + + {/* All Lessons Tab */} + +
+ {userLessons.length > 0 ? ( + userLessons.map((lesson) => ( + + )) + ) : ( +
+

No lessons available yet

+

+ Start your language learning journey by adding lessons +

+ +
+ )} +
+
+ + {/* Due for Review Tab */} + + {dueLessons.length > 0 ? ( +
+ {dueLessons.map((lesson) => ( + + ))} +
+ ) : ( + + +
+ +
+

All caught up!

+

+ You don't have any cards due for review right now. Check back later or start a new lesson. +

+ +
+
+ )} +
+ + {/* In Progress Tab */} + + {inProgressLessons.length > 0 ? ( +
+ {inProgressLessons.map((lesson) => ( + + ))} +
+ ) : ( + + +
+ +
+

No lessons in progress

+

+ Start learning by selecting a lesson from the All Lessons tab. +

+ +
+
+ )} +
+ + {/* Stats Tab */} + +
+ + + Overall Progress + Your language learning journey progress + + +
+
+ Progress + {overallProgress}% +
+ +
+ +
+
+ Success Rate + {Math.round(userStats.successRate * 100)}% +
+
+ Average Ease + {userStats.averageEaseFactor.toFixed(1)} +
+
+ Total Cards + {userStats.totalCards} +
+
+ Mastered + {userStats.masteredCards} +
+
+
+
+ + + + Study Streak + Keep the momentum going! + + +
+
+ +
+
{userStats.streakDays}
+
Days in a row
+ +
+
+
+ + + + Recent Activity + Your latest learning progress + + +
+ {recentLessons.map(lesson => ( +
+
+
+ +
+
+
{lesson.name}
+
+ {lesson.description || `Lesson ${lesson.id}`} +
+
+
+
+
+
{Math.round(lesson.progress)}%
+
+ {lesson.masteredCards} / {lesson.totalCards} cards +
+
+ +
+
+ ))} +
+
+
+
+
+
+ + {/* Quick Access Section */} +
+

Quick Access

+
+ + +
+ +
+
+

Start New Lesson

+

Add text to study

+
+ +
+
+ + + +
+ +
+
+

Review Due Cards

+

{userStats.dueCards} cards waiting

+
+ +
+
+ + + +
+ +
+
+

Track Progress

+

View detailed statistics

+
+ +
+
+
+
+
+
+ ); +} + +// Lesson Card Component +function LessonCard({ + lesson, + showDueCardsBadge = false, + showProgress = false +}: { + lesson: any; + showDueCardsBadge?: boolean; + showProgress?: boolean; +}) { + return ( + + +
+
+ {lesson.name} + + {lesson.description || `Lesson ${lesson.id}`} + +
+ {showDueCardsBadge && lesson.dueCards > 0 && ( + + {lesson.dueCards} due + + )} +
+
+ +
+ {lesson.masteredCards} of {lesson.totalCards} mastered + {Math.round(lesson.progress)}% complete +
+ + + {showProgress && ( +
+
+ Due Cards + {lesson.dueCards} +
+
+ Mastered + {lesson.masteredCards} +
+
+ )} +
+ + + +
+ ); +} + +export const getConfig = async () => { + return { + render: "dynamic", + } as const; +}; -- cgit v1.2.3