import React, { useState, useMemo, useEffect, useRef } from 'react'; import { useMockStore } from '../data/mockStore'; import { useTheme } from '../context/ThemeContext'; import { Calendar, User, Clock, MapPin, MoreHorizontal, Filter, X, ChevronDown } from 'lucide-react'; import { SpotlightCard } from '../components/SpotlightCard'; const AdminSchedule = () => { const { meetings } = useMockStore(); const { theme } = useTheme(); // Refs for click-outside detection const dateDropdownRef = useRef(null); const statusDropdownRef = useRef(null); // Filter states const [dateFilter, setDateFilter] = useState('all'); // all, today, week, month, quarter, custom const [customDateRange, setCustomDateRange] = useState({ start: '', end: '' }); const [statusFilter, setStatusFilter] = useState([]); const [showDateDropdown, setShowDateDropdown] = useState(false); const [showStatusDropdown, setShowStatusDropdown] = useState(false); const [openDropdownId, setOpenDropdownId] = useState(null); // Click-outside detection for filter dropdowns useEffect(() => { const handleClickOutside = (event) => { if (dateDropdownRef.current && !dateDropdownRef.current.contains(event.target)) { setShowDateDropdown(false); } if (statusDropdownRef.current && !statusDropdownRef.current.contains(event.target)) { setShowStatusDropdown(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // Click-outside detection for action dropdowns useEffect(() => { const handleClickOutside = (event) => { if (openDropdownId && !event.target.closest('.action-dropdown-container')) { setOpenDropdownId(null); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [openDropdownId]); // Get unique statuses from meetings const availableStatuses = useMemo(() => { return [...new Set(meetings.map(m => m.status))]; }, [meetings]); // Filter meetings based on selected filters const filteredMeetings = useMemo(() => { let filtered = [...meetings]; // Date filtering if (dateFilter !== 'all') { const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); filtered = filtered.filter(meeting => { const meetingDate = new Date(meeting.date); switch (dateFilter) { case 'today': return meetingDate.toDateString() === today.toDateString(); case 'week': const weekStart = new Date(today); weekStart.setDate(today.getDate() - today.getDay()); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 6); return meetingDate >= weekStart && meetingDate <= weekEnd; case 'month': return meetingDate.getMonth() === now.getMonth() && meetingDate.getFullYear() === now.getFullYear(); case 'quarter': const quarter = Math.floor(now.getMonth() / 3); const meetingQuarter = Math.floor(meetingDate.getMonth() / 3); return meetingQuarter === quarter && meetingDate.getFullYear() === now.getFullYear(); case 'custom': if (customDateRange.start && customDateRange.end) { const start = new Date(customDateRange.start); const end = new Date(customDateRange.end); return meetingDate >= start && meetingDate <= end; } return true; default: return true; } }); } // Status filtering if (statusFilter.length > 0) { filtered = filtered.filter(meeting => statusFilter.includes(meeting.status)); } return filtered; }, [meetings, dateFilter, customDateRange, statusFilter]); const toggleStatusFilter = (status) => { setStatusFilter(prev => prev.includes(status) ? prev.filter(s => s !== status) : [...prev, status] ); }; const clearFilters = () => { setDateFilter('all'); setCustomDateRange({ start: '', end: '' }); setStatusFilter([]); }; const handleActionClick = (meetingId, action) => { console.log(`Action ${action} for meeting ${meetingId}`); // TODO: Implement actual actions setOpenDropdownId(null); }; const [viewMode, setViewMode] = useState('list'); // 'list' or 'calendar' // Mock color mapping for agents const agentColors = { 'a1': 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-800', 'a2': 'bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800', 'a3': 'bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/30 dark:text-purple-300 dark:border-purple-800', 'a4': 'bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:border-amber-800', 'a5': 'bg-pink-100 text-pink-700 border-pink-200 dark:bg-pink-900/30 dark:text-pink-300 dark:border-pink-800', }; const getAgentColor = (agentId) => { // Simple hash or lookup const id = agentId?.toLowerCase() || 'a1'; return agentColors[id] || agentColors['a1']; }; return (
{/* Ambient Background Glows */}
{/* Content */}

Team Schedule

Manage field agent appointments

{/* View Toggle */}
{/* Filters Section (Only show relevant filters for list view potentially, or both) */}
{/* Date Filter */}
{showDateDropdown && (

Custom Range

setCustomDateRange(prev => ({ ...prev, start: e.target.value }))} className="w-full px-2 py-1 text-xs bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded mb-2 text-zinc-900 dark:text-white" placeholder="Start date" /> setCustomDateRange(prev => ({ ...prev, end: e.target.value }))} className="w-full px-2 py-1 text-xs bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded mb-2 text-zinc-900 dark:text-white" placeholder="End date" />
)}
{/* Status Filter */}
{showStatusDropdown && (
{availableStatuses.map(status => ( ))}
)}
{/* Clear Filters */} {(dateFilter !== 'all' || statusFilter.length > 0) && ( )} {/* Results Count */}
Showing {filteredMeetings.length} of {meetings.length} appointments

{viewMode === 'list' ? 'All Active Appointments' : 'Calendar Overview'}

{viewMode === 'list' ? (
{filteredMeetings.length === 0 ? ( ) : ( filteredMeetings.map((meeting) => ( )) )}
Agent Customer Property Date & Time Status Actions
No appointments found matching the selected filters.
{meeting.agentId}
Agent {meeting.agentId}
{meeting.customerName} {meeting.propertyId}
{meeting.date} {meeting.time}
{meeting.status}
{openDropdownId === meeting.id && (
)}
) : ( )}
igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?
); }; // --- Custom Month View Calendar --- const CalendarView = ({ meetings, getAgentColor }) => { // Current month view state const [currentDate, setCurrentDate] = useState(new Date()); const getDaysInMonth = (date) => { const year = date.getFullYear(); const month = date.getMonth(); const days = new Date(year, month + 1, 0).getDate(); const firstDay = new Date(year, month, 1).getDay(); return { days, firstDay }; }; const { days, firstDay } = useMemo(() => getDaysInMonth(currentDate), [currentDate]); const changeMonth = (offset) => { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + offset, 1)); }; // Group meetings by date (using local date string YYYY-MM-DD) const meetingsByDate = useMemo(() => { const map = {}; meetings.forEach(m => { // Assuming m.date is YYYY-MM-DD or parseable const d = new Date(m.date); // We need to compare local dates const key = d.getDate(); // 1-31 const monthMatches = d.getMonth() === currentDate.getMonth() && d.getFullYear() === currentDate.getFullYear(); if (monthMatches) { if (!map[key]) map[key] = []; map[key].push(m); } }); return map; }, [meetings, currentDate]); const renderCalendarCells = () => { const cells = []; // Empty cells for padding before the first day for (let i = 0; i < firstDay; i++) { cells.push(
); } // Days for (let d = 1; d <= days; d++) { const dayMeetings = meetingsByDate[d] || []; const isToday = new Date().toDateString() === new Date(currentDate.getFullYear(), currentDate.getMonth(), d).toDateString(); cells.push(
{d}
{dayMeetings.map(m => (
{m.time} {m.customerName}
))}
); } return cells; }; return (
{/* Calendar Header */}

{currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}

{/* Calendar Grid */}
{/* Weekday Headers */}
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
{day}
))}
{/* Days Grid */}
{renderCalendarCells()}
{/* Legend */}
Agent A1
Agent A2
Agent A3
Agent A4
Agent A5
); }; export default AdminSchedule;