Snaplocal / client /src /components /SmartCalendar.jsx
Kuruva Laxmi
SnapLocal MVP - Complete platform with 4 MVP features
0f8617c
Raw
History Blame Contribute Delete
5.94 kB
import { useState } from 'react';
import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, Check, X } from 'lucide-react';
const SmartCalendar = ({ availability = [], bookings = [], onToggleDate }) => {
const [currentDate, setCurrentDate] = useState(new Date());
const daysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
const firstDayOfMonth = (year, month) => new Date(year, month, 1).getDay();
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const monthNames = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
const prevMonth = () => setCurrentDate(new Date(year, month - 1, 1));
const nextMonth = () => setCurrentDate(new Date(year, month + 1, 1));
const days = [];
const totalDays = daysInMonth(year, month);
const startDay = firstDayOfMonth(year, month);
// Padding for previous month
for (let i = 0; i < startDay; i++) {
days.push({ day: null, fullDate: null });
}
// Current month days
for (let i = 1; i <= totalDays; i++) {
const fullDate = new Date(year, month, i).toISOString().split('T')[0];
const isAvailable = availability.includes(fullDate);
const dayBookings = bookings.filter(b => b.date.startsWith(fullDate));
days.push({ day: i, fullDate, isAvailable, bookings: dayBookings });
}
return (
<div className="bg-white rounded-[2rem] border border-gray-100 shadow-sm overflow-hidden animate-in fade-in duration-500">
<div className="p-6 bg-gray-50/50 border-b border-gray-50 flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="bg-blue-600 p-2 rounded-xl text-white">
<CalendarIcon size={18} />
</div>
<h3 className="font-black text-gray-900 tracking-tight">{monthNames[month]} {year}</h3>
</div>
<div className="flex items-center space-x-2">
<button onClick={prevMonth} className="p-2 hover:bg-gray-200 rounded-xl transition text-gray-400 hover:text-gray-900">
<ChevronLeft size={20} />
</button>
<button onClick={nextMonth} className="p-2 hover:bg-gray-200 rounded-xl transition text-gray-400 hover:text-gray-900">
<ChevronRight size={20} />
</button>
</div>
</div>
<div className="p-4">
<div className="grid grid-cols-7 gap-1 mb-4">
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => (
<div key={d} className="text-center text-[10px] font-black text-gray-400 uppercase tracking-widest py-2">
{d}
</div>
))}
{days.map((d, i) => (
<div
key={i}
onClick={() => d.fullDate && onToggleDate(d.fullDate)}
className={`
relative aspect-square flex flex-col items-center justify-center rounded-2xl cursor-pointer transition-all duration-300 group
${!d.day ? 'pointer-events-none' : 'hover:scale-95'}
${d.isAvailable ? 'bg-blue-50 text-blue-700 border border-blue-100' : 'text-gray-900 hover:bg-gray-50'}
${d.bookings.length > 0 ? 'ring-2 ring-indigo-600 ring-offset-2' : ''}
`}
>
<span className="text-sm font-bold z-10">{d.day}</span>
{d.day && (
<>
<div className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity">
{d.isAvailable ? <X size={10} className="text-red-400" /> : <Check size={10} className="text-green-400" />}
</div>
{d.bookings.length > 0 && (
<div className="absolute bottom-2 w-1.5 h-1.5 bg-indigo-600 rounded-full shadow-lg shadow-indigo-100"></div>
)}
{d.isAvailable && !d.bookings.length && (
<div className="absolute inset-0 bg-blue-100/50 rounded-2xl animate-pulse"></div>
)}
</>
)}
</div>
))}
</div>
<div className="mt-6 pt-6 border-t border-gray-50 flex items-center justify-between px-2">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-blue-50 border border-blue-100 rounded-sm"></div>
<span className="text-[10px] font-black uppercase text-gray-400 tracking-wider">Available</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-indigo-600 rounded-full"></div>
<span className="text-[10px] font-black uppercase text-gray-400 tracking-wider">Booked</span>
</div>
</div>
<p className="text-[9px] font-bold text-gray-300 italic italic">Click a date to toggle</p>
</div>
</div>
</div>
);
};
export default SmartCalendar;