Spaces:
Running
Running
| // Global State | |
| let currentDate = new Date(); | |
| let currentFilter = 'all'; | |
| let isSidebarCollapsed = false; | |
| // Sample Appointment Data | |
| const appointments = [ | |
| { id: 1, patient: "John Smith", time: "09:00 AM", type: "general", color: "bg-blue-500", date: 15 }, | |
| { id: 2, patient: "Emma Wilson", time: "10:30 AM", type: "emergency", color: "bg-red-500", date: 15 }, | |
| { id: 3, patient: "Michael Brown", time: "02:00 PM", type: "followup", color: "bg-green-500", date: 15 }, | |
| { id: 4, patient: "Sarah Davis", time: "11:00 AM", type: "consultation", color: "bg-yellow-500", date: 16 }, | |
| { id: 5, patient: "James Miller", time: "03:30 PM", type: "general", color: "bg-blue-500", date: 16 }, | |
| { id: 6, patient: "Linda Garcia", time: "09:30 AM", type: "emergency", color: "bg-red-500", date: 17 }, | |
| { id: 7, patient: "Robert Taylor", time: "01:00 PM", type: "followup", color: "bg-green-500", date: 18 }, | |
| { id: 8, patient: "Patricia Martinez", time: "10:00 AM", type: "general", color: "bg-blue-500", date: 19 }, | |
| { id: 9, patient: "William Anderson", time: "04:00 PM", type: "consultation", color: "bg-yellow-500", date: 20 }, | |
| { id: 10, patient: "Elizabeth Thomas", time: "11:30 AM", type: "followup", color: "bg-green-500", date: 21 }, | |
| { id: 11, patient: "David Jackson", time: "02:30 PM", type: "general", color: "bg-blue-500", date: 22 }, | |
| { id: 12, patient: "Jennifer White", time: "09:00 AM", type: "emergency", color: "bg-red-500", date: 23 } | |
| ]; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| renderCalendar(); | |
| updateCurrentDate(); | |
| // Check window size for responsive sidebar | |
| window.addEventListener('resize', handleResize); | |
| handleResize(); | |
| }); | |
| // Sidebar Toggle | |
| function toggleSidebar() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const mobileOverlay = document.getElementById('mobileOverlay'); | |
| const sidebarTexts = document.querySelectorAll('.sidebar-text'); | |
| if (window.innerWidth >= 1024) { | |
| // Desktop behavior | |
| isSidebarCollapsed = !isSidebarCollapsed; | |
| if (isSidebarCollapsed) { | |
| sidebar.classList.remove('w-64'); | |
| sidebar.classList.add('w-20'); | |
| sidebarTexts.forEach(text => { | |
| text.style.opacity = '0'; | |
| text.style.width = '0'; | |
| text.style.overflow = 'hidden'; | |
| }); | |
| } else { | |
| sidebar.classList.remove('w-20'); | |
| sidebar.classList.add('w-64'); | |
| sidebarTexts.forEach(text => { | |
| text.style.opacity = '1'; | |
| text.style.width = 'auto'; | |
| text.style.overflow = 'visible'; | |
| }); | |
| } | |
| } else { | |
| // Mobile behavior | |
| if (sidebar.classList.contains('-translate-x-full')) { | |
| sidebar.classList.remove('-translate-x-full'); | |
| mobileOverlay.classList.remove('hidden'); | |
| } else { | |
| sidebar.classList.add('-translate-x-full'); | |
| mobileOverlay.classList.add('hidden'); | |
| } | |
| } | |
| } | |
| function handleResize() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const mobileOverlay = document.getElementById('mobileOverlay'); | |
| if (window.innerWidth >= 1024) { | |
| mobileOverlay.classList.add('hidden'); | |
| if (!isSidebarCollapsed) { | |
| sidebar.classList.remove('-translate-x-full'); | |
| sidebar.classList.add('w-64'); | |
| sidebar.classList.remove('w-20'); | |
| } | |
| } else { | |
| sidebar.classList.add('-translate-x-full'); | |
| sidebar.classList.remove('w-20'); | |
| sidebar.classList.add('w-64'); | |
| } | |
| } | |
| // Calendar Functions | |
| function renderCalendar() { | |
| const year = currentDate.getFullYear(); | |
| const month = currentDate.getMonth(); | |
| // Update header | |
| const monthNames = ["January", "February", "March", "April", "May", "June", | |
| "July", "August", "September", "October", "November", "December" | |
| ]; | |
| document.getElementById('calendarMonth').textContent = `${monthNames[month]} ${year}`; | |
| const firstDay = new Date(year, month, 1).getDay(); | |
| const daysInMonth = new Date(year, month + 1, 0).getDate(); | |
| const daysInPrevMonth = new Date(year, month, 0).getDate(); | |
| const grid = document.getElementById('calendarGrid'); | |
| grid.innerHTML = ''; | |
| // Previous month days | |
| for (let i = firstDay - 1; i >= 0; i--) { | |
| const dayDiv = document.createElement('div'); | |
| dayDiv.className = 'calendar-day bg-gray-50 p-2 border-b border-r border-gray-200 min-h-[120px] text-gray-400'; | |
| dayDiv.innerHTML = `<span class="text-sm font-medium">${daysInPrevMonth - i}</span>`; | |
| grid.appendChild(dayDiv); | |
| } | |
| // Current month days | |
| const today = new Date(); | |
| for (let day = 1; day <= daysInMonth; day++) { | |
| const dayDiv = document.createElement('div'); | |
| const isToday = today.getDate() === day && today.getMonth() === month && today.getFullYear() === year; | |
| dayDiv.className = `calendar-day bg-white p-2 border-b border-r border-gray-200 min-h-[120px] hover:bg-gray-50 transition-colors cursor-pointer ${isToday ? 'bg-blue-50/30' : ''}`; | |
| let dayContent = `<div class="flex justify-between items-start mb-1"> | |
| <span class="text-sm font-semibold ${isToday ? 'bg-blue-600 text-white w-7 h-7 flex items-center justify-center rounded-full' : 'text-gray-700'}">${day}</span> | |
| </div>`; | |
| // Add appointments for this day | |
| const dayAppointments = appointments.filter(apt => apt.date === day); | |
| if (currentFilter !== 'all') { | |
| const filteredApts = dayAppointments.filter(apt => apt.type === currentFilter); | |
| dayContent += generateAppointmentHTML(filteredApts); | |
| } else { | |
| dayContent += generateAppointmentHTML(dayAppointments); | |
| } | |
| dayDiv.innerHTML = dayContent; | |
| dayDiv.onclick = () => openAddAppointment(day); | |
| grid.appendChild(dayDiv); | |
| } | |
| // Next month days to fill grid | |
| const remainingCells = 42 - (firstDay + daysInMonth); | |
| for (let i = 1; i <= remainingCells; i++) { | |
| const dayDiv = document.createElement('div'); | |
| dayDiv.className = 'calendar-day bg-gray-50 p-2 border-b border-r border-gray-200 min-h-[120px] text-gray-400'; | |
| dayDiv.innerHTML = `<span class="text-sm font-medium">${i}</span>`; | |
| grid.appendChild(dayDiv); | |
| } | |
| } | |
| function generateAppointmentHTML(appointments) { | |
| if (appointments.length === 0) return ''; | |
| let html = '<div class="space-y-1 mt-1">'; | |
| appointments.slice(0, 3).forEach(apt => { | |
| html += ` | |
| <div class="${apt.color} bg-opacity-10 border-l-2 ${apt.color.replace('bg-', 'border-')} px-2 py-1 rounded text-xs cursor-pointer hover:opacity-80 transition-opacity"> | |
| <p class="font-semibold text-gray-900 truncate">${apt.time}</p> | |
| <p class="text-gray-600 truncate">${apt.patient}</p> | |
| </div> | |
| `; | |
| }); | |
| if (appointments.length > 3) { | |
| html += `<div class="text-xs text-gray-500 text-center mt-1">+${appointments.length - 3} more</div>`; | |
| } | |
| html += '</div>'; | |
| return html; | |
| } | |
| function changeMonth(direction) { | |
| currentDate.setMonth(currentDate.getMonth() + direction); | |
| renderCalendar(); | |
| } | |
| function goToToday() { | |
| currentDate = new Date(); | |
| renderCalendar(); | |
| } | |
| function changeDateRange(value) { | |
| if (value === 'today') { | |
| // Filter to show only today's appointments in a list view (simplified for demo) | |
| alert('Switched to Today view - showing today\'s schedule'); | |
| } else if (value === 'week') { | |
| alert('Switched to Week view - showing weekly schedule'); | |
| } | |
| // Month view is default | |
| } | |
| // Category Filter | |
| function filterCategory(category) { | |
| currentFilter = category; | |
| // Update UI | |
| document.querySelectorAll('.category-btn').forEach(btn => { | |
| if (btn.dataset.category === category) { | |
| btn.classList.remove('bg-white', 'text-gray-700', 'border', 'border-gray-200'); | |
| btn.classList.add('bg-gray-900', 'text-white'); | |
| btn.querySelector('span').classList.remove('category-dot'); | |
| if (category !== 'all') { | |
| btn.querySelector('span').className = 'w-2 h-2 rounded-full bg-white'; | |
| } | |
| } else { | |
| btn.classList.add('bg-white', 'text-gray-700', 'border', 'border-gray-200'); | |
| btn.classList.remove('bg-gray-900', 'text-white'); | |
| const colors = { | |
| 'general': 'bg-blue-500 text-blue-500', | |
| 'emergency': 'bg-red-500 text-red-500', | |
| 'followup': 'bg-green-500 text-green-500', | |
| 'consultation': 'bg-yellow-500 text-yellow-500' | |
| }; | |
| if (btn.dataset.category !== 'all') { | |
| btn.querySelector('span').className = `w-2 h-2 rounded-full ${colors[btn.dataset.category]} category-dot`; | |
| } else { | |
| btn.querySelector('span').className = 'w-2 h-2 rounded-full bg-gray-900'; | |
| } | |
| } | |
| }); | |
| renderCalendar(); | |
| } | |
| // Modal Functions | |
| function openAddAppointment(day = null) { | |
| const modal = document.getElementById('addModal'); | |
| modal.classList.remove('hidden'); | |
| lucide.createIcons(); | |
| } | |
| function closeAddAppointment() { | |
| document.getElementById('addModal').classList.add('hidden'); | |
| } | |
| function saveAppointment() { | |
| closeAddAppointment(); | |
| // Show success notification (simplified) | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed bottom-4 right-4 bg-green-600 text-white px-6 py-3 rounded-lg shadow-lg z-50 fade-in flex items-center gap-2'; | |
| notification.innerHTML = '<i data-lucide="check-circle" class="w-5 h-5"></i> Appointment saved successfully!'; | |
| document.body.appendChild(notification); | |
| lucide.createIcons(); | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 3000); | |
| } | |
| // Export and Reports | |
| function exportData() { | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed bottom-4 right-4 bg-blue-600 text-white px-6 py-3 rounded-lg shadow-lg z-50 fade-in flex items-center gap-2'; | |
| notification.innerHTML = '<i data-lucide="download" class="w-5 h-5"></i> Exporting calendar data...'; | |
| document.body.appendChild(notification); | |
| lucide.createIcons(); | |
| setTimeout(() => { | |
| notification.innerHTML = '<i data-lucide="check-circle" class="w-5 h-5"></i> Export complete!'; | |
| setTimeout(() => notification.remove(), 2000); | |
| }, 1500); | |
| } | |
| function generateReports() { | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed bottom-4 right-4 bg-purple-600 text-white px-6 py-3 rounded-lg shadow-lg z-50 fade-in flex items-center gap-2'; | |
| notification.innerHTML = '<i data-lucide="file-text" class="w-5 h-5"></i> Generating report...'; | |
| document.body.appendChild(notification); | |
| lucide.createIcons(); | |
| setTimeout(() => { | |
| notification.innerHTML = '<i data-lucide="check-circle" class="w-5 h-5"></i> Report generated!'; | |
| setTimeout(() => notification.remove(), 2000); | |
| }, 1500); | |
| } | |
| // Utility | |
| function updateCurrentDate() { | |
| const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; | |
| document.getElementById('currentDateDisplay').textContent = new Date().toLocaleDateString('en-US', options); | |
| } | |
| // Close modal on outside click | |
| document.getElementById('addModal').addEventListener('click', (e) => { | |
| if (e.target === document.getElementById('addModal')) { | |
| closeAddAppointment(); | |
| } | |
| }); |