summaryrefslogtreecommitdiff
path: root/src/pages
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-05-15 20:32:25 +0700
committerpolwex <polwex@sortug.com>2025-05-15 20:32:25 +0700
commitfd86dc15734f3b7126d88f0130897c597100e30a (patch)
tree253890a5f0bde7bc460904ce1743581f53a23d5b /src/pages
parent3d4b740e5a512db8fbdd934af2fbc9585fa00f0f (diff)
m
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/_layout.tsx18
-rw-r--r--src/pages/api/proxy.ts2
-rw-r--r--src/pages/picker.tsx29
-rw-r--r--src/pages/test/index.tsx21
-rw-r--r--src/pages/test/product-details-server.tsx340
-rw-r--r--src/pages/zoom.tsx29
6 files changed, 71 insertions, 368 deletions
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index 6d227c9..7f6a434 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -1,9 +1,9 @@
-import '../styles.css';
+import "../styles.css";
-import type { ReactNode } from 'react';
+import type { ReactNode } from "react";
-import { Header } from '../components/header';
-import { Footer } from '../components/footer';
+import { Header } from "../components/header";
+import { Footer } from "../components/footer";
type RootLayoutProps = { children: ReactNode };
@@ -14,19 +14,17 @@ export default async function RootLayout({ children }: RootLayoutProps) {
<div className="font-['Nunito']">
<meta name="description" content={data.description} />
<link rel="icon" type="image/png" href={data.icon} />
- <Header />
- <main className="m-6 flex items-center *:min-h-64 *:min-w-64 lg:m-0 lg:min-h-svh lg:justify-center">
+ <main className="m-6 items-center *:min-h-64 *:min-w-64 lg:m-0 lg:min-h-svh lg:justify-center">
{children}
</main>
- <Footer />
</div>
);
}
const getData = async () => {
const data = {
- description: 'An internet website!',
- icon: '/images/favicon.png',
+ description: "An internet website!",
+ icon: "/images/favicon.png",
};
return data;
@@ -34,6 +32,6 @@ const getData = async () => {
export const getConfig = async () => {
return {
- render: 'static',
+ render: "static",
} as const;
};
diff --git a/src/pages/api/proxy.ts b/src/pages/api/proxy.ts
index 3114f6b..72e5fec 100644
--- a/src/pages/api/proxy.ts
+++ b/src/pages/api/proxy.ts
@@ -1,7 +1,7 @@
// import db from "../../lib/db";
import { z } from "zod";
-export const proxySchema = z.object({
+const proxySchema = z.object({
path: z.string().startsWith("/").optional(),
url: z.string().url("Invalid urladdress"),
body: z.any().optional(),
diff --git a/src/pages/picker.tsx b/src/pages/picker.tsx
new file mode 100644
index 0000000..9cd86f5
--- /dev/null
+++ b/src/pages/picker.tsx
@@ -0,0 +1,29 @@
+import { getContextData } from "waku/middleware/context";
+import App from "@/picker/App";
+
+export default async function HomePage() {
+ const { user } = getContextData();
+
+ return (
+ <div>
+ <h1 className="text-lg text-center">Interactive Language Explorer</h1>
+ <App />;
+ </div>
+ );
+}
+
+const getData = async () => {
+ const data = {
+ title: "Waku",
+ headline: "Waku",
+ body: "Hello world!",
+ };
+
+ return data;
+};
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};
diff --git a/src/pages/test/index.tsx b/src/pages/test/index.tsx
index 35ce5db..4c9325c 100644
--- a/src/pages/test/index.tsx
+++ b/src/pages/test/index.tsx
@@ -1,21 +1,12 @@
// This is a Server Component by default
-import ProductDetailsServer from "./product-details-server";
+import ServerWord from "@/zoom/ServerWord";
import TriggerModalButton from "./trigger-modal-button"; // We'll make this a client component to manage state
export default function SomePage() {
const productIdForModal = "123"; // Or get this dynamically
return (
- <main className="container p-8 mx-auto">
- <h1 className="mb-6 text-3xl font-bold">
- Modal with Server Component Content
- </h1>
- <p>
- This page demonstrates opening a modal whose content is rendered by a
- Server Component. The modal shell (open/close logic) is a Client
- Component.
- </p>
-
+ <div>
{/*
The TriggerModalButton will manage the modal's open/close state.
It will receive the Server Component as a child to pass to ClientModal.
@@ -23,12 +14,8 @@ export default function SomePage() {
<TriggerModalButton
modalTitle={`Product Details for ID: ${productIdForModal}`}
>
- <ProductDetailsServer word={"fantastic"} />
+ <ServerWord word={"fantastic"} lang={"en"} />
</TriggerModalButton>
-
- <div className="mt-8">
- <p>Other content on the page...</p>
- </div>
- </main>
+ </div>
);
}
diff --git a/src/pages/test/product-details-server.tsx b/src/pages/test/product-details-server.tsx
deleted file mode 100644
index 552ff21..0000000
--- a/src/pages/test/product-details-server.tsx
+++ /dev/null
@@ -1,340 +0,0 @@
-// This is a Server Component
-import React from "react";
-import db from "@/lib/db";
-import {
- Card,
- CardHeader,
- CardDescription,
- CardContent,
- CardFooter,
- CardTitle,
-} from "@/components/ui/card";
-import { NLP } from "sortug-ai";
-import {
- BookOpen,
- Volume2,
- Link as LinkIcon,
- ChevronDown,
- ChevronUp,
- Search,
- Info,
- MessageSquareQuote,
- Tags,
- ListTree,
- Lightbulb,
-} from "lucide-react";
-import {
- Example,
- SubSense,
- RelatedEntry,
- Sense,
- WordData,
-} from "@/zoom/logic/types";
-
-export default async function Wordd({
- word,
- lang,
-}: {
- word: string;
- lang: string;
-}) {
- const data = db.fetchWordBySpelling(word, "en");
- console.log({ data });
-
- if (!data) return <p>oh...</p>;
- return (
- <Card>
- <CardHeader>
- <CardTitle>
- <h1 className="text-xl">{word}</h1>
- </CardTitle>
- <CardDescription>
- <IpaDisplay ipaEntries={data.ipa} />
- </CardDescription>
- </CardHeader>
- <CardContent>
- {/* Senses */}
- <h2 className="text-2xl font-semibold text-gray-800 mb-4">
- Meanings & Definitions
- </h2>
- {data.senses.map((sense, index) => (
- <SenseCard key={index} senseData={sense} senseNumber={index + 1} />
- ))}
- </CardContent>
- <CardFooter></CardFooter>
- </Card>
- );
- // return (
- // <div className="p-6">
- // <h3 className="mb-2 text-2xl font-bold">{word}</h3>
- // <p className="mb-1 text-xl text-green-600">${word.}</p>
- // <p className="text-gray-700">{word}</p>
- // <p className="mt-4 text-xs text-gray-500">
- // Content rendered on the server at: {new Date().toLocaleTimeString()}
- // </p>
- // </div>
- // );
-}
-
-// Helper component for IPA display
-const IpaDisplay = ({
- ipaEntries,
-}: {
- ipaEntries: Array<{ ipa: string; tags?: string[] }>;
-}) => {
- if (!ipaEntries || ipaEntries.length === 0) return null;
- return (
- <div className="flex items-center space-x-2 flex-wrap">
- {ipaEntries.map((entry, index) => {
- const tags = entry.tags ? entry.tags : [];
- return (
- <span key={index} className="text-lg text-blue-600 font-serif">
- {entry.ipa}{" "}
- {tags.length > 0 && (
- <span className="text-xs text-gray-500">({tags.join(", ")})</span>
- )}
- </span>
- );
- })}
- <button
- className="p-1 text-blue-500 hover:text-blue-700 transition-colors"
- title="Pronounce"
- // onClick={() => {
- // /* Pronunciation logic would be client-side or a server roundtrip for audio file. */ alert(
- // "Pronunciation feature not implemented for server component.",
- // );
- // }}
- >
- <Volume2 size={20} />
- </button>
- </div>
- );
-};
-
-// Component for displaying examples
-const ExampleDisplay = ({ examples }: { examples: Example[] }) => {
- if (!examples || examples.length === 0) return null;
- return (
- <div className="mt-2">
- <h5 className="text-xs font-semibold text-gray-600 mb-1 flex items-center">
- <MessageSquareQuote size={14} className="mr-1 text-gray-500" />
- Examples:
- </h5>
- <ul className="list-disc list-inside pl-2 space-y-1">
- {examples.map((ex, idx) => (
- <li key={idx} className="text-xs text-gray-600">
- <span className="italic">"{ex.text}"</span>
- {ex.ref && (
- <span className="text-gray-400 text-xxs"> ({ex.ref})</span>
- )}
- {ex.type !== "quote" && (
- <span className="ml-1 text-xxs bg-sky-100 text-sky-700 px-1 rounded-sm">
- {ex.type}
- </span>
- )}
- </li>
- ))}
- </ul>
- </div>
- );
-};
-
-// Component for displaying related terms (synonyms, antonyms, etc.)
-const RelatedTermsDisplay = ({
- terms,
- type,
-}: {
- terms: RelatedEntry[] | undefined;
- type: string;
-}) => {
- if (!terms || terms.length === 0) return null;
- return (
- <div className="mt-1">
- <span className="text-xs font-semibold text-gray-500 capitalize">
- {type}:{" "}
- </span>
- {terms.map((term, idx) => (
- <React.Fragment key={idx}>
- <a
- href={`/search?q=${encodeURIComponent(term.word)}`}
- className="text-xs text-blue-500 hover:text-blue-700 hover:underline"
- >
- {term.word}
- </a>
- {term.source && (
- <span className="text-xxs text-gray-400"> ({term.source})</span>
- )}
- {idx < terms.length - 1 && ", "}
- </React.Fragment>
- ))}
- </div>
- );
-};
-
-// Component for displaying a SubSense
-const SubSenseDisplay = ({
- subSense,
- subSenseNumber,
-}: {
- subSense: SubSense;
- subSenseNumber: number;
-}) => {
- return (
- <div className="mb-3 pl-4 border-l-2 border-indigo-200">
- {subSense.glosses.map((gloss, glossIdx) => (
- <p key={glossIdx} className="text-gray-700 mb-1">
- <span className="font-semibold">
- {subSenseNumber}.{glossIdx + 1}
- </span>{" "}
- {gloss}
- </p>
- ))}
- {subSense.raw_glosses &&
- subSense.raw_glosses.length > 0 &&
- subSense.raw_glosses.join("") !== subSense.glosses.join("") && (
- <p className="text-xs text-gray-500 italic mb-1">
- (Raw: {subSense.raw_glosses.join("; ")})
- </p>
- )}
-
- {subSense.categories && subSense.categories.length > 0 && (
- <div className="mt-1 mb-2">
- <h5 className="text-xs font-semibold text-gray-600 mb-0.5 flex items-center">
- <ListTree size={14} className="mr-1 text-gray-500" />
- Categories:
- </h5>
- <div className="flex flex-wrap gap-1">
- {subSense.categories.map((cat, idx) => (
- <span
- key={idx}
- className="text-xxs bg-gray-100 text-gray-700 px-1.5 py-0.5 rounded-full"
- >
- {cat}
- </span>
- ))}
- </div>
- </div>
- )}
-
- <ExampleDisplay examples={subSense.examples || []} />
- <RelatedTermsDisplay terms={subSense.synonyms} type="Synonyms" />
-
- {subSense.tags && subSense.tags.length > 0 && (
- <div className="mt-2">
- <h5 className="text-xs font-semibold text-gray-600 mb-0.5 flex items-center">
- <Tags size={14} className="mr-1 text-gray-500" />
- Tags:
- </h5>
- <div className="flex flex-wrap gap-1">
- {subSense.tags.map((tag, idx) => (
- <span
- key={idx}
- className="text-xxs bg-purple-100 text-purple-700 px-1.5 py-0.5 rounded-full"
- >
- {tag}
- </span>
- ))}
- </div>
- </div>
- )}
-
- {subSense.links && subSense.links.length > 0 && (
- <div className="mt-2">
- {subSense.links.map(([type, target], linkIdx) => (
- <a
- key={linkIdx}
- href={target} // Assuming target is a full URL or a path
- target="_blank"
- rel="noopener noreferrer"
- className="text-xs text-blue-500 hover:text-blue-700 hover:underline mr-2 inline-flex items-center"
- >
- <LinkIcon size={12} className="mr-1" /> {type}
- </a>
- ))}
- </div>
- )}
- </div>
- );
-};
-
-// Component for individual sense
-const SenseCard = ({
- senseData,
- senseNumber,
-}: {
- senseData: Sense;
- senseNumber: number;
-}) => {
- return (
- <div className="mb-6 p-4 border border-gray-200 rounded-lg shadow-sm bg-white">
- <div className="flex justify-between items-center mb-2">
- <h3 className="text-xl font-semibold text-indigo-700">
- {senseNumber}. {senseData.pos}
- </h3>
- </div>
-
- {senseData.etymology && (
- <details className="mb-3 group">
- <summary className="cursor-pointer flex items-center text-sm text-gray-600 hover:text-indigo-600 transition-colors list-none">
- Etymology
- <ChevronDown size={16} className="ml-1 group-open:hidden" />
- <ChevronUp size={16} className="ml-1 hidden group-open:inline" />
- </summary>
- <p className="mt-1 text-xs text-gray-500 italic bg-gray-50 p-2 rounded">
- {senseData.etymology}
- </p>
- </details>
- )}
-
- {senseData.forms && senseData.forms.length > 0 && (
- <div className="mb-3">
- <h4 className="text-sm font-medium text-gray-700">Forms:</h4>
- <div className="flex flex-wrap gap-2 mt-1">
- {senseData.forms.map((form, idx) => (
- <span
- key={idx}
- className="text-xs bg-gray-100 text-gray-700 px-2 py-1 rounded-full"
- >
- {form.form}{" "}
- {form.tags.length > 0 && `(${form.tags.join(", ")})`}
- </span>
- ))}
- </div>
- </div>
- )}
-
- {senseData.senses.map((subSense, idx) => (
- <SubSenseDisplay
- key={idx}
- subSense={subSense}
- subSenseNumber={senseNumber}
- />
- ))}
-
- {senseData.related && (
- <div className="mt-3 pt-3 border-t border-gray-100">
- <h4 className="text-sm font-medium text-gray-700 mb-1 flex items-center">
- <Lightbulb size={16} className="mr-1 text-gray-500" />
- Related Terms:
- </h4>
- <RelatedTermsDisplay
- terms={senseData.related.related}
- type="Related"
- />
- <RelatedTermsDisplay
- terms={senseData.related.synonyms}
- type="Synonyms (POS)"
- />
- <RelatedTermsDisplay
- terms={senseData.related.antonyms}
- type="Antonyms (POS)"
- />
- <RelatedTermsDisplay
- terms={senseData.related.derived}
- type="Derived"
- />
- </div>
- )}
- </div>
- );
-};
diff --git a/src/pages/zoom.tsx b/src/pages/zoom.tsx
new file mode 100644
index 0000000..d088553
--- /dev/null
+++ b/src/pages/zoom.tsx
@@ -0,0 +1,29 @@
+import { getContextData } from "waku/middleware/context";
+import * as Zoom from "@/zoom";
+
+export default async function HomePage() {
+ const { user } = getContextData();
+
+ return (
+ <div>
+ <h1 className="text-lg text-center">Interactive Language Explorer</h1>
+ <Zoom.App />;
+ </div>
+ );
+}
+
+const getData = async () => {
+ const data = {
+ title: "Waku",
+ headline: "Waku",
+ body: "Hello world!",
+ };
+
+ return data;
+};
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};