import { useEffect, useMemo, useState, type ReactElement } from "react"; import { useNavigate } from "react-router-dom"; import { Layout } from "@/components/layout/Layout"; import { DataTable, FilterDropdown, Pagination, PrimaryButton, type Column, } from "@/components/shared"; import { documentService } from "@/services/document-service"; import { moduleService } from "@/services/module-service"; import type { DocumentCategory, DocumentSummary } from "@/types/document"; import type { Module } from "@/types/module"; import { Plus, Search } from "lucide-react"; const formatDate = (value?: string | null): string => { if (!value) return "-"; return new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", }); }; const toLabel = (value: string): string => value .split("_") .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(" "); const Documents = (): ReactElement => { const navigate = useNavigate(); const [documents, setDocuments] = useState([]); const [categories, setCategories] = useState([]); const [statuses, setStatuses] = useState>( [], ); const [types, setTypes] = useState>([]); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState(null); const [categoryFilter, setCategoryFilter] = useState(null); const [typeFilter, setTypeFilter] = useState(null); const [moduleFilter, setModuleFilter] = useState(null); const [modules, setModules] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(10); const [total, setTotal] = useState(0); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const offset = (currentPage - 1) * limit; const totalPages = Math.max(1, Math.ceil(total / limit)); useEffect(() => { const loadDropdownData = async (): Promise => { try { const [categoriesRes, statusesRes, typesRes, modulesRes] = await Promise.all([ documentService.getCategories(), documentService.getStatuses(), documentService.getTypes(), moduleService.getAvailable(), ]); setCategories(categoriesRes.data || []); setStatuses(statusesRes.data || []); setTypes(typesRes.data || []); setModules(modulesRes.data || []); } catch { // Keep page usable even if some filter metadata endpoints fail. } }; void loadDropdownData(); }, []); useEffect(() => { const loadDocuments = async (): Promise => { try { setIsLoading(true); setError(null); const response = await documentService.list({ status: statusFilter || undefined, category_id: categoryFilter || undefined, document_type: typeFilter || undefined, source_module_id: moduleFilter || undefined, search: search.trim() || undefined, limit, offset, }); setDocuments(response.data || []); setTotal(response.pagination?.total || 0); } catch (err: any) { setError( err?.response?.data?.error?.message || "Failed to load documents", ); } finally { setIsLoading(false); } }; void loadDocuments(); }, [statusFilter, categoryFilter, typeFilter, moduleFilter, search, limit, offset]); const columns: Column[] = useMemo( () => [ { key: "document_number", label: "Document No", render: (doc) => ( ), }, { key: "title", label: "Title", render: (doc) => {doc.title}, }, { key: "document_type", label: "Type", render: (doc) => ( {doc.document_type || "-"} ), }, { key: "category", label: "Category", render: (doc) => {doc.category || "-"}, }, { key: "status", label: "Status", render: (doc) => ( {toLabel(doc.status)} ), }, { key: "module_name", label: "Module", render: (doc) => ( {doc.module_name || "Platform"} ), }, { key: "current_version", label: "Version", render: (doc) => ( {doc.current_version || "-"} ), }, { key: "updated_at", label: "Updated", render: (doc) => ( {formatDate(doc.updated_at)} ), }, { key: "actions", label: "Actions", render: (doc) => ( ), }, ], [navigate], ); return ( {/* */} navigate("/tenant/documents/create")}> New Document ), }} >
{/* Left side: Search and Filters */}
{/* Search Bar */}
{ setSearch(e.target.value); setCurrentPage(1); }} placeholder="Search by name, ID..." className="h-10 w-full pl-9 pr-3 bg-white border border-[rgba(0,0,0,0.08)] rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-[#112868]/10 transition-all" />
{/* Filters */}
({ value: status.code, label: status.name, }))} value={statusFilter} onChange={(value) => { setStatusFilter(value as string | null); setCurrentPage(1); }} placeholder="All" /> ({ value: category.id, label: category.name, }))} value={categoryFilter} onChange={(value) => { setCategoryFilter(value as string | null); setCurrentPage(1); }} placeholder="All" /> ({ value: type.code, label: type.name, }))} value={typeFilter} onChange={(value) => { setTypeFilter(value as string | null); setCurrentPage(1); }} placeholder="All" /> ({ value: module.id, label: module.name, }))} value={moduleFilter} onChange={(value) => { setModuleFilter(value as string | null); setCurrentPage(1); }} placeholder="All Modules" /> {/* {}} placeholder="All" /> } options={[ { value: "more", label: "More Filters..." }, ]} value={null} onChange={() => {}} placeholder="More" /> */}
{/* Right side: Clear Filters */}
doc.id} emptyMessage="No documents found" isLoading={isLoading} error={error} /> {total > 0 && ( { setLimit(value); setCurrentPage(1); }} /> )}
); }; export default Documents;