"use client"; import { useState, useEffect, useCallback } from "react"; import { Plus, Clock, CheckCircle, XCircle, Calendar } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { fetchLeaveRequests, fetchLeaveTypes, fetchLeaveRequestsForCalendar, approveLeaveRequest, rejectLeaveRequest, } from "@/lib/supabase/leave-actions"; import { useUser } from "@clerk/nextjs"; import { STATUS_LABELS, STATUS_COLORS, formatDate, formatDateShort, type LeaveRequestWithDetails, type LeaveType, type LeaveBalance, } from "@/lib/supabase/leave-types"; import PageSkeleton from "./PageSkeleton"; import { LeavePatternInsights } from "@/components/ai/LeavePatternInsights"; const STATUS_ICONS: Record = { pending: , approved: , rejected: , }; function CalendarView({ requests }: { requests: LeaveRequestWithDetails[] }) { const [viewDate, setViewDate] = useState(new Date()); const [selectedDay, setSelectedDay] = useState(null); const currentMonth = viewDate.getMonth(); const currentYear = viewDate.getFullYear(); const now = new Date(); const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate(); const firstDayOfWeek = new Date(currentYear, currentMonth, 1).getDay(); const monthName = viewDate.toLocaleDateString("en-AU", { month: "long", year: "numeric" }); // Build calendar grid const days: { date: Date; isCurrentMonth: boolean }[] = []; for (let i = firstDayOfWeek - 1; i >= 0; i--) { days.push({ date: new Date(currentYear, currentMonth, -i), isCurrentMonth: false }); } for (let d = 1; d <= daysInMonth; d++) { days.push({ date: new Date(currentYear, currentMonth, d), isCurrentMonth: true }); } const remaining = 42 - days.length; for (let i = 1; i <= remaining; i++) { days.push({ date: new Date(currentYear, currentMonth + 1, i), isCurrentMonth: false }); } const getLeaveForDate = (date: Date) => { const dateStr = date.toISOString().split("T")[0]; return requests.filter((r) => { const start = new Date(r.start_date + "T00:00:00"); const end = new Date(r.end_date + "T00:00:00"); return date >= start && date <= end; }); }; // Get leave for selected day const selectedDayLeave = selectedDay ? getLeaveForDate(selectedDay) : []; const LEAVE_COLORS = [ "bg-teal-500", "bg-blue-500", "bg-purple-500", "bg-orange-500", "bg-pink-500", "bg-indigo-500", ]; const getEmployeeColor = (empId: string) => { let hash = 0; for (let i = 0; i < empId.length; i++) hash = empId.charCodeAt(i) + ((hash << 5) - hash); return LEAVE_COLORS[Math.abs(hash) % LEAVE_COLORS.length]; }; const prevMonth = () => setViewDate(new Date(currentYear, currentMonth - 1, 1)); const nextMonth = () => setViewDate(new Date(currentYear, currentMonth + 1, 1)); const goToday = () => { setViewDate(new Date()); setSelectedDay(null); }; const dayNames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; // Adjust for Monday start (ISO) const adjustedDays = [...days.slice(1), days[0]]; return (
{/* Header */}

{monthName}

{dayNames.map((d) => (
{d}
))} {adjustedDays.map((day, i) => { const leave = getLeaveForDate(day.date); const isToday = day.date.getDate() === now.getDate() && day.date.getMonth() === now.getMonth() && day.date.getFullYear() === now.getFullYear(); const isWeekend = day.date.getDay() === 0 || day.date.getDay() === 6; const isSelected = selectedDay && day.date.toDateString() === selectedDay.toDateString(); return ( ); })}
{/* Selected day detail */} {selectedDay && (

{selectedDay.toLocaleDateString("en-AU", { weekday: "long", day: "numeric", month: "long", year: "numeric" })}

{selectedDayLeave.length === 0 ? (

No leave scheduled

) : (
{selectedDayLeave.map((r) => (
{r.employee?.first_name?.[0]}{r.employee?.last_name?.[0]}

{r.employee?.first_name} {r.employee?.last_name}

{r.leave_type?.name} · {formatDate(r.start_date)} — {formatDate(r.end_date)}

{STATUS_LABELS[r.status]}
))}
)}
)} {/* Legend */}
Legend: {Array.from(new Set(requests.map((r) => r.employee?.id))) .filter(Boolean) .slice(0, 6) .map((empId, idx) => { const emp = requests.find((r) => r.employee?.id === empId)?.employee; if (!emp) return null; return (
{emp.first_name} {emp.last_name?.[0]}.
); })}
); } function PendingApprovalCard({ request, onApprove, onReject, }: { request: LeaveRequestWithDetails; onApprove: (id: string) => void; onReject: (id: string) => void; }) { const [confirming, setConfirming] = useState<"approve" | "reject" | null>(null); const [rejectReason, setRejectReason] = useState(""); return (
{request.employee?.first_name} {request.employee?.last_name} {STATUS_LABELS.pending}

{request.leave_type?.name} · {formatDateShort(request.start_date)} — {formatDateShort(request.end_date)} · {request.days_count} {Number(request.days_count) === 1 ? "day" : "days"}

{request.reason && (

“{request.reason}”

)}
{confirming === "reject" ? (
setRejectReason(e.target.value)} placeholder="Reason..." className="text-xs border border-gray-200 rounded px-2 py-1 w-32" />
) : confirming === "approve" ? (
Approve?
) : ( <> )}
); } export default function LeaveListPage({ onNewRequest }: { onNewRequest: () => void }) { const { user } = useUser(); const [requests, setRequests] = useState([]); const [leaveTypes, setLeaveTypes] = useState([]); const [calendarRequests, setCalendarRequests] = useState([]); const [balances, setBalances] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; Promise.all([ fetchLeaveRequests(), fetchLeaveTypes(), fetchLeaveRequestsForCalendar(), ]) .then(([reqs, types, calReqs]) => { if (!cancelled) { setRequests(reqs); setLeaveTypes(types); setCalendarRequests(calReqs); } }) .catch(() => {}) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); const pendingRequests = requests.filter((r) => r.status === "pending"); const handleApprove = useCallback( async (id: string) => { if (!user?.id) return; const result = await approveLeaveRequest(id, user.id); if (result.success) { setRequests((prev) => prev.filter((r) => r.id !== id)); setCalendarRequests((prev) => prev.filter((r) => r.id !== id)); } }, [user] ); const handleReject = useCallback( async (id: string) => { if (!user?.id) return; const result = await rejectLeaveRequest(id, user.id, "Rejected"); if (result.success) { setRequests((prev) => prev.filter((r) => r.id !== id)); } }, [user] ); const approvedRequests = requests.filter((r) => r.status === "approved"); // Prepare data for AI leave pattern analysis const currentMonthLabel = new Date().toLocaleDateString("en-AU", { month: "long", year: "numeric" }); const aiLeaveRequests = requests.map((r) => ({ employee_name: r.employee ? `${r.employee.first_name} ${r.employee.last_name}` : "Unknown", leave_type_name: r.leave_type?.name || "Unknown", start_date: r.start_date, end_date: r.end_date, days_count: r.days_count, status: r.status, reason: r.reason, })); if (loading) return ; return (
{/* Header */}

Leave

Manage time off and approvals

{/* AI Leave Pattern Insights */} {/* Pending Approvals */} {pendingRequests.length > 0 && (

Pending approvals ({pendingRequests.length})

{pendingRequests.map((r) => ( ))}
)} {/* Calendar */} {/* Approved Leave List */} {approvedRequests.length > 0 && (

Recent approved leave

{approvedRequests.slice(0, 10).map((r) => ( ))}
Employee Type Dates Days Status

{r.employee?.first_name} {r.employee?.last_name}

{r.employee?.job_title || ""}

{r.leave_type?.name} {formatDateShort(r.start_date)} — {formatDateShort(r.end_date)} {r.days_count} {STATUS_ICONS[r.status]} {STATUS_LABELS[r.status]}
)} {/* Empty state */} {pendingRequests.length === 0 && approvedRequests.length === 0 && (

No leave requests yet

Request time off or manage approvals here

)}
); }