import { useState, useEffect, useRef } from "react"; import { format } from "date-fns"; import { CalendarIcon, Check, Info, ChevronDown } from "lucide-react"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import * as z from "zod"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { cn } from "@/lib/utils"; import { AppraisalCycle, Appraisal } from "@/types"; import CustomDialog from "@/components/CustomDialog"; import { getEmployeesByRole } from "@/services/employeeApi"; import { useAuth } from "@/lib/auth-context"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { ScrollArea } from "@/components/ui/scroll-area"; const formSchema = z.object({ cycleId: z.string({ required_error: "Please select an appraisal cycle.", }), appraiserId: z.string({ required_error: "Please select a manager.", }), }); interface MissingCycleDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onSubmit: (cycleId: number, appraiserId: number) => void; cycles: AppraisalCycle[]; currentAppraisals: Appraisal[]; isLoading?: boolean; } export function MissingCycleDialog({ open, onOpenChange, onSubmit, cycles, currentAppraisals, isLoading = false, }: MissingCycleDialogProps) { const [managers, setManagers] = useState>([]); const [isLoadingManagers, setIsLoadingManagers] = useState(false); const [error, setError] = useState(null); const [availableCycles, setAvailableCycles] = useState([]); const { userData } = useAuth(); // Dropdown state const [cycleDropdownOpen, setCycleDropdownOpen] = useState(false); const [managerDropdownOpen, setManagerDropdownOpen] = useState(false); // Search state const [cycleSearch, setCycleSearch] = useState(""); const [managerSearch, setManagerSearch] = useState(""); // Refs for click outside handling const cycleDropdownRef = useRef(null); const managerDropdownRef = useRef(null); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { cycleId: "", appraiserId: "", }, }); // Handle click outside to close dropdowns useEffect(() => { function handleClickOutside(event: MouseEvent) { if (cycleDropdownRef.current && !cycleDropdownRef.current.contains(event.target as Node)) { setCycleDropdownOpen(false); } if (managerDropdownRef.current && !managerDropdownRef.current.contains(event.target as Node)) { setManagerDropdownOpen(false); } } document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); // Filter out cycles that the employee already has appraisals for useEffect(() => { if (cycles && currentAppraisals) { const currentCycleIds = currentAppraisals.map(a => a.cycleId); const filtered = cycles.filter(cycle => !currentCycleIds.includes(cycle.cycleId)); setAvailableCycles(filtered); } }, [cycles, currentAppraisals]); // Load managers when component mounts useEffect(() => { const loadManagers = async () => { setIsLoadingManagers(true); try { // Get employees with manager roles (assuming role IDs for managers are 1, 2, 3) // In a real app, you would need to filter by appropriate manager roles const managerRoleIds = [1, 2, 3]; // Example role IDs for managers const promises = managerRoleIds.map(roleId => getEmployeesByRole(roleId)); const responses = await Promise.all(promises); const allManagers: Array<{ id: number; name: string }> = []; responses.forEach(response => { if (response.success) { const managers = response.data.map(employee => ({ id: employee.id, name: `${employee.firstName} ${employee.lastName}` })); allManagers.push(...managers); } }); setManagers(allManagers); } catch (error) { console.error("Error loading managers:", error); setError("Failed to load managers. Please try again."); } finally { setIsLoadingManagers(false); } }; if (open) { loadManagers(); } }, [open]); function handleSubmit(values: z.infer) { setError(null); const cycleId = parseInt(values.cycleId); const appraiserId = parseInt(values.appraiserId); if (isNaN(cycleId) || isNaN(appraiserId)) { setError("Invalid selection. Please try again."); return; } onSubmit(cycleId, appraiserId); } // Handle dialog closing const handleClose = () => { onOpenChange(false); form.reset(); setCycleSearch(""); setManagerSearch(""); setCycleDropdownOpen(false); setManagerDropdownOpen(false); }; const isPastCycle = (cycle: AppraisalCycle) => { const now = new Date(); const endDate = new Date(cycle.endDate); return now > endDate; }; // Find cycle name by id const getCycleName = (cycleId: string) => { if (!cycleId) return ""; const cycle = availableCycles.find(c => c.cycleId.toString() === cycleId); return cycle ? cycle.name : ""; }; // Find manager name by id const getManagerName = (managerId: string) => { if (!managerId) return ""; const manager = managers.find(m => m.id.toString() === managerId); return manager ? manager.name : ""; }; // Filter cycles based on search const filteredCycles = availableCycles.filter(cycle => cycle.name.toLowerCase().includes(cycleSearch.toLowerCase()) ); // Filter managers based on search const filteredManagers = managers.filter(manager => manager.name.toLowerCase().includes(managerSearch.toLowerCase()) ); return ( {availableCycles.length === 0 ? ( No missing cycles You have appraisals for all available cycles. ) : (
( Appraisal Cycle
{cycleDropdownOpen && (
setCycleSearch(e.target.value)} onClick={(e) => e.stopPropagation()} className="w-full mb-2" />
{filteredCycles.length === 0 ? (
No cycles found
) : (
{filteredCycles.map((cycle) => { const isDisabled = !isPastCycle(cycle); return ( ); })}
)}
)}
)} /> ( Manager
{managerDropdownOpen && (
setManagerSearch(e.target.value)} onClick={(e) => e.stopPropagation()} className="w-full mb-2" />
{isLoadingManagers ? (
Loading managers...
) : filteredManagers.length === 0 ? (
No managers found
) : (
{filteredManagers.map((manager) => ( ))}
)}
)}
)} /> {error &&

{error}

}
)}
); }