'use client' import { useState, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' import { Plus, Edit, Trash2, Clock, Coffee, Calendar } from 'lucide-react' interface StaffSchedule { id: string staff_id: string date: string start_time: string end_time: string is_available: boolean reason?: string } interface Staff { id: string display_name: string role: string } const DAYS_OF_WEEK = [ { key: 'monday', label: 'Lunes' }, { key: 'tuesday', label: 'Martes' }, { key: 'wednesday', label: 'Miércoles' }, { key: 'thursday', label: 'Jueves' }, { key: 'friday', label: 'Viernes' }, { key: 'saturday', label: 'Sábado' }, { key: 'sunday', label: 'Domingo' } ] const TIME_SLOTS = Array.from({ length: 24 * 2 }, (_, i) => { const hour = Math.floor(i / 2) const minute = (i % 2) * 30 return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}` }) export default function ScheduleManagement() { const [staff, setStaff] = useState([]) const [selectedStaff, setSelectedStaff] = useState('') const [schedule, setSchedule] = useState([]) const [loading, setLoading] = useState(false) const [dialogOpen, setDialogOpen] = useState(false) const [editingSchedule, setEditingSchedule] = useState(null) const [formData, setFormData] = useState({ date: '', start_time: '09:00', end_time: '17:00', is_available: true, reason: '' }) useEffect(() => { fetchStaff() }, []) useEffect(() => { if (selectedStaff) { fetchSchedule() } }, [selectedStaff]) const fetchStaff = async () => { try { const response = await fetch('/api/aperture/staff') const data = await response.json() if (data.success) { setStaff(data.staff) } } catch (error) { console.error('Error fetching staff:', error) } } const fetchSchedule = async () => { if (!selectedStaff) return setLoading(true) try { const today = new Date() const startDate = today.toISOString().split('T')[0] const endDate = new Date(today.setDate(today.getDate() + 30)).toISOString().split('T')[0] const response = await fetch( `/api/aperture/staff/schedule?staff_id=${selectedStaff}&start_date=${startDate}&end_date=${endDate}` ) const data = await response.json() if (data.success) { setSchedule(data.availability || []) } } catch (error) { console.error('Error fetching schedule:', error) } finally { setLoading(false) } } const generateWeeklySchedule = async () => { if (!selectedStaff) return const weeklyData = DAYS_OF_WEEK.map((day, index) => { const date = new Date() date.setDate(date.getDate() + ((index + 7 - date.getDay()) % 7)) const dateStr = date.toISOString().split('T')[0] const isWeekend = day.key === 'saturday' || day.key === 'sunday' const startTime = isWeekend ? '10:00' : '09:00' const endTime = isWeekend ? '15:00' : '17:00' return { staff_id: selectedStaff, date: dateStr, start_time: startTime, end_time: endTime, is_available: !isWeekend, reason: isWeekend ? 'Fin de semana' : undefined } }) try { for (const day of weeklyData) { await fetch('/api/aperture/staff/schedule', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(day) }) } await fetchSchedule() alert('Horario semanal generado exitosamente') } catch (error) { console.error('Error generating weekly schedule:', error) alert('Error al generar el horario') } } const addBreakToSchedule = async (scheduleId: string, breakStart: string, breakEnd: string) => { try { await fetch('/api/aperture/staff/schedule', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ staff_id: selectedStaff, date: schedule.find(s => s.id === scheduleId)?.date, start_time: breakStart, end_time: breakEnd, is_available: false, reason: 'Break de 30 min' }) }) await fetchSchedule() } catch (error) { console.error('Error adding break:', error) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() try { await fetch('/api/aperture/staff/schedule', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ staff_id: selectedStaff, ...formData }) }) await fetchSchedule() setDialogOpen(false) setEditingSchedule(null) setFormData({ date: '', start_time: '09:00', end_time: '17:00', is_available: true, reason: '' }) } catch (error) { console.error('Error saving schedule:', error) alert('Error al guardar el horario') } } const handleDelete = async (scheduleId: string) => { if (!confirm('¿Eliminar este horario?')) return try { await fetch(`/api/aperture/staff/schedule?id=${scheduleId}`, { method: 'DELETE' }) await fetchSchedule() } catch (error) { console.error('Error deleting schedule:', error) } } const calculateWorkingHours = (schedules: StaffSchedule[]) => { return schedules.reduce((total, s) => { if (!s.is_available) return total const start = parseInt(s.start_time.split(':')[0]) * 60 + parseInt(s.start_time.split(':')[1]) const end = parseInt(s.end_time.split(':')[0]) * 60 + parseInt(s.end_time.split(':')[1]) return total + (end - start) }, 0) } const getScheduleForDate = (date: string) => { return schedule.filter(s => s.date === date && s.is_available) } const getBreaksForDate = (date: string) => { return schedule.filter(s => s.date === date && !s.is_available && s.reason === 'Break de 30 min') } const selectedStaffData = staff.find(s => s.id === selectedStaff) return (

Gestión de Horarios

Administra horarios y breaks del staff

{selectedStaff && ( <> )}
Seleccionar Staff Selecciona un miembro del equipo para ver y gestionar su horario {selectedStaff && ( Horario de {selectedStaffData?.display_name} Total horas programadas: {(calculateWorkingHours(schedule) / 60).toFixed(1)}h {' • '}Los breaks de 30min se agregan automáticamente cada 8hrs {loading ? (
Cargando horario...
) : (
{DAYS_OF_WEEK.map((day) => { const date = new Date() const currentDayOfWeek = date.getDay() const targetDayOfWeek = DAYS_OF_WEEK.findIndex(d => d.key === day.key) const daysUntil = (targetDayOfWeek - currentDayOfWeek + 7) % 7 date.setDate(date.getDate() + daysUntil) const dateStr = date.toISOString().split('T')[0] const daySchedules = getScheduleForDate(dateStr) const dayBreaks = getBreaksForDate(dateStr) const totalMinutes = daySchedules.reduce((total, s) => { const start = parseInt(s.start_time.split(':')[0]) * 60 + parseInt(s.start_time.split(':')[1]) const end = parseInt(s.end_time.split(':')[0]) * 60 + parseInt(s.end_time.split(':')[1]) return total + (end - start) }, 0) const shouldHaveBreak = totalMinutes >= 480 return (
{day.label} {dateStr}
{shouldHaveBreak && dayBreaks.length === 0 && ( Break pendiente )} {dayBreaks.length > 0 && ( Break incluido )} 0 ? 'default' : 'secondary'}> {(totalMinutes / 60).toFixed(1)}h
{daySchedules.length > 0 ? (
{daySchedules.map((s) => (
{s.start_time} - {s.end_time}
))} {dayBreaks.map((b) => (
{b.start_time} - {b.end_time} (Break)
))}
) : (

Sin horario programado

)}
) })}
)}
)} Agregar Día de Trabajo Define el horario de trabajo para este día
setFormData({...formData, date: e.target.value})} className="col-span-3" required />
setFormData({...formData, reason: e.target.value})} className="col-span-3" placeholder="Opcional" />
) }