Tips & Tricks

Cara Membangun Component Kalender dengan Claude Code

Pelajari cara membangun component kalender menggunakan Claude Code. Dilengkapi contoh kode praktis dan panduan langkah demi langkah.

Kebutuhan Component Kalender

UI kalender dibutuhkan di banyak aplikasi seperti sistem reservasi, manajemen jadwal, pemilihan tanggal, dan lainnya. Bisa mengandalkan library eksternal, tapi jika menginginkan kebebasan desain dan fungsionalitas, membuatnya sendiri adalah yang terbaik. Dengan Claude Code, component kalender yang fleksibel bisa dibangun dengan cepat.

Implementasi Dasar Kalender Tampilan Bulanan

> Buat component kalender tampilan bulanan menggunakan date-fns.
> Dukung perpindahan bulan dan pemilihan tanggal.
import { useState } from 'react';
import {
  startOfMonth, endOfMonth, startOfWeek, endOfWeek,
  eachDayOfInterval, format, addMonths, subMonths, isSameMonth, isSameDay, isToday,
} from 'date-fns';

interface CalendarProps {
  selected?: Date;
  onSelect: (date: Date) => void;
  events?: { date: Date; title: string }[];
}

function Calendar({ selected, onSelect, events = [] }: CalendarProps) {
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const monthStart = startOfMonth(currentMonth);
  const monthEnd = endOfMonth(currentMonth);
  const calendarStart = startOfWeek(monthStart);
  const calendarEnd = endOfWeek(monthEnd);
  const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });

  const weekDays = ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'];

  const getEventsForDay = (day: Date) =>
    events.filter((e) => isSameDay(e.date, day));

  return (
    <div className="w-full max-w-md mx-auto" role="application" aria-label="kalender">
      <div className="flex items-center justify-between mb-4">
        <button onClick={() => setCurrentMonth(subMonths(currentMonth, 1))} aria-label="Bulan sebelumnya">

        </button>
        <h2 className="text-lg font-bold" aria-live="polite">
          {format(currentMonth, 'MMMM yyyy')}
        </h2>
        <button onClick={() => setCurrentMonth(addMonths(currentMonth, 1))} aria-label="Bulan berikutnya">

        </button>
      </div>

      <div className="grid grid-cols-7 gap-px" role="grid">
        {weekDays.map((day) => (
          <div key={day} className="text-center text-sm font-medium text-gray-500 py-2" role="columnheader">
            {day}
          </div>
        ))}

        {days.map((day) => {
          const dayEvents = getEventsForDay(day);
          const isSelected = selected && isSameDay(day, selected);
          const inMonth = isSameMonth(day, currentMonth);

          return (
            <button
              key={day.toISOString()}
              onClick={() => onSelect(day)}
              role="gridcell"
              aria-selected={isSelected}
              className={`p-2 text-center rounded-lg relative ${
                !inMonth ? 'text-gray-300' :
                isSelected ? 'bg-blue-600 text-white' :
                isToday(day) ? 'bg-blue-100 font-bold' :
                'hover:bg-gray-100'
              }`}
            >
              {format(day, 'd')}
              {dayEvents.length > 0 && (
                <span className="absolute bottom-1 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-blue-500" />
              )}
            </button>
          );
        })}
      </div>
    </div>
  );
}

Pemilihan Rentang Tanggal

> Dukung pemilihan rentang tanggal seperti check-in/check-out.
function useDateRange() {
  const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
    start: null,
    end: null,
  });

  const handleSelect = (date: Date) => {
    if (!range.start || (range.start && range.end)) {
      setRange({ start: date, end: null });
    } else if (date < range.start) {
      setRange({ start: date, end: range.start });
    } else {
      setRange({ start: range.start, end: date });
    }
  };

  const isInRange = (date: Date) => {
    if (!range.start || !range.end) return false;
    return date >= range.start && date <= range.end;
  };

  return { range, handleSelect, isInRange };
}

Kalender dengan Tampilan Event

function EventCalendar({ events }: { events: CalendarEvent[] }) {
  const [selectedDate, setSelectedDate] = useState<Date>(new Date());
  const dayEvents = events.filter((e) => isSameDay(e.date, selectedDate));

  return (
    <div className="flex gap-4">
      <Calendar selected={selectedDate} onSelect={setSelectedDate} events={events} />
      <div className="flex-1">
        <h3 className="font-bold mb-2">
          Jadwal {format(selectedDate, 'd MMM')}
        </h3>
        {dayEvents.length === 0 ? (
          <p className="text-gray-500">Tidak ada jadwal</p>
        ) : (
          <ul className="space-y-2">
            {dayEvents.map((event) => (
              <li key={event.id} className="p-3 rounded-lg border">
                <span className="font-medium">{event.title}</span>
                <span className="text-sm text-gray-500 ml-2">
                  {format(event.date, 'HH:mm')}
                </span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

Summary

Dengan Claude Code, component kalender yang fleksibel bisa dibangun sesuai kebutuhan seperti tampilan bulanan, pemilihan rentang tanggal, dan tampilan event. Untuk kombinasi dengan tampilan tabel, lihat Component Tabel, dan untuk dukungan responsif, lihat Responsive Design.

Untuk detail date-fns, lihat Situs Resmi date-fns.

#Claude Code #calendar #React #UI #date-fns