import { useState, useEffect, useCallback } from "react"; import { getCandidatesApi, deleteCandidateApi } from "@/api/candidates"; import { exportToExcel } from "@/utils/exportToExcel"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Loader2, Trash, RefreshCw, Download, ChevronLeft, ChevronRight, Eye, User, } from "lucide-react"; export default function CandidatePage() { const [candidates, setCandidates] = useState([]); const [loading, setLoading] = useState(true); const [searchText, setSearchText] = useState(""); const [filterCareer, setFilterCareer] = useState(""); const [viewOpen, setViewOpen] = useState(false); const [viewData, setViewData] = useState(null); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 10; const fetchAll = useCallback(async () => { setLoading(true); try { const res = await getCandidatesApi(); setCandidates(res?.data || []); } catch (err) { console.error(err); } finally { setLoading(false); } }, []); useEffect(() => { fetchAll(); }, [fetchAll]); const filteredCandidates = candidates.filter((item) => { const matchesSearch = item.fullName?.toLowerCase().includes(searchText.toLowerCase()) || item.mobile?.includes(searchText) || item.email?.toLowerCase().includes(searchText.toLowerCase()); const matchesCareer = filterCareer ? item.career?.post?.toLowerCase().includes(filterCareer.toLowerCase()) : true; return matchesSearch && matchesCareer; }); useEffect(() => { setCurrentPage(1); }, [searchText, filterCareer]); const totalPages = Math.ceil(filteredCandidates.length / itemsPerPage); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = filteredCandidates.slice( indexOfFirstItem, indexOfLastItem, ); function openView(item: any) { setViewData(item); setViewOpen(true); } async function handleDelete(id: number) { if (!confirm("Delete candidate?")) return; await deleteCandidateApi(id); fetchAll(); } const handleExport = () => { const exportData = filteredCandidates.map((item) => ({ ID: item.id, Name: item.fullName, Phone: item.mobile, Email: item.email, Career: item.career?.post, Designation: item.career?.designation, Subject: item.subject, CoverLetter: item.coverLetter, AppliedDate: new Date(item.createdAt).toLocaleDateString(), })); exportToExcel(exportData, "candidates"); }; return (

Candidates

setSearchText(e.target.value)} className="w-[250px] text-base" /> setFilterCareer(e.target.value)} className="w-[200px] text-base" />
Application List
ID Full Name Career & Post Contact Applied On Cover Letter Actions {loading ? ( ) : currentItems.length === 0 ? ( No candidates found ) : ( currentItems.map((item) => ( {item.id}
{item.fullName}
{item.email}
{item.career?.post || "-"}
{item.career?.designation}
{item.mobile} {new Date(item.createdAt).toLocaleDateString()}
{item.coverLetter || "No cover letter provided."}
)) )}
{!loading && filteredCandidates.length > 0 && (
Showing{" "} {indexOfFirstItem + 1} to{" "} {Math.min(indexOfLastItem, filteredCandidates.length)} {" "} of{" "} {filteredCandidates.length}
Page {currentPage} of {totalPages}
)}
Candidate Details {viewData && (

Personal Information

{viewData.fullName}

{viewData.email}

{viewData.mobile}

Applied For

{viewData.career?.post || "General Application"}

{viewData.career?.designation}

Application Date

{new Date(viewData.createdAt).toLocaleString()}

Subject

{viewData.subject || "N/A"}

Cover Letter / Message

{viewData.coverLetter || "No cover letter provided."}

)}
); }