Luminaria / frontend /components /file-list.tsx
senlinyy's picture
feat: completed initial dev
4b81334
Raw
History Blame Contribute Delete
2.81 kB
"use client";
import { useEffect, useState, useCallback } from "react";
import { Trash2, FileText, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
export function FileList({ refreshKey }: { refreshKey: number }) {
const [files, setFiles] = useState<string[] | null>(null);
const [error, setError] = useState<string | null>(null);
const [deleting, setDeleting] = useState<string | null>(null);
const load = useCallback(async () => {
setError(null);
try {
const r = await fetch("/api/files");
if (!r.ok) throw new Error(await r.text());
const data = await r.json();
setFiles(data.files ?? []);
} catch (e) {
setError((e as Error).message);
}
}, []);
useEffect(() => {
load();
}, [load, refreshKey]);
async function remove(name: string) {
if (!confirm(`Delete ${name}? This removes it from the dataset and the index.`))
return;
setDeleting(name);
try {
const r = await fetch("/api/delete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename: name }),
});
if (!r.ok) throw new Error(await r.text());
await load();
} catch (e) {
setError((e as Error).message);
} finally {
setDeleting(null);
}
}
return (
<div className="space-y-3">
<div className="flex items-center justify-between">
<h2 className="text-sm font-semibold">Course materials</h2>
<Button variant="ghost" size="sm" onClick={load}>
<RefreshCw className="mr-1 h-3.5 w-3.5" /> Refresh
</Button>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
{files === null ? (
<p className="text-sm text-muted-foreground">Loading…</p>
) : files.length === 0 ? (
<p className="text-sm text-muted-foreground">No files uploaded yet.</p>
) : (
<ul className="divide-y rounded-lg border">
{files.map((name) => (
<li
key={name}
className="flex items-center justify-between gap-3 px-4 py-2.5"
>
<div className="flex min-w-0 items-center gap-2">
<FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate text-sm">{name}</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => remove(name)}
disabled={deleting === name}
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
<Trash2 className="h-4 w-4" />
</Button>
</li>
))}
</ul>
)}
</div>
);
}