diff options
Diffstat (limited to 'src/pages')
-rw-r--r-- | src/pages/index.tsx | 106 | ||||
-rw-r--r-- | src/pages/lang/[slug].tsx | 74 | ||||
-rw-r--r-- | src/pages/lesson/[slug].tsx | 66 | ||||
-rw-r--r-- | src/pages/login.tsx | 29 | ||||
-rw-r--r-- | src/pages/logintest/Form.tsx | 53 | ||||
-rw-r--r-- | src/pages/logintest/ServerForm.tsx | 67 | ||||
-rw-r--r-- | src/pages/logintest/funcs.ts | 24 | ||||
-rw-r--r-- | src/pages/logintest/index.tsx | 24 | ||||
-rw-r--r-- | src/pages/parse.tsx | 26 |
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; +}; |