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 type { DocumentCategory, DocumentSummary } from "@/types/document"; import { Plus } 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 [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] = await Promise.all([ documentService.getCategories(), documentService.getStatuses(), documentService.getTypes(), ]); setCategories(categoriesRes.data || []); setStatuses(statusesRes.data || []); setTypes(typesRes.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, 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, 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 (
({ 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" />
navigate("/tenant/documents/create")}> New Document
{ setSearch(e.target.value); setCurrentPage(1); }} placeholder="Search by title, description or document number" className="h-10 w-full max-w-xl px-3 border border-[rgba(0,0,0,0.08)] rounded-md text-sm" />
doc.id} emptyMessage="No documents found" isLoading={isLoading} error={error} /> {total > 0 && ( { setLimit(value); setCurrentPage(1); }} /> )}
); }; export default Documents;