Files
dashboard/components/shared/CalendarView.tsx
T
Alberto-Audrix 32a36cceff
CI / lint-and-test (push) Canceled after 0s
first commit
2026-07-28 08:55:05 +07:00

133 lines
4.4 KiB
TypeScript

import React, { useState, useMemo } from 'react';
interface CalendarEvent {
id: string;
date: Date;
text: string;
color: string;
}
interface CalendarViewProps {
events: CalendarEvent[];
onDateSelect: (date: Date) => void;
selectedDate: Date | null;
}
const EventPill: React.FC<{ text: string; color: string }> = ({ text, color }) => (
<div className={`text-white text-[10px] px-1.5 py-0.5 rounded-full truncate ${color}`}>
{text}
</div>
);
const CalendarView: React.FC<CalendarViewProps> = ({ events, onDateSelect, selectedDate }) => {
const [currentMonth, setCurrentMonth] = useState(new Date());
const changeMonth = (amount: number) => {
setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + amount, 1));
};
const calendarGrid = useMemo(() => {
const year = currentMonth.getFullYear();
const month = currentMonth.getMonth();
const firstDayOfMonth = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const grid = [];
let dayCounter = 1;
for (let i = 0; i < 6; i++) {
// 6 weeks for full display
const week = [];
for (let j = 0; j < 7; j++) {
if ((i === 0 && j < firstDayOfMonth) || dayCounter > daysInMonth) {
week.push(null);
} else {
const date = new Date(year, month, dayCounter);
week.push(date);
dayCounter++;
}
}
grid.push(week);
if (dayCounter > daysInMonth) break;
}
return grid;
}, [currentMonth]);
const isSameDay = (d1: Date, d2: Date) =>
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate();
return (
<div className="w-full">
<div className="flex justify-between items-center mb-4 px-2">
<button
onClick={() => changeMonth(-1)}
className="text-gray-600 hover:text-red-600 p-2 rounded-full w-10 h-10 flex items-center justify-center hover:bg-gray-100"
>
<i className="fa-solid fa-chevron-left"></i>
</button>
<h3 className="text-lg font-bold text-gray-800">
{currentMonth.toLocaleString('id-ID', { month: 'long', year: 'numeric' })}
</h3>
<button
onClick={() => changeMonth(1)}
className="text-gray-600 hover:text-red-600 p-2 rounded-full w-10 h-10 flex items-center justify-center hover:bg-gray-100"
>
<i className="fa-solid fa-chevron-right"></i>
</button>
</div>
<div className="grid grid-cols-7 gap-1 text-center text-xs font-semibold text-gray-500 mb-2">
{['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'].map((day) => (
<div key={day}>{day}</div>
))}
</div>
<div className="grid grid-cols-7 gap-1">
{calendarGrid.flat().map((date, index) => {
if (!date) {
return <div key={`empty-${index}`} className="h-24 bg-gray-50 rounded-md"></div>;
}
const isToday = isSameDay(date, new Date());
const isSelected = selectedDate ? isSameDay(date, selectedDate) : false;
const dayEvents = events.filter((e) => isSameDay(e.date, date));
return (
<div
key={date.toString()}
onClick={() => onDateSelect(date)}
className={`h-24 p-1.5 border rounded-md flex flex-col cursor-pointer transition-colors ${
isSelected
? 'bg-red-100 border-red-300'
: 'bg-white hover:bg-gray-100 border-gray-200'
}`}
>
<span
className={`font-semibold text-xs flex items-center justify-center w-6 h-6 rounded-full ${
isToday ? 'bg-red-600 text-white' : 'text-gray-700'
}`}
>
{date.getDate()}
</span>
<div className="mt-1 space-y-1 overflow-hidden">
{dayEvents.slice(0, 2).map((event) => (
<EventPill key={event.id} text={event.text} color={event.color} />
))}
{dayEvents.length > 2 && (
<div className="text-[10px] text-gray-500 font-semibold text-center">
+{dayEvents.length - 2} lagi
</div>
)}
</div>
</div>
);
})}
</div>
</div>
);
};
export default CalendarView;