Spaces:
Sleeping
Sleeping
| "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> | |
| ); | |
| } | |