"use client" import * as React from "react" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { ScrollArea } from "@/components/ui/scroll-area" import { formatDateTime } from "@/lib/utils" import { CandidateLogWithUser, getCandidateLogs } from "../service" import { useToast } from "@/hooks/use-toast" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" import { Download, Search, User } from "lucide-react" interface ViewCandidateLogsDialogProps { open: boolean onOpenChange: (open: boolean) => void candidateId: number | null } export function ViewCandidateLogsDialog({ open, onOpenChange, candidateId, }: ViewCandidateLogsDialogProps) { const [logs, setLogs] = React.useState([]) const [filteredLogs, setFilteredLogs] = React.useState([]) const [loading, setLoading] = React.useState(false) const [error, setError] = React.useState(null) const [searchQuery, setSearchQuery] = React.useState("") const [actionFilter, setActionFilter] = React.useState("all") const { toast } = useToast() // Get unique action types for filter dropdown const actionTypes = React.useMemo(() => { if (!logs.length) return [] return Array.from(new Set(logs.map(log => log.action))) }, [logs]) // Fetch logs when dialog opens React.useEffect(() => { if (open && candidateId) { setLoading(true) setError(null) getCandidateLogs(candidateId) .then((res) => { setLogs(res) setFilteredLogs(res) }) .catch((err) => { console.error(err) setError("Failed to load logs. Please try again.") toast({ variant: "destructive", title: "Error", description: "Failed to load candidate logs", }) }) .finally(() => setLoading(false)) } else { // Reset state when dialog closes setSearchQuery("") setActionFilter("all") } }, [open, candidateId, toast]) // Filter logs based on search query and action filter React.useEffect(() => { if (!logs.length) return let result = [...logs] // Apply action filter if (actionFilter !== "all") { result = result.filter(log => log.action === actionFilter) } // Apply search filter (case insensitive) if (searchQuery) { const query = searchQuery.toLowerCase() result = result.filter(log => log.action.toLowerCase().includes(query) || (log.comment && log.comment.toLowerCase().includes(query)) || (log.oldStatus && log.oldStatus.toLowerCase().includes(query)) || (log.newStatus && log.newStatus.toLowerCase().includes(query)) || (log.userName && log.userName.toLowerCase().includes(query)) || (log.userEmail && log.userEmail.toLowerCase().includes(query)) ) } setFilteredLogs(result) }, [logs, searchQuery, actionFilter]) // Export logs as CSV const exportLogs = () => { if (!filteredLogs.length) return const headers = ["Action", "Old Status", "New Status", "Comment", "User", "Email", "Date"] const csvContent = [ headers.join(","), ...filteredLogs.map(log => [ `"${log.action}"`, `"${log.oldStatus || ''}"`, `"${log.newStatus || ''}"`, `"${log.comment?.replace(/"/g, '""') || ''}"`, `"${log.userName || ''}"`, `"${log.userEmail || ''}"`, `"${formatDateTime(log.createdAt, "KR")}"` ].join(",")) ].join("\n") const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }) const url = URL.createObjectURL(blob) const link = document.createElement("a") link.setAttribute("href", url) link.setAttribute("download", `candidate-logs-${candidateId}-${new Date().toISOString().split('T')[0]}.csv`) link.style.visibility = "hidden" document.body.appendChild(link) link.click() document.body.removeChild(link) } // Render status change with appropriate badge const renderStatusChange = (oldStatus: string, newStatus: string) => { return (
Status: {oldStatus} {newStatus}
) } return ( 협력업체 상태변경 히스토리 {/* Filters and search */} {/* Filters and search */}
setSearchQuery(e.target.value)} className="pl-8" />
{loading && (

Loading logs...

)} {error && !loading && (
{error}
)} {!loading && !error && filteredLogs.length === 0 && (

{logs.length > 0 ? "No logs match your search criteria." : "No logs found for this candidate."}

)} {!loading && !error && filteredLogs.length > 0 && ( <>
Showing {filteredLogs.length} {filteredLogs.length === 1 ? 'log' : 'logs'} {filteredLogs.length !== logs.length && ` (filtered from ${logs.length})`}
{filteredLogs.map((log) => (
{log.action}
{formatDateTime(log.createdAt, "KR")}
{log.oldStatus && log.newStatus && (
{renderStatusChange(log.oldStatus, log.newStatus)}
)} {log.comment && (
Comment: {log.comment}
)} {(log.userName || log.userEmail) && (
{log.userName || "Unknown"} {log.userEmail && ({log.userEmail})}
)}
))}
)}
) }