diff --git a/app/staff/schedule/page.tsx b/app/staff/schedule/page.tsx new file mode 100644 index 00000000..aeee5445 --- /dev/null +++ b/app/staff/schedule/page.tsx @@ -0,0 +1,229 @@ +"use client" + +import { useEffect, useState } from "react" +import { createClient } from "@/lib/supabase/client" +import { useAuth } from "@/lib/hooks/useAuth" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { + Calendar, + ChevronLeft, + ChevronRight, + Clock, + CalendarDays, + Info +} from "lucide-react" +import { + format, + startOfWeek, + endOfWeek, + eachDayOfInterval, + addWeeks, + subWeeks, + isSameDay, + isToday, + parseISO, + isSameMonth +} from "date-fns" + +type Shift = { + id: string + title: string + start_time: string + end_time: string + type: 'Regular' | 'Overtime' | 'OnCall' +} + +type Leave = { + id: string + start_date: string + end_date: string + leave_type: string + status: string +} + +export default function SchedulePage() { + const { user, loading: authLoading } = useAuth() + const [currentDate, setCurrentDate] = useState(new Date()) + const [shifts, setShifts] = useState([]) + const [leaves, setLeaves] = useState([]) + const [loading, setLoading] = useState(true) + const supabase = createClient() + + useEffect(() => { + if (!user) return + + const fetchSchedule = async () => { + setLoading(true) + try { + // Determine date range for current view (give buffer +1/-1 week) + const start = startOfWeek(subWeeks(currentDate, 1), { weekStartsOn: 1 }) + const end = endOfWeek(addWeeks(currentDate, 1), { weekStartsOn: 1 }) + + // Fetch Shifts + const { data: shiftsData, error: shiftsError } = await supabase + .from("shifts") + .select("*") + .eq("user_id", user.id) + .gte("start_time", start.toISOString()) + .lte("end_time", end.toISOString()) + + if (shiftsError && shiftsError.code !== '42P01') throw shiftsError // Ignore table not found for dev + + // Fetch Leaves + const { data: leavesData, error: leavesError } = await supabase + .from("leave_requests") + .select("*") + .eq("user_id", user.id) + .neq("status", "rejected") + .gte("end_date", start.toISOString()) // Overlap check simplified + + if (leavesError) throw leavesError + + setShifts(shiftsData || []) + setLeaves(leavesData || []) + + } catch (err) { + console.error("Error fetching schedule:", err) + } finally { + setLoading(false) + } + } + + fetchSchedule() + }, [user, currentDate, supabase]) + + const nextWeek = () => setCurrentDate(addWeeks(currentDate, 1)) + const prevWeek = () => setCurrentDate(subWeeks(currentDate, 1)) + const resetToToday = () => setCurrentDate(new Date()) + + const weekStart = startOfWeek(currentDate, { weekStartsOn: 1 }) // Monday + const weekEnd = endOfWeek(currentDate, { weekStartsOn: 1 }) + const days = eachDayOfInterval({ start: weekStart, end: weekEnd }) + + if (authLoading) { + return ( +
+
+
+ ) + } + + return ( +
+
+
+

+ My Schedule +

+

+ View your upcoming shifts and time off. +

+
+
+ + + +
+
+ + {/* Weekly Calendar Grid */} +
+ {days.map((day, i) => { + const isTodayDate = isToday(day) + const isCurrentMonth = isSameMonth(day, currentDate) + + // Find shifts for this day + const dayShifts = shifts.filter(s => isSameDay(parseISO(s.start_time), day)) + + // Find leaves for this day + const dayLeaves = leaves.filter(l => { + const start = parseISO(l.start_date) + const end = parseISO(l.end_date) + // Simple check: is day between start and end (inclusive) + // Compare just dates to avoid time issues + const target = format(day, 'yyyy-MM-dd') + return target >= l.start_date && target <= l.end_date + }) + + return ( + + +
+ {format(day, "EEE")} + + {format(day, "d")} + +
+
+ + {/* Shifts */} + {dayShifts.map(shift => ( +
+
{shift.title}
+
+ + {format(parseISO(shift.start_time), "HH:mm")} - {format(parseISO(shift.end_time), "HH:mm")} +
+
+ ))} + + {/* Leaves */} + {dayLeaves.map(leave => ( +
+
On Leave
+
{leave.leave_type}
+
+ ))} + + {/* Empty State (if weekday and no activity) */} + {dayShifts.length === 0 && dayLeaves.length === 0 && day.getDay() !== 0 && ( +
+ +
+ )} + + {/* Sunday indicator */} + {day.getDay() === 0 && dayShifts.length === 0 && ( +
+ + Weekly Off +
+ )} +
+
+ ) + })} +
+ + {/* Legend / Info */} +
+
+
+ Scheduled Shift +
+
+
+ On Leave +
+
+
+ Weekly Off +
+
+
+ ) +}