summaryrefslogtreecommitdiff
path: root/src/components
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/components
parent3d4b740e5a512db8fbdd934af2fbc9585fa00f0f (diff)
m
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Main.tsx25
-rw-r--r--src/components/Modal.tsx55
-rw-r--r--src/components/ui/dialog.tsx133
-rw-r--r--src/components/zoom/Entry.tsx13
4 files changed, 208 insertions, 18 deletions
diff --git a/src/components/Main.tsx b/src/components/Main.tsx
index 2157a91..3e6f3e7 100644
--- a/src/components/Main.tsx
+++ b/src/components/Main.tsx
@@ -19,6 +19,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { Loader2 } from "lucide-react"; // Loading spinner
+import { useRouter } from "waku";
const SorlangPage: React.FC = () => {
const [textValue, setTextValue] = useState<string>("");
@@ -82,24 +83,30 @@ const SorlangPage: React.FC = () => {
};
}, [handlePaste]);
- const handleProcessText = async () => {
- setIsAnalyzing(true);
- const text = textValue.trim();
- if (!text) {
- alert("Text area is empty!");
- return;
- }
+ const router = useRouter();
+ async function fetchNLP(text: string, app: "spacy" | "stanza") {
const opts = {
method: "POST",
headers: { "Content-type": "application/json" },
- body: JSON.stringify({ text, app: "spacy" }),
+ body: JSON.stringify({ text, app }),
};
const res = await fetch("/api/nlp", opts);
const j = await res.json();
console.log("j", j);
if ("ok" in j) {
- console.log("good");
+ sessionStorage.setItem(`${app}res`, JSON.stringify(j.ok));
+ }
+ }
+
+ const handleProcessText = async () => {
+ setIsAnalyzing(true);
+ const text = textValue.trim();
+ if (!text) {
+ alert("Text area is empty!");
+ return;
}
+ await Promise.all([fetchNLP(text, "spacy"), fetchNLP(text, "stanza")]);
+ router.push("/zoom");
setIsAnalyzing(false);
};
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
new file mode 100644
index 0000000..4c52caa
--- /dev/null
+++ b/src/components/Modal.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+import { useState, ReactNode, useEffect } from "react";
+
+interface ClientModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ children: ReactNode; // This will receive the Server Component's output
+ title?: string;
+}
+
+export default function ClientModal({
+ isOpen,
+ onClose,
+ children,
+}: ClientModalProps) {
+ // Optional: Prevent body scroll when modal is open
+ useEffect(() => {
+ if (isOpen) {
+ document.body.style.overflow = "hidden";
+ } else {
+ document.body.style.overflow = "unset";
+ }
+ return () => {
+ document.body.style.overflow = "unset";
+ };
+ }, [isOpen]);
+
+ if (!isOpen) {
+ return null;
+ }
+
+ return (
+ <div
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
+ onClick={onClose} // Close on overlay click
+ >
+ <div
+ className="p-6 bg-white rounded-lg shadow-xl w-11/12 max-w-lg"
+ onClick={(e) => e.stopPropagation()} // Prevent click from closing modal if clicking inside content
+ >
+ <button
+ onClick={onClose}
+ className="text-gray-500 hover:text-gray-700"
+ aria-label="Close modal"
+ >
+ × {/* A simple 'X' close button */}
+ </button>
+ <div>
+ {children} {/* Server Component content will be rendered here */}
+ </div>
+ </div>
+ </div>
+ );
+}
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..981e999
--- /dev/null
+++ b/src/components/ui/dialog.tsx
@@ -0,0 +1,133 @@
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { XIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Dialog({
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Root>) {
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
+}
+
+function DialogTrigger({
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
+}
+
+function DialogPortal({
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
+}
+
+function DialogClose({
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Close>) {
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
+}
+
+function DialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
+ return (
+ <DialogPrimitive.Overlay
+ data-slot="dialog-overlay"
+ className={cn(
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function DialogContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Content>) {
+ return (
+ <DialogPortal data-slot="dialog-portal">
+ <DialogOverlay />
+ <DialogPrimitive.Content
+ data-slot="dialog-content"
+ className={cn(
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
+ className
+ )}
+ {...props}
+ >
+ {children}
+ <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
+ <XIcon />
+ <span className="sr-only">Close</span>
+ </DialogPrimitive.Close>
+ </DialogPrimitive.Content>
+ </DialogPortal>
+ )
+}
+
+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+ <div
+ data-slot="dialog-header"
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
+ {...props}
+ />
+ )
+}
+
+function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+ <div
+ data-slot="dialog-footer"
+ className={cn(
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function DialogTitle({
+ className,
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Title>) {
+ return (
+ <DialogPrimitive.Title
+ data-slot="dialog-title"
+ className={cn("text-lg leading-none font-semibold", className)}
+ {...props}
+ />
+ )
+}
+
+function DialogDescription({
+ className,
+ ...props
+}: React.ComponentProps<typeof DialogPrimitive.Description>) {
+ return (
+ <DialogPrimitive.Description
+ data-slot="dialog-description"
+ className={cn("text-muted-foreground text-sm", className)}
+ {...props}
+ />
+ )
+}
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+}
diff --git a/src/components/zoom/Entry.tsx b/src/components/zoom/Entry.tsx
index a60c75c..9e6eed9 100644
--- a/src/components/zoom/Entry.tsx
+++ b/src/components/zoom/Entry.tsx
@@ -1,14 +1,9 @@
-"use client";
-import { Zoom } from "prosody-ui";
+import * as Zoom from "@/zoom";
+import { useEffect, useState } from "react";
import { NLP } from "sortug-ai";
-type Props = { text: string; doc: NLP.Spacy.SpacyRes };
-function ZoomText(props: Props) {
- return (
- <div>
- <Zoom.FullText {...props} />
- </div>
- );
+function ZoomText() {
+ return <div></div>;
}
export default ZoomText;