Files
Gesti-n-Reservas-Naturcalab…/src/components/YearlyCalendar.tsx

115 lines
5.8 KiB
TypeScript

import { format, eachDayOfInterval, endOfMonth, startOfMonth, startOfYear, endOfYear, eachMonthOfInterval, getDay } from 'date-fns';
import { es } from 'date-fns/locale';
import type { Reservation } from '../types';
interface Props {
reservations: Reservation[];
year: number;
}
export function YearlyCalendar({ reservations = [], year }: Props) {
const months = eachMonthOfInterval({
start: startOfYear(new Date(year, 0, 1)),
end: endOfYear(new Date(year, 0, 1))
});
const isDayOccupied = (date: Date) => {
if (!reservations || !Array.isArray(reservations)) return false;
return reservations.some(res => {
if (!res.start_date || !res.end_date) return false;
const start = new Date(res.start_date);
const end = new Date(res.end_date);
if (isNaN(start.getTime()) || isNaN(end.getTime())) return false;
// Ajuste de zona horaria simple
const checkDate = new Date(date);
checkDate.setHours(12, 0, 0, 0);
start.setHours(0, 0, 0, 0);
end.setHours(23, 59, 59, 999);
return checkDate >= start && checkDate <= end;
});
};
return (
<div className="p-8 max-w-[1600px] mx-auto animate-in fade-in zoom-in duration-500">
<header className="mb-8 flex justify-between items-end">
<div>
<h1 className="text-4xl font-black text-white tracking-tighter mb-2">Vista Anual {year}</h1>
<p className="text-slate-400 text-lg">Panorama global de ocupación.</p>
</div>
<div className="flex gap-4 items-center bg-slate-800/50 px-4 py-2 rounded-full border border-slate-700">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-emerald-500"></div>
<span className="text-slate-300 text-xs font-bold uppercase tracking-wider">Libre</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-red-500 shadow-[0_0_10px_rgba(239,68,68,0.5)]"></div>
<span className="text-slate-300 text-xs font-bold uppercase tracking-wider">Ocupado</span>
</div>
</div>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{months.map((month) => {
const monthStart = startOfMonth(month);
const monthEnd = endOfMonth(month);
const days = eachDayOfInterval({ start: monthStart, end: monthEnd });
const startDayOfWeek = getDay(monthStart); // 0 = Sunday
// Create empty slots for days before the 1st of the month
const emptySlots = Array(startDayOfWeek).fill(null);
return (
<div key={month.toString()} className="bg-slate-800/40 backdrop-blur-md border border-slate-700/50 rounded-2xl p-4 hover:border-slate-600 transition-colors shadow-lg">
<h3 className="text-lg font-bold text-white mb-4 capitalize text-center border-b border-slate-700/50 pb-2">
{format(month, 'MMMM', { locale: es })}
</h3>
<div className="grid grid-cols-7 gap-1 mb-2">
{['D', 'L', 'M', 'X', 'J', 'V', 'S'].map(d => (
<div key={d} className="text-center text-[10px] font-bold text-slate-500">{d}</div>
))}
</div>
<div className="grid grid-cols-7 gap-1">
{emptySlots.map((_, i) => (
<div key={`empty-${i}`} />
))}
{days.map((day) => {
const occupied = isDayOccupied(day);
return (
<div
key={day.toString()}
className={`
aspect-square rounded-md flex items-center justify-center text-xs font-medium relative group transition-all duration-300
${occupied
? 'bg-red-500/30 text-red-100 shadow-[0_0_10px_rgba(239,68,68,0.2)] hover:bg-red-500/40'
: 'bg-emerald-500/10 text-emerald-500/60 hover:bg-emerald-500/20 hover:text-emerald-400'
}
`}
title={format(day, 'dd/MM/yyyy')}
>
{format(day, 'd')}
{/* Tooltip on hover if occupied (simplified) */}
{occupied && (
<div className="absolute bottom-full mb-2 left-1/2 -translate-x-1/2 hidden group-hover:block whitespace-nowrap z-10 bg-black/80 backdrop-blur text-white text-[10px] px-2 py-1 rounded shadow-lg border border-white/10">
Ocupado
</div>
)}
</div>
);
})}
</div>
</div>
);
})}
</div>
</div>
);
}