| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Kalender Multikultural</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet"> |
| <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| .calendar-day:hover { |
| transform: scale(1.05); |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| } |
| .event-dot { |
| width: 6px; |
| height: 6px; |
| border-radius: 50%; |
| display: inline-block; |
| margin-right: 2px; |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| |
| <header class="flex justify-between items-center mb-8" data-aos="fade-down"> |
| <h1 class="text-3xl font-bold text-indigo-800">Kalender Multikultural</h1> |
| <div class="flex items-center space-x-4"> |
| <button id="today-btn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition"> |
| Hari Ini |
| </button> |
| <div class="relative"> |
| <button id="month-selector" class="flex items-center px-4 py-2 bg-white rounded-lg shadow hover:bg-gray-50"> |
| <span id="current-month-year" class="font-medium text-gray-700">Januari 2023</span> |
| <i data-feather="chevron-down" class="ml-2 text-gray-500"></i> |
| </button> |
| <div id="month-dropdown" class="hidden absolute z-10 mt-1 w-48 bg-white rounded-md shadow-lg"> |
| |
| </div> |
| </div> |
| <div class="flex"> |
| <button id="prev-month" class="p-2 rounded-full hover:bg-gray-200"> |
| <i data-feather="chevron-left"></i> |
| </button> |
| <button id="next-month" class="p-2 rounded-full hover:bg-gray-200"> |
| <i data-feather="chevron-right"></i> |
| </button> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6" data-aos="fade-up"> |
| |
| <div class="grid grid-cols-7 bg-indigo-600 text-white"> |
| <div class="py-3 text-center font-medium">Minggu</div> |
| <div class="py-3 text-center font-medium">Senin</div> |
| <div class="py-3 text-center font-medium">Selasa</div> |
| <div class="py-3 text-center font-medium">Rabu</div> |
| <div class="py-3 text-center font-medium">Kamis</div> |
| <div class="py-3 text-center font-medium">Jumat</div> |
| <div class="py-3 text-center font-medium">Sabtu</div> |
| </div> |
| |
| |
| <div id="calendar-days" class="grid grid-cols-7 gap-1 p-2"> |
| |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> |
| |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden" data-aos="fade-right"> |
| <div class="bg-red-600 text-white py-3 text-center font-bold"> |
| Kalender Cina |
| </div> |
| <div id="chinese-calendar" class="p-4"> |
| <div class="text-center mb-4"> |
| <h3 id="chinese-month-year" class="text-xl font-semibold"></h3> |
| <p id="chinese-zodiac" class="text-gray-600"></p> |
| </div> |
| <div id="chinese-dates" class="grid grid-cols-7 gap-1"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden" data-aos="fade-left"> |
| <div class="bg-green-600 text-white py-3 text-center font-bold"> |
| Kalender Hijriyah |
| </div> |
| <div id="hijri-calendar" class="p-4"> |
| <div class="text-center mb-4"> |
| <h3 id="hijri-month-year" class="text-xl font-semibold"></h3> |
| <p id="hijri-info" class="text-gray-600"></p> |
| </div> |
| <div id="hijri-dates" class="grid grid-cols-7 gap-1"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
| |
| <div class="bg-white rounded-xl shadow-lg p-6" data-aos="fade-right"> |
| <div class="flex items-center justify-between mb-4"> |
| <h2 class="text-xl font-bold text-indigo-800">Acara Hari Ini</h2> |
| <span id="today-date" class="text-gray-500"></span> |
| </div> |
| <div id="today-events" class="space-y-3"> |
| <div class="text-gray-500 italic">Tidak ada acara hari ini</div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-lg p-6" data-aos="fade-left"> |
| <h2 class="text-xl font-bold text-indigo-800 mb-4">Tambah Acara</h2> |
| <form id="add-event-form" class="space-y-4"> |
| <div> |
| <label for="event-title" class="block text-sm font-medium text-gray-700">Judul Acara</label> |
| <input type="text" id="event-title" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> |
| </div> |
| <div class="grid grid-cols-2 gap-4"> |
| <div> |
| <label for="event-date" class="block text-sm font-medium text-gray-700">Tanggal</label> |
| <input type="date" id="event-date" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> |
| </div> |
| <div> |
| <label for="event-time" class="block text-sm font-medium text-gray-700">Waktu</label> |
| <input type="time" id="event-time" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> |
| </div> |
| </div> |
| <div> |
| <label for="event-color" class="block text-sm font-medium text-gray-700">Warna</label> |
| <select id="event-color" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> |
| <option value="bg-blue-500">Biru</option> |
| <option value="bg-red-500">Merah</option> |
| <option value="bg-green-500">Hijau</option> |
| <option value="bg-yellow-500">Kuning</option> |
| <option value="bg-purple-500">Ungu</option> |
| </select> |
| </div> |
| <button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition"> |
| Tambah Acara |
| </button> |
| </form> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="event-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-xl p-6 w-full max-w-md"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 id="modal-event-title" class="text-xl font-bold"></h3> |
| <button id="close-modal" class="text-gray-500 hover:text-gray-700"> |
| <i data-feather="x"></i> |
| </button> |
| </div> |
| <div class="space-y-2"> |
| <div class="flex items-center"> |
| <i data-feather="calendar" class="text-gray-500 mr-2"></i> |
| <span id="modal-event-date"></span> |
| </div> |
| <div class="flex items-center"> |
| <i data-feather="clock" class="text-gray-500 mr-2"></i> |
| <span id="modal-event-time"></span> |
| </div> |
| </div> |
| <div class="mt-6 flex justify-end space-x-3"> |
| <button id="delete-event" class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 transition"> |
| Hapus |
| </button> |
| <button id="edit-event" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition"> |
| Edit |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const zodiacAnimals = [ |
| "Tikus", "Kerbau", "Harimau", "Kelinci", |
| "Naga", "Ular", "Kuda", "Kambing", |
| "Monyet", "Ayam", "Anjing", "Babi" |
| ]; |
| |
| |
| const chineseMonths = [ |
| "Zhēngyuè", "Èryuè", "Sānyuè", "Sìyuè", |
| "Wǔyuè", "Liùyuè", "Qīyuè", "Bāyuè", |
| "Jiǔyuè", "Shíyuè", "Shíyīyuè", "Shí'èryuè" |
| ]; |
| |
| |
| const hijriMonths = [ |
| "Muharram", "Safar", "Rabi' al-Awwal", "Rabi' al-Thani", |
| "Jumada al-Awwal", "Jumada al-Thani", "Rajab", "Sha'ban", |
| "Ramadan", "Shawwal", "Dhu al-Qi'dah", "Dhu al-Hijjah" |
| ]; |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| feather.replace(); |
| AOS.init(); |
| |
| |
| let currentDate = new Date(); |
| let events = JSON.parse(localStorage.getItem('calendarEvents')) || []; |
| |
| |
| const calendarDays = document.getElementById('calendar-days'); |
| const currentMonthYear = document.getElementById('current-month-year'); |
| const prevMonthBtn = document.getElementById('prev-month'); |
| const nextMonthBtn = document.getElementById('next-month'); |
| const todayBtn = document.getElementById('today-btn'); |
| const monthSelector = document.getElementById('month-selector'); |
| const monthDropdown = document.getElementById('month-dropdown'); |
| const addEventForm = document.getElementById('add-event-form'); |
| const todayEvents = document.getElementById('today-events'); |
| const todayDate = document.getElementById('today-date'); |
| const eventModal = document.getElementById('event-modal'); |
| const closeModal = document.getElementById('close-modal'); |
| const deleteEventBtn = document.getElementById('delete-event'); |
| const editEventBtn = document.getElementById('edit-event'); |
| |
| |
| const monthNames = [ |
| "Januari", "Februari", "Maret", "April", "Mei", "Juni", |
| "Juli", "Agustus", "September", "Oktober", "November", "Desember" |
| ]; |
| |
| |
| const dayNames = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"]; |
| |
| |
| function renderCalendar() { |
| |
| calendarDays.innerHTML = ''; |
| |
| |
| currentMonthYear.textContent = `${monthNames[currentDate.getMonth()]} ${currentDate.getFullYear()}`; |
| |
| |
| const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); |
| const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); |
| const totalDays = lastDay.getDate(); |
| |
| |
| const firstDayIndex = firstDay.getDay(); |
| |
| |
| const prevLastDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0).getDate(); |
| |
| |
| const nextDays = 7 - ((firstDayIndex + totalDays) % 7); |
| |
| |
| for (let i = firstDayIndex; i > 0; i--) { |
| const day = prevLastDay - i + 1; |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'calendar-day p-2 text-center text-gray-400'; |
| dayElement.textContent = day; |
| calendarDays.appendChild(dayElement); |
| } |
| |
| |
| const today = new Date(); |
| for (let i = 1; i <= totalDays; i++) { |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'calendar-day p-2 text-center cursor-pointer transition'; |
| |
| |
| const isToday = i === today.getDate() && |
| currentDate.getMonth() === today.getMonth() && |
| currentDate.getFullYear() === today.getFullYear(); |
| |
| if (isToday) { |
| dayElement.classList.add('bg-indigo-100', 'font-bold'); |
| } |
| |
| dayElement.textContent = i; |
| |
| |
| const dayEvents = events.filter(event => { |
| const eventDate = new Date(event.date); |
| return eventDate.getDate() === i && |
| eventDate.getMonth() === currentDate.getMonth() && |
| eventDate.getFullYear() === currentDate.getFullYear(); |
| }); |
| |
| if (dayEvents.length > 0) { |
| const eventDots = document.createElement('div'); |
| eventDots.className = 'flex justify-center mt-1'; |
| |
| dayEvents.slice(0, 3).forEach(event => { |
| const dot = document.createElement('span'); |
| dot.className = `event-dot ${event.color}`; |
| eventDots.appendChild(dot); |
| }); |
| |
| if (dayEvents.length > 3) { |
| const moreDot = document.createElement('span'); |
| moreDot.className = 'event-dot bg-gray-300'; |
| eventDots.appendChild(moreDot); |
| } |
| |
| dayElement.appendChild(eventDots); |
| } |
| |
| |
| dayElement.addEventListener('click', () => { |
| showDayEvents(i); |
| }); |
| |
| calendarDays.appendChild(dayElement); |
| } |
| |
| |
| for (let i = 1; i <= nextDays; i++) { |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'calendar-day p-2 text-center text-gray-400'; |
| dayElement.textContent = i; |
| calendarDays.appendChild(dayElement); |
| } |
| |
| |
| updateTodayEvents(); |
| } |
| |
| |
| function showDayEvents(day) { |
| const modalTitle = document.getElementById('modal-event-title'); |
| const modalDate = document.getElementById('modal-event-date'); |
| const modalTime = document.getElementById('modal-event-time'); |
| |
| const selectedDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day); |
| const dayEvents = events.filter(event => { |
| const eventDate = new Date(event.date); |
| return eventDate.getDate() === day && |
| eventDate.getMonth() === currentDate.getMonth() && |
| eventDate.getFullYear() === currentDate.getFullYear(); |
| }); |
| |
| if (dayEvents.length === 0) { |
| modalTitle.textContent = 'Tidak ada acara'; |
| modalDate.textContent = dayNames[selectedDate.getDay()] + ', ' + day + ' ' + monthNames[currentDate.getMonth()] + ' ' + currentDate.getFullYear(); |
| modalTime.textContent = ''; |
| |
| |
| deleteEventBtn.classList.add('hidden'); |
| editEventBtn.classList.add('hidden'); |
| } else { |
| |
| const event = dayEvents[0]; |
| modalTitle.textContent = event.title; |
| const eventDate = new Date(event.date); |
| modalDate.textContent = dayNames[eventDate.getDay()] + ', ' + eventDate.getDate() + ' ' + monthNames[eventDate.getMonth()] + ' ' + eventDate.getFullYear(); |
| modalTime.textContent = event.time; |
| |
| |
| deleteEventBtn.classList.remove('hidden'); |
| editEventBtn.classList.remove('hidden'); |
| deleteEventBtn.dataset.eventId = event.id; |
| editEventBtn.dataset.eventId = event.id; |
| } |
| |
| eventModal.classList.remove('hidden'); |
| } |
| |
| |
| function updateTodayEvents() { |
| const today = new Date(); |
| todayDate.textContent = dayNames[today.getDay()] + ', ' + today.getDate() + ' ' + monthNames[today.getMonth()] + ' ' + today.getFullYear(); |
| |
| const todayEventsList = events.filter(event => { |
| const eventDate = new Date(event.date); |
| return eventDate.getDate() === today.getDate() && |
| eventDate.getMonth() === today.getMonth() && |
| eventDate.getFullYear() === today.getFullYear(); |
| }); |
| |
| todayEvents.innerHTML = ''; |
| |
| if (todayEventsList.length === 0) { |
| todayEvents.innerHTML = '<div class="text-gray-500 italic">Tidak ada acara hari ini</div>'; |
| } else { |
| todayEventsList.forEach(event => { |
| const eventElement = document.createElement('div'); |
| eventElement.className = 'flex items-center p-3 bg-gray-50 rounded-lg'; |
| |
| const colorDot = document.createElement('div'); |
| colorDot.className = `w-3 h-3 rounded-full mr-3 ${event.color}`; |
| |
| const eventInfo = document.createElement('div'); |
| eventInfo.className = 'flex-1'; |
| |
| const eventTitle = document.createElement('div'); |
| eventTitle.className = 'font-medium'; |
| eventTitle.textContent = event.title; |
| |
| const eventTime = document.createElement('div'); |
| eventTime.className = 'text-sm text-gray-500'; |
| eventTime.textContent = event.time; |
| |
| eventInfo.appendChild(eventTitle); |
| eventInfo.appendChild(eventTime); |
| |
| eventElement.appendChild(colorDot); |
| eventElement.appendChild(eventInfo); |
| |
| todayEvents.appendChild(eventElement); |
| }); |
| } |
| } |
| |
| |
| prevMonthBtn.addEventListener('click', () => { |
| currentDate.setMonth(currentDate.getMonth() - 1); |
| renderCalendar(); |
| }); |
| |
| nextMonthBtn.addEventListener('click', () => { |
| currentDate.setMonth(currentDate.getMonth() + 1); |
| renderCalendar(); |
| }); |
| |
| todayBtn.addEventListener('click', () => { |
| currentDate = new Date(); |
| renderCalendar(); |
| }); |
| |
| monthSelector.addEventListener('click', () => { |
| monthDropdown.classList.toggle('hidden'); |
| |
| if (!monthDropdown.classList.contains('hidden')) { |
| monthDropdown.innerHTML = ''; |
| |
| |
| for (let i = 0; i < 12; i++) { |
| const monthItem = document.createElement('div'); |
| monthItem.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; |
| monthItem.textContent = monthNames[i]; |
| |
| monthItem.addEventListener('click', () => { |
| currentDate.setMonth(i); |
| renderCalendar(); |
| monthDropdown.classList.add('hidden'); |
| }); |
| |
| monthDropdown.appendChild(monthItem); |
| } |
| } |
| }); |
| |
| addEventForm.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| |
| const title = document.getElementById('event-title').value; |
| const date = document.getElementById('event-date').value; |
| const time = document.getElementById('event-time').value; |
| const color = document.getElementById('event-color').value; |
| |
| if (!title || !date || !time) { |
| alert('Harap isi semua field'); |
| return; |
| } |
| |
| const newEvent = { |
| id: Date.now().toString(), |
| title, |
| date, |
| time, |
| color |
| }; |
| |
| events.push(newEvent); |
| localStorage.setItem('calendarEvents', JSON.stringify(events)); |
| |
| |
| addEventForm.reset(); |
| |
| |
| renderCalendar(); |
| |
| |
| alert('Acara berhasil ditambahkan'); |
| }); |
| |
| closeModal.addEventListener('click', () => { |
| eventModal.classList.add('hidden'); |
| }); |
| |
| deleteEventBtn.addEventListener('click', () => { |
| const eventId = deleteEventBtn.dataset.eventId; |
| events = events.filter(event => event.id !== eventId); |
| localStorage.setItem('calendarEvents', JSON.stringify(events)); |
| renderCalendar(); |
| eventModal.classList.add('hidden'); |
| }); |
| |
| |
| document.getElementById('event-date').valueAsDate = new Date(); |
| |
| |
| function renderChineseCalendar() { |
| const chineseCalendar = document.getElementById('chinese-dates'); |
| chineseCalendar.innerHTML = ''; |
| |
| const now = new Date(); |
| const chineseYear = now.getFullYear() - (now.getMonth() < 1 ? 1 : 0) + 2697; |
| const zodiacIndex = (chineseYear - 4) % 12; |
| |
| document.getElementById('chinese-month-year').textContent = |
| `${chineseMonths[now.getMonth()]} ${chineseYear}`; |
| document.getElementById('chinese-zodiac').textContent = |
| `Tahun ${zodiacAnimals[zodiacIndex]} (${zodiacIndex + 1}/12)`; |
| |
| |
| for (let i = 1; i <= 31; i++) { |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'text-center p-1 border border-gray-100'; |
| dayElement.textContent = i; |
| chineseCalendar.appendChild(dayElement); |
| } |
| } |
| |
| |
| function renderHijriCalendar() { |
| const hijriCalendar = document.getElementById('hijri-dates'); |
| hijriCalendar.innerHTML = ''; |
| |
| |
| const now = new Date(); |
| const hijriDate = new Intl.DateTimeFormat('en-u-ca-islamic', { |
| day: 'numeric', |
| month: 'numeric', |
| year: 'numeric' |
| }).format(now); |
| |
| const [hijriMonth, hijriDay, hijriYear] = hijriDate.split('/'); |
| |
| document.getElementById('hijri-month-year').textContent = |
| `${hijriMonths[parseInt(hijriMonth)-1]} ${hijriYear}H`; |
| document.getElementById('hijri-info').textContent = |
| `Hari ini: ${hijriDay} ${hijriMonths[parseInt(hijriMonth)-1]} ${hijriYear}H`; |
| |
| |
| for (let i = 1; i <= 30; i++) { |
| const dayElement = document.createElement('div'); |
| dayElement.className = 'text-center p-1 border border-gray-100'; |
| dayElement.textContent = i; |
| hijriCalendar.appendChild(dayElement); |
| } |
| } |
| |
| |
| renderCalendar(); |
| renderChineseCalendar(); |
| renderHijriCalendar(); |
| }); |
| |
| |
| |
| |
| </script> |
| </body> |
|
|
| <style> |
| #chinese-dates, #hijri-dates { |
| grid-template-columns: repeat(7, minmax(0, 1fr)); |
| } |
| #chinese-calendar .bg-red-600, #hijri-calendar .bg-green-600 { |
| background-color: #dc2626; |
| } |
| #hijri-calendar .bg-green-600 { |
| background-color: #16a34a; |
| } |
| </style> |
| </html> |
|
|