You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
5.5 KiB

"use client";
import { useEffect, useState } from "react";
import { useAuth } from "@/lib/auth-context";
import { useRouter } from "next/navigation";
interface UploadedFile {
id: string;
original_name: string;
file_path: string;
upload_status: string;
created_at: string;
}
export default function RendersPage() {
const { isAuthenticated, user } = useAuth();
const router = useRouter();
const [files, setFiles] = useState<UploadedFile[]>([]);
const [loading, setLoading] = useState(true);
const [authInitialized, setAuthInitialized] = useState(false);
const [deletingId, setDeletingId] = useState<string | null>(null);
useEffect(() => {
const timer = setTimeout(() => setAuthInitialized(true), 100);
return () => clearTimeout(timer);
}, []);
useEffect(() => {
if (!authInitialized) return;
if (!isAuthenticated || !user) {
router.push("/login?redirect=/renders");
return;
}
fetchFiles();
// eslint-disable-next-line
}, [authInitialized, isAuthenticated, user]);
async function fetchFiles() {
try {
const res = await fetch("/api/renders/in", {
headers: { "user-id": user?.id },
});
const data = await res.json();
setFiles(data.files || []);
} catch {
setFiles([]);
} finally {
setLoading(false);
}
}
async function handleDelete(id: string) {
if (!user) return;
const ok = confirm("Delete this file? This cannot be undone.");
if (!ok) return;
setDeletingId(id);
try {
const res = await fetch("/api/renders/in", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"user-id": user.id,
},
body: JSON.stringify({ id }),
});
const data = await res.json();
if (!res.ok) throw new Error(data?.error || "Delete failed");
await fetchFiles();
} catch (e) {
alert((e as any).message || "Delete failed");
} finally {
setDeletingId(null);
}
}
if (!authInitialized || !isAuthenticated) {
return (
<div className="min-h-screen flex items-center justify-center">
Loading...
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="max-w-5xl mx-auto px-4">
<h1 className="text-4xl font-bold text-gray-900 mb-8">My Renders</h1>
<section className="mb-12">
<h2 className="text-2xl font-bold text-orange-600 mb-4">
Uploaded Files
</h2>
{loading ? (
<div>Loading...</div>
) : files.length === 0 ? (
<div className="text-gray-500">No files uploaded yet.</div>
) : (
<table className="w-full bg-white rounded-lg shadow-md">
<thead>
<tr className="bg-orange-50">
<th className="p-3 text-left font-semibold">Filename</th>
<th className="p-3 text-left font-semibold">Status</th>
<th className="p-3 text-left font-semibold">Uploaded At</th>
<th className="p-3 text-left font-semibold">Download</th>
<th className="p-3 text-left font-semibold">Actions</th>
</tr>
</thead>
<tbody>
{files.map((f) => (
<tr key={f.id} className="border-t">
<td className="p-3">{f.original_name}</td>
<td className="p-3 capitalize">
<span
className={`px-2 py-1 rounded-full text-xs font-bold ${
f.upload_status === "COMPLETE"
? "bg-green-100 text-green-800"
: "bg-gray-200 text-gray-600"
}`}
>
{f.upload_status}
</span>
</td>
<td className="p-3">
{new Date(f.created_at).toLocaleString()}
</td>
<td className="p-3">
<a
href={
f.file_path
? `/uploads/${f.file_path.split("/").pop()}`
: "#"
}
className="underline text-blue-700"
target="_blank"
rel="noopener noreferrer"
download={f.original_name}
>
Download
</a>
</td>
<td className="p-3">
<button
className="px-3 py-1 text-sm rounded-md bg-red-500 text-white hover:bg-red-600 disabled:bg-red-300"
disabled={deletingId === f.id}
onClick={() => handleDelete(f.id)}
>
{deletingId === f.id ? "Deleting..." : "Delete"}
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</section>
<section>
<h2 className="text-2xl font-bold text-gray-600 mb-4">
Rendered Files (outputs)
</h2>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center text-gray-400">
Placeholder: Your rendered products will appear here in the future!
</div>
</section>
</div>
</div>
);
}