neuroread/components/UploadZone.tsx

123 lines
3.9 KiB
TypeScript

"use client";
import { createClient } from "@/utils/supabase/client";
import { CloudUpload, LoaderCircle } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import { SSE } from "sse.js";
export default function UploadZone({ user }: { user?: { id: string } }) {
const supabase = createClient();
const [uploading, setUploading] = useState(false);
const [status, setStatus] = useState("");
const onUpload = async (file: File) => {
setUploading(true);
setStatus("Uploading...");
const { data, error } = await supabase.auth.getSession();
if (error) {
toast.error("Failed to get user session.");
setUploading(false);
return;
}
const body = new FormData();
body.append("file", file);
body.append("jwt", data.session?.access_token || "");
const edgeFunctionUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/functions/v1/process-document`;
// Start listening to the SSE stream
const eventSource = new SSE(edgeFunctionUrl, {
payload: body,
headers: {
apikey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
Authorization: `Bearer ${process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY}`,
"Content-Type": "application/json",
},
});
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("SSE Message:", data);
if (data.message) {
setStatus(data.message);
}
};
eventSource.addEventListener("status", (event) => {
const data = JSON.parse(event.data);
console.log("Status Event:", data);
setStatus(data.message);
});
eventSource.addEventListener("error", (event) => {
console.error("SSE Error:", event);
toast.error("An error occurred while processing the document.");
setUploading(false);
eventSource.close();
});
eventSource.addEventListener("complete", (event) => {
const data = JSON.parse(event.data);
console.log("Processing Complete:", data);
toast.success("Document processing complete!");
setUploading(false);
eventSource.close();
});
// Invoke the serverless function
supabase.functions.invoke("process-document", {
body,
method: "POST",
});
toast.info(
"Document is being processed in the background. You will be notified when it's ready."
);
};
return (
<div className="flex flex-1 flex-col gap-4 px-4 py-10">
<div className="flex items-center justify-center w-full">
<label
htmlFor="dropzone-file"
className="flex flex-col items-center justify-center w-full h-64 border-2 border-muted border-dashed rounded-lg cursor-pointer bg-muted/50"
>
{uploading ? (
<div className="flex flex-col items-center justify-center pt-5 pb-5">
<LoaderCircle className="w-10 h-10 mb-4 text-slate-400 animate-spin" />
<p className="mb-2 text-sm text-slate-400">{status}</p>
</div>
) : (
<>
<div className="flex flex-col items-center justify-center pt-5 pb-5">
<CloudUpload className="w-10 h-10 mb-4 text-slate-400" />
<p className="mb-2 text-sm text-slate-400">
<span className="font-semibold">Click to upload</span> or drag
and drop
</p>
</div>
</>
)}
<input
id="dropzone-file"
type="file"
className="hidden"
accept="application/pdf"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
onUpload(file);
}
}}
/>
</label>
</div>
</div>
);
}