tahamajs/IE / Projects /front /src /components /admin /RagDocTable.tsx
tahamajs's picture
download
raw
3.69 kB
import { useState, useEffect } from "react";
import api from "@/lib/api";
import { Trash2, Search, Loader2 } from "lucide-react";
import { toast } from "react-toastify";
interface RagDocument {
id: string;
title: string;
fileType: string;
addedAt: string;
source: "upload" | "article";
}
export function RagDocTable() {
const [docs, setDocs] = useState<RagDocument[]>([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
const fetchDocs = async () => {
setLoading(true);
try {
const res = await api.get("/admin/rag/documents");
setDocs(res.data || []);
} catch {
toast.error("Failed to load RAG documents");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchDocs();
}, []);
const deleteDoc = async (id: string) => {
if (!confirm("Remove this document from RAG index?")) return;
try {
await api.delete(`/documents/${id}/rag-delete`);
toast.success("Document removed");
fetchDocs();
} catch {
toast.error("Delete failed");
}
};
const filteredDocs = docs.filter((doc) => doc.title.toLowerCase().includes(search.toLowerCase()));
return (
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm overflow-hidden">
<div className="p-4 border-b">
<div className="relative max-w-sm">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
<input
type="text"
placeholder="Search by title..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-9 pr-3 py-2 border rounded-lg dark:bg-gray-700 text-sm"
/>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-gray-50 dark:bg-gray-700/50 border-b">
<tr>
<th className="px-4 py-3 text-left font-semibold">Title</th>
<th className="px-4 py-3 text-left font-semibold">Type</th>
<th className="px-4 py-3 text-left font-semibold">Source</th>
<th className="px-4 py-3 text-left font-semibold">Added</th>
<th className="px-4 py-3 text-left font-semibold">Actions</th>
</tr>
</thead>
<tbody>
{loading ? (
<tr>
<td colSpan={5} className="text-center py-8">
<Loader2 className="animate-spin mx-auto" />
</td>
</tr>
) : filteredDocs.length === 0 ? (
<tr>
<td colSpan={5} className="text-center py-8 text-gray-400">
No RAG documents found
</td>
</tr>
) : (
filteredDocs.map((doc) => (
<tr key={doc.id} className="border-b hover:bg-gray-50 dark:hover:bg-gray-700/30">
<td className="px-4 py-3 font-medium">{doc.title}</td>
<td className="px-4 py-3">{doc.fileType?.toUpperCase() || "unknown"}</td>
<td className="px-4 py-3">{doc.source}</td>
<td className="px-4 py-3">{new Date(doc.addedAt).toLocaleDateString()}</td>
<td className="px-4 py-3">
<button onClick={() => deleteDoc(doc.id)} className="p-1 text-gray-500 hover:text-red-500" title="Remove">
<Trash2 className="w-4 h-4" />
</button>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
);
}

Xet Storage Details

Size:
3.69 kB
·
Xet hash:
6af7e2bd6d17ae6dbb55828db45d49471ca28503d93b2bea63a1de6947c1b0eb

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.