import { useState, useEffect } from 'react' import api from '@/services/api' import dayjs from 'dayjs' // Types for Calendar entities interface TimeSlotResponse { slot_id: string // real int id or "plan-{planId}-{date}" for virtual date: string slot_type: 'work' | 'on_call' | 'entertainment' | 'system' estimated_duration: number scheduled_at: string // HH:mm started_at: string | null attended: boolean actual_duration: number | null event_type: 'job' | 'entertainment' | 'system_event' | null event_data: Record | null priority: number status: 'not_started' | 'ongoing' | 'deferred' | 'skipped' | 'paused' | 'finished' | 'aborted' plan_id: number | null is_virtual: boolean created_at: string | null updated_at: string | null } interface SchedulePlanResponse { id: number slot_type: 'work' | 'on_call' | 'entertainment' | 'system' estimated_duration: number event_type: string | null event_data: Record | null at_time: string on_day: string | null on_week: number | null on_month: string | null is_active: boolean created_at: string updated_at: string | null } const SLOT_TYPE_ICONS: Record = { work: '๐Ÿ’ผ', on_call: '๐Ÿ“ž', entertainment: '๐ŸŽฎ', system: 'โš™๏ธ', } const STATUS_CLASSES: Record = { not_started: 'status-open', ongoing: 'status-undergoing', deferred: 'status-pending', skipped: 'status-closed', paused: 'status-pending', finished: 'status-completed', aborted: 'status-closed', } export default function CalendarPage() { const [selectedDate, setSelectedDate] = useState(dayjs().format('YYYY-MM-DD')) const [slots, setSlots] = useState([]) const [plans, setPlans] = useState([]) const [loading, setLoading] = useState(false) const [activeTab, setActiveTab] = useState<'daily' | 'plans'>('daily') const [error, setError] = useState('') const fetchSlots = async (date: string) => { setLoading(true) setError('') try { const { data } = await api.get(`/calendar/day?date=${date}`) setSlots(data) } catch (err: any) { setError(err.response?.data?.detail || 'Failed to load calendar') setSlots([]) } finally { setLoading(false) } } const fetchPlans = async () => { try { const { data } = await api.get('/calendar/plans') setPlans(data) } catch (err: any) { console.error('Failed to load plans:', err) } } useEffect(() => { fetchSlots(selectedDate) fetchPlans() }, []) useEffect(() => { if (activeTab === 'daily') { fetchSlots(selectedDate) } }, [selectedDate]) const handleDateChange = (e: React.ChangeEvent) => { setSelectedDate(e.target.value) } const goToday = () => { setSelectedDate(dayjs().format('YYYY-MM-DD')) } const goPrev = () => { setSelectedDate(dayjs(selectedDate).subtract(1, 'day').format('YYYY-MM-DD')) } const goNext = () => { setSelectedDate(dayjs(selectedDate).add(1, 'day').format('YYYY-MM-DD')) } const formatPlanSchedule = (plan: SchedulePlanResponse) => { const parts: string[] = [`at ${plan.at_time}`] if (plan.on_day) parts.push(`on ${plan.on_day}`) if (plan.on_week) parts.push(`week ${plan.on_week}`) if (plan.on_month) parts.push(`in ${plan.on_month}`) return parts.join(' ยท ') } return (

๐Ÿ“… Calendar

{/* Tab switcher */}
{error &&
{error}
} {activeTab === 'daily' && ( <> {/* Date navigation */}
{/* Slot list */} {loading ? (
Loading...
) : slots.length === 0 ? (
No slots scheduled for {dayjs(selectedDate).format('MMMM D, YYYY')}.
) : (
{slots.map((slot) => (
{SLOT_TYPE_ICONS[slot.slot_type] || '๐Ÿ“‹'} {slot.slot_type.replace('_', ' ')} {slot.status.replace('_', ' ')} {slot.is_virtual && plan} {slot.scheduled_at}
โฑ {slot.estimated_duration} min โšก Priority: {slot.priority} {slot.event_type && ๐Ÿ“Œ {slot.event_type.replace('_', ' ')}}
{slot.event_data && slot.event_data.code && (
๐Ÿ”— {slot.event_data.code}
)} {slot.event_data && slot.event_data.event && (
๐Ÿ“ฃ {slot.event_data.event}
)}
))}
)} )} {activeTab === 'plans' && (
{plans.length === 0 ? (
No schedule plans configured.
) : ( plans.map((plan) => (
{SLOT_TYPE_ICONS[plan.slot_type] || '๐Ÿ“‹'} {plan.slot_type.replace('_', ' ')} {!plan.is_active && inactive} Plan #{plan.id}
๐Ÿ”„ {formatPlanSchedule(plan)}
โฑ {plan.estimated_duration} min
{plan.event_type &&
๐Ÿ“Œ {plan.event_type.replace('_', ' ')}
}
)) )}
)}
) }