import { Head, Link, router } from '@inertiajs/react'; import { ArrowLeft, ArrowRight, CalendarDays, Clock, Filter, Package, Plus, User, X } from 'lucide-react'; import { useState } from 'react'; import { PageHeading } from '@/components/page-heading'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { cn } from '@/lib/utils'; import type { Reservation } from '@/types'; const weekdays = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb', 'Dom']; const statusOptions = [ { value: '', label: 'Todos os estados' }, { value: 'pending', label: 'Pendente' }, { value: 'confirmed', label: 'Confirmada ou em curso' }, { value: 'closed', label: 'Fechada sem utilização' }, ]; export default function ReservationCalendar({ month, reservations, scope = 'personal', status = '', }: { month: string; reservations: (Reservation & { requester?: { name: string; department: string | null }; })[]; scope?: 'personal' | 'global'; status?: string; }) { const [selectedDay, setSelectedDay] = useState(null); const [year, monthNumber] = month.split('-').map(Number); const monthStart = new Date(year, monthNumber - 1, 1); const firstWeekday = (monthStart.getDay() + 6) % 7; const daysInMonth = new Date(year, monthNumber, 0).getDate(); const cellCount = Math.ceil((firstWeekday + daysInMonth) / 7) * 7; const gridStart = new Date(year, monthNumber - 1, 1 - firstWeekday); const days = Array.from({ length: cellCount }, (_, index) => { const date = new Date(gridStart); date.setDate(gridStart.getDate() + index); return date; }); const title = new Intl.DateTimeFormat('pt-PT', { month: 'long', year: 'numeric', }).format(monthStart); const previousMonth = monthKey(new Date(year, monthNumber - 2, 1)); const nextMonth = monthKey(new Date(year, monthNumber, 1)); const global = scope === 'global'; const calendarUrl = global ? '/calendario-global' : '/requisicoes/calendario'; function handleStatusChange(value: string) { router.get(calendarUrl, { month, status: value || undefined }, { preserveState: true, preserveScroll: true, }); } function handleDayClick(day: Date) { setSelectedDay(day); } function formatDate(date: Date): string { return date.toLocaleDateString('pt-PT', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', }); } function formatTime(dateString: string): string { const date = new Date(dateString); return date.toLocaleTimeString('pt-PT', { hour: '2-digit', minute: '2-digit', }); } function getStatusLabel(status: string): string { if (['cancelled', 'rejected', 'expired', 'no_show'].includes(status)) { return 'Fechada sem utilização'; } if (['approved', 'ready', 'delivered', 'in_use'].includes(status)) { return 'Confirmada ou em curso'; } return 'Pendente'; } function getStatusColor(status: string): string { if (['cancelled', 'rejected', 'expired', 'no_show'].includes(status)) { return 'bg-slate-400'; } if (['approved', 'ready', 'delivered', 'in_use'].includes(status)) { return 'bg-emerald-500'; } return 'bg-amber-500'; } return ( <>
Nova requisição ) : undefined } />
{status && ( )}

{title}

{weekdays.map((day) => (
{day}
))}
{days.map((day) => { const dayStart = new Date( day.getFullYear(), day.getMonth(), day.getDate(), ); const dayEnd = new Date( day.getFullYear(), day.getMonth(), day.getDate() + 1, ); const entries = reservations.filter( (reservation) => new Date(reservation.starts_at) < dayEnd && new Date(reservation.ends_at) > dayStart, ); const isCurrentMonth = day.getMonth() === monthNumber - 1; const isToday = sameDay(day, new Date()); return (
entries.length > 0 && handleDayClick(day)} className={cn( 'min-h-28 border-r border-b p-1.5 last:border-r-0 sm:min-h-36 sm:p-2 cursor-pointer', !isCurrentMonth && 'bg-slate-50/70 text-muted-foreground dark:bg-slate-950/40', entries.length > 0 && 'hover:bg-slate-50 dark:hover:bg-slate-900/50', )} > {day.getDate()}
{entries .slice(0, 3) .map((reservation) => (
item.asset.name).join(', ')}`} className={cn( 'truncate rounded border-l-2 bg-slate-100 px-1.5 py-1 text-[10px] font-medium sm:text-xs dark:bg-slate-900', statusBorder( reservation.status, ), )} > {reservation.reference} {global && reservation.requester ? ` · ${reservation.requester.name}` : ''}
))} {entries.length > 3 && (

+{entries.length - 3} pedido(s)

)}
); })}
{reservations.length === 0 && (
Não existem requisições neste mês.
)} setSelectedDay(null)}> Requisições do dia {selectedDay && (() => { const dayStart = new Date( selectedDay.getFullYear(), selectedDay.getMonth(), selectedDay.getDate(), ); const dayEnd = new Date( selectedDay.getFullYear(), selectedDay.getMonth(), selectedDay.getDate() + 1, ); const dayEntries = reservations.filter( (reservation) => new Date(reservation.starts_at) < dayEnd && new Date(reservation.ends_at) > dayStart, ); return (

{formatDate(selectedDay)}

{dayEntries.length === 0 ? (

Não existem requisições neste dia.

) : (
{dayEntries.map((reservation) => (
{reservation.reference} {getStatusLabel(reservation.status)}
{global && reservation.requester && (
{reservation.requester.name} {reservation.requester.department && ( · {reservation.requester.department} )}
)}
{formatTime(reservation.starts_at)} - {formatTime(reservation.ends_at)}
{reservation.items.map((item) => item.asset.name).join(', ')}
))}
)}
); })()}
); } function Legend({ color, label }: { color: string; label: string }) { return ( {label} ); } function monthKey(date: Date): string { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; } function sameDay(left: Date, right: Date): boolean { return ( left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate() ); } function statusBorder(status: string): string { if (['cancelled', 'rejected', 'expired', 'no_show'].includes(status)) { return 'border-slate-400 opacity-60'; } if (['approved', 'ready', 'delivered', 'in_use'].includes(status)) { return 'border-emerald-500'; } return 'border-amber-500'; } ReservationCalendar.layout = { breadcrumbs: [{ title: 'Calendário', href: '/requisicoes/calendario' }], };