summaryrefslogtreecommitdiff
path: root/src/pages
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-05-21 14:00:28 +0700
committerpolwex <polwex@sortug.com>2025-05-21 14:00:28 +0700
commite839a5f61f0faa21ca8b4bd5767f7575d5e576ee (patch)
tree53e5bcc3977b6ebef687521a7ac387a89aeb21c8 /src/pages
parent4f2bd597beaa778476b84c10b571db1b13524301 (diff)
the card flip animation is legit
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/index.tsx106
-rw-r--r--src/pages/lang/[slug].tsx74
-rw-r--r--src/pages/lesson/[slug].tsx66
-rw-r--r--src/pages/login.tsx29
-rw-r--r--src/pages/logintest/Form.tsx53
-rw-r--r--src/pages/logintest/ServerForm.tsx67
-rw-r--r--src/pages/logintest/funcs.ts24
-rw-r--r--src/pages/logintest/index.tsx24
-rw-r--r--src/pages/parse.tsx26
9 files changed, 445 insertions, 24 deletions
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 82ffd99..48fa46e 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,13 +1,62 @@
import { Link } from "waku";
-import { Counter } from "../components/counter";
+import { Progress } from "@/components/ui/progress";
import { getContextData } from "waku/middleware/context";
-import Main from "../components/Main";
+
+type LanguageChoice = "th" | "en" | "zh" | "ja" | "es" | "fr";
+type LangMeta = { flag: string; name: string };
+const langs: Record<LanguageChoice, LangMeta> = {
+ th: { flag: "🇹🇭", name: "Thai" },
+ en: { flag: "🇬🇧", name: "English" },
+ zh: { flag: "🇨🇳", name: "Chinese" },
+ ja: { flag: "🇯🇵", name: "Japanese" },
+ es: { flag: "🇪🇸", name: "Spanish" },
+ fr: { flag: "🇫🇷", name: "French" },
+};
export default async function HomePage() {
const { user } = getContextData();
- return <Main />;
+ return (
+ <div className="min-h-screen bg-gray-50">
+ <header className="bg-white shadow-sm sticky top-0 z-50">
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+ <div className="flex justify-between items-center h-16">
+ <div className="flex items-center">
+ <h1 className="text-2xl font-bold text-indigo-600">Prosody</h1>
+ </div>
+
+ {/* Desktop Navigation */}
+ <nav className="hidden md:flex space-x-8">
+ <Link to="/">
+ <button
+ className={`py-2 font-medium text-indigo-600 border-b-2 border-indigo-600`}
+ >
+ Home
+ </button>
+ </Link>
+ <Link to="/parse">
+ <button
+ className={`py-2 font-medium text-gray-600 hover:text-indigo-600`}
+ >
+ Analyze Text
+ </button>
+ </Link>
+ </nav>
+ </div>
+ </div>
+
+ {/* Mobile Navigation */}
+ </header>
+ <section className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
+ <h2 className="text-lg"> Your Languages</h2>
+ <LanguageItem lang="en" />
+ <LanguageItem lang="th" />
+ <LanguageItem lang="zh" />
+ <LanguageItem lang="ja" />
+ </section>
+ </div>
+ );
}
const getData = async () => {
@@ -25,3 +74,54 @@ export const getConfig = async () => {
render: "dynamic",
} as const;
};
+
+async function LanguageItem({ lang }: { lang: LanguageChoice }) {
+ return (
+ <Link to={`/lang/${lang}`}>
+ <div className="bg-white rounded-xl h-32 shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
+ <div className="p-6">
+ <div className="flex">
+ <div className="text-lg">{langs[lang].flag}</div>
+ <div className="text-lg">{langs[lang].name}</div>
+ </div>
+ <Progress value={50} className="w-[60%]" />
+ </div>
+ </div>
+ </Link>
+ );
+}
+{
+ /* Language Cards */
+}
+// <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
+// {languages.slice(0, 3).map((lang) => (
+// <div
+// key={lang.id}
+// className="bg-white rounded-xl shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300"
+// >
+// <div className="p-6">
+// <div className="flex items-center justify-between mb-4">
+// <div className="flex items-center">
+// <span className="text-3xl mr-3">{lang.flag}</span>
+// <h3 className="text-xl font-semibold">{lang.name}</h3>
+// </div>
+// <span className="text-xs font-semibold bg-indigo-100 text-indigo-800 px-2 py-1 rounded-full">
+// Beginner
+// </span>
+// </div>
+// <div className="mb-4">
+// <div className="w-full bg-gray-200 rounded-full h-2.5 mb-1">
+// <div
+// className="bg-indigo-600 h-2.5 rounded-full"
+// style={{ width: "40%" }}
+// ></div>
+// </div>
+// <div className="text-right text-sm text-gray-500">40% Complete</div>
+// </div>
+// <button className="w-full py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors duration-300">
+// Continue Learning
+// </button>
+// </div>
+// </div>
+// ))}
+// </div>;
diff --git a/src/pages/lang/[slug].tsx b/src/pages/lang/[slug].tsx
new file mode 100644
index 0000000..11962a5
--- /dev/null
+++ b/src/pages/lang/[slug].tsx
@@ -0,0 +1,74 @@
+import { Button } from "@/components/ui/button";
+import { Link } from "waku";
+
+import { getContextData } from "waku/middleware/context";
+import type { PageProps } from "waku/router";
+import db from "@/lib/db";
+import { Progress } from "@/components/ui/progress";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+
+const flags: Record<string, string> = {
+ th: "🇹🇭",
+ en: "🇬🇧",
+ zh: "🇨🇳",
+ ja: "🇯🇵",
+ es: "🇪🇸",
+ fr: "🇫🇷",
+};
+
+export default async function HomePage(props: PageProps<"/lang/[slug]">) {
+ const lessons = await getData(props.slug);
+ const { user } = getContextData();
+ function commit() {}
+
+ return (
+ <>
+ <section>
+ <h2 className="text-lg">Thai!</h2>
+ {lessons.map((l) => (
+ <Link key={l.id} to={`/lesson/${l.id}`}>
+ <Card>
+ <CardHeader>
+ <CardTitle>
+ <h3>{l.name}</h3>
+ </CardTitle>
+ <CardDescription>
+ <p>{l.description}</p>
+ </CardDescription>
+ </CardHeader>
+ <CardContent>
+ <Progress value={(l.position / l.count) * 100} />
+ </CardContent>
+ </Card>
+ </Link>
+ ))}
+ </section>
+ </>
+ );
+}
+
+const getData = async (lang: string) => {
+ const lessons = db.fetchLanguage(lang);
+
+ return lessons;
+};
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};
+
+async function LanguageItem({ lang }: { lang: string }) {
+ return (
+ <div className="flex">
+ <div className="text-lg">{flags[lang] || ""}</div>
+ </div>
+ );
+}
diff --git a/src/pages/lesson/[slug].tsx b/src/pages/lesson/[slug].tsx
new file mode 100644
index 0000000..6632838
--- /dev/null
+++ b/src/pages/lesson/[slug].tsx
@@ -0,0 +1,66 @@
+import { getHonoContext } from "waku/unstable_hono";
+import { Button } from "@/components/ui/button";
+import { Link } from "waku";
+
+import { getContext, getContextData } from "waku/middleware/context";
+import * as WServer from "waku/server";
+import type { PageProps } from "waku/router";
+import db from "@/lib/db";
+import { useCookies } from "@/lib/server/cookiebridge";
+import Deck from "@/components/Flashcard/Deck";
+
+const flags: Record<string, string> = {
+ th: "🇹🇭",
+ en: "🇬🇧",
+ zh: "🇨🇳",
+ ja: "🇯🇵",
+ es: "🇪🇸",
+ fr: "🇫🇷",
+};
+
+export default async function HomePage(props: PageProps<"/lesson/[slug]">) {
+ const hctx: any = getHonoContext();
+ console.log({ hctx });
+ const ctx = getContext();
+ console.log(ctx.req.headers, "heders");
+ hctx.set("lol", "lmao");
+ const cokis = useCookies();
+ const coki = cokis.getCookie("sorlang");
+ console.log({ coki });
+ console.log({ props });
+ // const { user } = getContextData() as any;
+ // console.log({ user });
+ const user = { id: 2 };
+ const data = await getData(Number(props.slug), user.id);
+ if ("error" in data) return <p>Error</p>;
+ // console.log({ data });
+
+ return (
+ <>
+ <section>
+ <h2 className="text-lg">Thai!</h2>
+ <Deck data={data.ok} />
+ </section>
+ </>
+ );
+}
+
+const getData = async (lesson: number, userId: number) => {
+ const lessons = db.fetchLesson(userId, lesson);
+
+ return lessons;
+};
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};
+
+async function LanguageItem({ lang }: { lang: string }) {
+ return (
+ <div className="flex">
+ <div className="text-lg">{flags[lang] || ""}</div>
+ </div>
+ );
+}
diff --git a/src/pages/login.tsx b/src/pages/login.tsx
index 13d3bd4..d70d365 100644
--- a/src/pages/login.tsx
+++ b/src/pages/login.tsx
@@ -1,31 +1,18 @@
import AuthScreen from "@/components/Login2";
import ProfileScreen from "@/components/Profile";
-import { Link } from "waku";
-import db from "@/lib/db";
import { getContextData } from "waku/middleware/context";
export default async function AuthPage() {
const ctx = getContextData();
- console.log({ ctx });
- const data = await getData();
- if (ctx.user) return <ProfileScreen user={ctx.user as any} />;
- else
- return (
- <div>
- <AuthScreen />
- </div>
- );
+ // console.log({ ctx });
+ // if (ctx.user) return <ProfileScreen user={ctx.user as any} />;
+ // else
+ return (
+ <div>
+ <AuthScreen />
+ </div>
+ );
}
-
-const getData = async () => {
- // const data = {
- // title: "Waku",
- // headline: "Waku",
- // body: "Hello world!",
- // };
- // return data;
-};
-
export const getConfig = async () => {
return {
render: "static",
diff --git a/src/pages/logintest/Form.tsx b/src/pages/logintest/Form.tsx
new file mode 100644
index 0000000..a593acb
--- /dev/null
+++ b/src/pages/logintest/Form.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import { useFormStatus } from "react-dom";
+
+const SubmitButton = () => {
+ const { pending } = useFormStatus();
+ return (
+ <>
+ <button
+ disabled={pending}
+ type="submit"
+ className="hover:bg-slate-50 w-fit rounded-lg bg-white p-2"
+ >
+ {pending ? "Pending..." : "Submit"}
+ </button>
+ </>
+ );
+};
+
+export const Form = ({
+ message,
+ greet,
+}: {
+ message: Promise<string>;
+ greet: (formData: FormData) => Promise<void>;
+}) => (
+ <div style={{ border: "3px blue dashed", margin: "1em", padding: "1em" }}>
+ <p>{message}</p>
+ <form action={greet}>
+ <div className="flex flex-col gap-1 text-left">
+ <div>
+ Name:{" "}
+ <input
+ name="name"
+ required
+ className="invalid:border-red-500 rounded-sm border px-2 py-1"
+ />
+ </div>
+ <div>
+ Email:{" "}
+ <input
+ type="email"
+ name="email"
+ required
+ className="invalid:border-red-500 rounded-sm border px-2 py-1"
+ />
+ </div>
+ <SubmitButton />
+ </div>
+ </form>
+ <h3>This is a client component.</h3>
+ </div>
+);
diff --git a/src/pages/logintest/ServerForm.tsx b/src/pages/logintest/ServerForm.tsx
new file mode 100644
index 0000000..8e629b8
--- /dev/null
+++ b/src/pages/logintest/ServerForm.tsx
@@ -0,0 +1,67 @@
+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 (
+ <form action={submitUserProfile} className="space-y-4">
+ <div style={{ display: "flex", gap: 4, marginBottom: 4 }}>
+ <label htmlFor="name">Full Name</label>
+ <input type="text" name="name" id="name" required />
+ </div>
+
+ <div style={{ display: "flex", gap: 4, marginBottom: 4 }}>
+ <label htmlFor="age">Age</label>
+ <input type="number" name="age" id="age" min="13" max="120" />
+ </div>
+
+ <div style={{ display: "flex", gap: 4, marginBottom: 4 }}>
+ <label htmlFor="favoriteColor">Favorite Color</label>
+ <select name="favoriteColor" id="favoriteColor">
+ <option value="red">Red</option>
+ <option value="blue">Blue</option>
+ <option value="green">Green</option>
+ <option value="purple">Purple</option>
+ <option value="yellow">Yellow</option>
+ </select>
+ </div>
+
+ <div style={{ display: "flex", gap: 4, marginBottom: 4 }}>
+ <label htmlFor="hobby">Favorite Hobby</label>
+ <input
+ type="text"
+ name="hobby"
+ id="hobby"
+ placeholder="e.g. Reading, Gaming, Cooking"
+ />
+ </div>
+
+ <div style={{ display: "flex", gap: 4, marginBottom: 4 }}>
+ <label>
+ <input type="checkbox" name="newsletter" />
+ Subscribe to newsletter
+ </label>
+ </div>
+
+ <button
+ type="submit"
+ className="hover:bg-slate-50 w-fit rounded-lg bg-white p-2"
+ >
+ Save Profile
+ </button>
+ </form>
+ );
+};
diff --git a/src/pages/logintest/funcs.ts b/src/pages/logintest/funcs.ts
new file mode 100644
index 0000000..4ffd5ef
--- /dev/null
+++ b/src/pages/logintest/funcs.ts
@@ -0,0 +1,24 @@
+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
new file mode 100644
index 0000000..df8bc08
--- /dev/null
+++ b/src/pages/logintest/index.tsx
@@ -0,0 +1,24 @@
+import { Form } from "./Form";
+import { getMessage, greet } from "./funcs";
+import { ServerForm } from "./ServerForm";
+
+export default function HomePage() {
+ return (
+ <div className="flex h-full w-full flex-col items-center justify-center gap-8 p-6">
+ <div className="bg-slate-100 rounded-md p-4">
+ <h2 className="text-2xl">Server Form</h2>
+ <ServerForm />
+ </div>
+ <div className="bg-slate-100 rounded-md p-4">
+ <h2 className="text-2xl">Client Form</h2>
+ <Form message={getMessage()} greet={greet} />
+ </div>
+ </div>
+ );
+}
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};
diff --git a/src/pages/parse.tsx b/src/pages/parse.tsx
new file mode 100644
index 0000000..8c0f793
--- /dev/null
+++ b/src/pages/parse.tsx
@@ -0,0 +1,26 @@
+import { Link } from "waku";
+
+import { getContextData } from "waku/middleware/context";
+import ParseForm from "@/components/ParseForm";
+
+export default async function HomePage() {
+ const { user } = getContextData();
+
+ return <ParseForm />;
+}
+
+const getData = async () => {
+ const data = {
+ title: "Waku",
+ headline: "Waku",
+ body: "Hello world!",
+ };
+
+ return data;
+};
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};