Spaces:
Running
Running
| // Initialize Lucide icons | |
| document.addEventListener('DOMContentLoaded', function() { | |
| lucide.createIcons(); | |
| initializeDate(); | |
| initializeMockPatients(); | |
| }); | |
| // Sidebar Management | |
| let sidebarCollapsed = false; | |
| function toggleSidebar() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const overlay = document.getElementById('sidebarOverlay'); | |
| sidebar.classList.toggle('-translate-x-full'); | |
| overlay.classList.toggle('active'); | |
| } | |
| function toggleSidebarDesktop() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const mainContent = document.querySelector('.flex-1.flex.flex-col'); | |
| sidebarCollapsed = !sidebarCollapsed; | |
| if (sidebarCollapsed) { | |
| sidebar.classList.remove('w-72'); | |
| sidebar.classList.add('w-20', 'collapsed'); | |
| } else { | |
| sidebar.classList.remove('w-20', 'collapsed'); | |
| sidebar.classList.add('w-72'); | |
| } | |
| // Re-render icons to ensure proper sizing | |
| setTimeout(() => lucide.createIcons(), 300); | |
| } | |
| function toggleSubmenu() { | |
| const submenu = document.getElementById('appointmentsSubmenu'); | |
| const icon = document.getElementById('submenuIcon'); | |
| submenu.classList.toggle('expanded'); | |
| if (submenu.classList.contains('expanded')) { | |
| icon.style.transform = 'rotate(180deg)'; | |
| } else { | |
| icon.style.transform = 'rotate(0deg)'; | |
| } | |
| } | |
| // Patient Type Management | |
| let currentPatientType = 'new'; | |
| function setPatientType(type) { | |
| currentPatientType = type; | |
| const btnNew = document.getElementById('btnNewPatient'); | |
| const btnExisting = document.getElementById('btnExistingPatient'); | |
| const newForm = document.getElementById('newPatientForm'); | |
| const existingForm = document.getElementById('existingPatientForm'); | |
| if (type === 'new') { | |
| btnNew.classList.add('active'); | |
| btnExisting.classList.remove('active'); | |
| newForm.classList.remove('hidden'); | |
| existingForm.classList.add('hidden'); | |
| } else { | |
| btnNew.classList.remove('active'); | |
| btnExisting.classList.add('active'); | |
| newForm.classList.add('hidden'); | |
| existingForm.classList.remove('hidden'); | |
| } | |
| } | |
| // Mock Patient Data | |
| const mockPatients = [ | |
| { id: 1, name: 'John Anderson', email: 'john.a@email.com', phone: '(555) 123-4567', age: 45, lastVisit: '2024-01-15' }, | |
| { id: 2, name: 'Maria Garcia', email: 'maria.g@email.com', phone: '(555) 234-5678', age: 32, lastVisit: '2024-02-20' }, | |
| { id: 3, name: 'Robert Chen', email: 'robert.c@email.com', phone: '(555) 345-6789', age: 28, lastVisit: '2024-03-01' }, | |
| { id: 4, name: 'Sarah Johnson', email: 'sarah.j@email.com', phone: '(555) 456-7890', age: 56, lastVisit: '2024-01-28' }, | |
| { id: 5, name: 'Michael Brown', email: 'mike.b@email.com', phone: '(555) 567-8901', age: 41, lastVisit: '2024-02-14' }, | |
| { id: 6, name: 'Emily Davis', email: 'emily.d@email.com', phone: '(555) 678-9012', age: 29, lastVisit: '2024-03-10' } | |
| ]; | |
| function initializeMockPatients() { | |
| // Pre-populate with all patients | |
| displayPatientResults(mockPatients); | |
| } | |
| function searchPatients(query) { | |
| const filtered = mockPatients.filter(patient => | |
| patient.name.toLowerCase().includes(query.toLowerCase()) || | |
| patient.email.toLowerCase().includes(query.toLowerCase()) || | |
| patient.phone.includes(query) | |
| ); | |
| displayPatientResults(filtered); | |
| } | |
| function displayPatientResults(patients) { | |
| const container = document.getElementById('patientResults'); | |
| if (patients.length === 0) { | |
| container.innerHTML = ` | |
| <div class="text-center py-8 text-gray-400"> | |
| <p class="text-sm">No patients found</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| container.innerHTML = patients.map(patient => ` | |
| <div onclick="selectPatient(${patient.id})" class="flex items-center gap-3 p-3 rounded-xl border border-gray-200 hover:border-blue-500 hover:bg-blue-50 cursor-pointer transition-all group"> | |
| <div class="w-10 h-10 bg-gradient-to-br from-blue-400 to-blue-600 rounded-full flex items-center justify-center text-white font-semibold text-sm"> | |
| ${patient.name.split(' ').map(n => n[0]).join('')} | |
| </div> | |
| <div class="flex-1 min-w-0"> | |
| <p class="font-medium text-gray-900 truncate group-hover:text-blue-700">${patient.name}</p> | |
| <p class="text-xs text-gray-500 truncate">${patient.email} • ${patient.phone}</p> | |
| </div> | |
| <div class="text-xs text-gray-400"> | |
| Age: ${patient.age} | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| function selectPatient(id) { | |
| const patient = mockPatients.find(p => p.id === id); | |
| showToast(`Selected patient: ${patient.name}`); | |
| // In a real app, this would fill the form or mark as selected | |
| } | |
| // Category Selection | |
| function selectCategory(category) { | |
| // Remove active class from all | |
| document.querySelectorAll('.category-btn').forEach(btn => { | |
| btn.classList.remove('border-blue-500', 'bg-blue-50', 'border-red-500', 'bg-red-50'); | |
| btn.classList.add('border-gray-200'); | |
| }); | |
| // Add active class to selected | |
| const selectedBtn = document.querySelector(`[data-category="${category}"]`); | |
| if (category === 'emergency') { | |
| selectedBtn.classList.remove('border-gray-200'); | |
| selectedBtn.classList.add('border-red-500', 'bg-red-50'); | |
| } else { | |
| selectedBtn.classList.remove('border-gray-200'); | |
| selectedBtn.classList.add('border-blue-500', 'bg-blue-50'); | |
| } | |
| document.getElementById('selectedCategory').value = category; | |
| } | |
| // Time Slot Management | |
| const timeSlotsData = { | |
| 'dr-mitchell': ['09:00 AM', '09:30 AM', '10:00 AM', '10:30 AM', '11:00 AM', '02:00 PM', '02:30 PM', '03:00 PM', '03:30 PM', '04:00 PM'], | |
| 'dr-johnson': ['08:00 AM', '08:30 AM', '09:00 AM', '11:30 AM', '01:00 PM', '01:30 PM', '03:30 PM', '04:30 PM'], | |
| 'dr-williams': ['09:00 AM', '10:30 AM', '12:00 PM', '02:30 PM', '03:30 PM', '04:30 PM'], | |
| 'dr-brown': ['08:30 AM', '09:30 AM', '10:30 AM', '01:30 PM', '02:30 PM', '03:00 PM'], | |
| 'dr-davis': ['09:00 AM', '11:00 AM', '01:00 PM', '02:00 PM', '04:00 PM', '04:30 PM'] | |
| }; | |
| // Randomly book some slots to simulate reality | |
| const bookedSlots = {}; | |
| function initializeDate() { | |
| const today = new Date().toISOString().split('T')[0]; | |
| document.getElementById('appointmentDate').value = today; | |
| document.getElementById('appointmentDate').min = today; | |
| document.getElementById('currentDate').textContent = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); | |
| } | |
| function updateTimeSlots() { | |
| const physician = document.getElementById('physicianSelect').value; | |
| const date = document.getElementById('appointmentDate').value; | |
| const container = document.getElementById('timeSlotsContainer'); | |
| const dateDisplay = document.getElementById('selectedDateDisplay'); | |
| if (!physician || !date) { | |
| container.innerHTML = ` | |
| <div class="text-center py-12 text-gray-400"> | |
| <i data-lucide="calendar-days" class="w-12 h-12 mx-auto mb-3 opacity-50"></i> | |
| <p class="text-sm">Please select a physician and date to view available time slots</p> | |
| </div> | |
| `; | |
| lucide.createIcons(); | |
| return; | |
| } | |
| dateDisplay.textContent = new Date(date).toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' }); | |
| const slots = timeSlotsData[physician] || []; | |
| const booked = bookedSlots[`${physician}-${date}`] || []; | |
| // Generate morning and afternoon sections | |
| const morningSlots = slots.filter(time => { | |
| const hour = parseInt(time.split(':')[0]); | |
| const isPM = time.includes('PM'); | |
| return (!isPM && hour < 12) || (isPM && hour === 12); | |
| }); | |
| const afternoonSlots = slots.filter(time => { | |
| const hour = parseInt(time.split(':')[0]); | |
| const isPM = time.includes('PM'); | |
| return isPM && hour !== 12; | |
| }); | |
| let html = ''; | |
| if (morningSlots.length > 0) { | |
| html += ` | |
| <div class="mb-4"> | |
| <p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Morning</p> | |
| <div class="grid grid-cols-2 gap-2"> | |
| ${morningSlots.map(time => createTimeSlotButton(time, booked.includes(time))).join('')} | |
| </div> | |
| </div> | |
| `; | |
| } | |
| if (afternoonSlots.length > 0) { | |
| html += ` | |
| <div> | |
| <p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Afternoon</p> | |
| <div class="grid grid-cols-2 gap-2"> | |
| ${afternoonSlots.map(time => createTimeSlotButton(time, booked.includes(time))).join('')} | |
| </div> | |
| </div> | |
| `; | |
| } | |
| container.innerHTML = html; | |
| } | |
| function createTimeSlotButton(time, isBooked) { | |
| if (isBooked) { | |
| return `<button disabled class="time-slot booked py-2 px-3 rounded-lg border border-gray-200 text-sm font-medium">${time}</button>`; | |
| } | |
| return `<button onclick="selectTime('${time}')" class="time-slot py-2 px-3 rounded-lg border border-gray-200 text-sm font-medium text-gray-700 hover:border-blue-500 hover:text-blue-600 bg-white" data-time="${time}">${time}</button>`; | |
| } | |
| function selectTime(time) { | |
| // Remove previous selection | |
| document.querySelectorAll('.time-slot').forEach(btn => { | |
| btn.classList.remove('selected'); | |
| }); | |
| // Add selection to clicked button | |
| const buttons = document.querySelectorAll(`[data-time="${time}"]`); | |
| buttons.forEach(btn => btn.classList.add('selected')); | |
| // Show selected time display | |
| document.getElementById('selectedTimeDisplay').classList.remove('hidden'); | |
| document.getElementById('confirmedTime').textContent = time; | |
| } | |
| // Form Actions | |
| function saveAppointment() { | |
| const physician = document.getElementById('physicianSelect').value; | |
| const date = document.getElementById('appointmentDate').value; | |
| const category = document.getElementById('selectedCategory').value; | |
| const selectedTime = document.querySelector('.time-slot.selected'); | |
| if (!physician) { | |
| showToast('Please select a physician', 'error'); | |
| return; | |
| } | |
| if (!date) { | |
| showToast('Please select a date', 'error'); | |
| return; | |
| } | |
| if (!category) { | |
| showToast('Please select an appointment category', 'error'); | |
| return; | |
| } | |
| if (!selectedTime) { | |
| showToast('Please select a time slot', 'error'); | |
| return; | |
| } | |
| // Simulate saving | |
| const time = selectedTime.getAttribute('data-time'); | |
| // Mark slot as booked | |
| const key = `${physician}-${date}`; | |
| if (!bookedSlots[key]) bookedSlots[key] = []; | |
| bookedSlots[key].push(time); | |
| showToast('Appointment scheduled successfully!'); | |
| // Reset after delay | |
| setTimeout(() => { | |
| resetForm(); | |
| }, 2000); | |
| } | |
| function resetForm() { | |
| // Reset patient type | |
| setPatientType('new'); | |
| // Reset inputs | |
| document.querySelectorAll('input[type="text"], input[type="email"], input[type="tel"], textarea').forEach(input => { | |
| input.value = ''; | |
| }); | |
| document.getElementById('physicianSelect').value = ''; | |
| document.getElementById('selectedCategory').value = ''; | |
| // Reset categories | |
| document.querySelectorAll('.category-btn').forEach(btn => { | |
| btn.classList.remove('border-blue-500', 'bg-blue-50', 'border-red-500', 'bg-red-50'); | |
| btn.classList.add('border-gray-200'); | |
| }); | |
| // Reset time slots | |
| document.getElementById('timeSlotsContainer').innerHTML = ` | |
| <div class="text-center py-12 text-gray-400"> | |
| <i data-lucide="calendar-days" class="w-12 h-12 mx-auto mb-3 opacity-50"></i> | |
| <p class="text-sm">Please select a physician and date to view available time slots</p> | |
| </div> | |
| `; | |
| document.getElementById('selectedTimeDisplay').classList.add('hidden'); | |
| document.getElementById('selectedDateDisplay').textContent = 'Select a date to view times'; | |
| // Reset date to today | |
| initializeDate(); | |
| lucide.createIcons(); | |
| } | |
| function showToast(message, type = 'success') { | |
| const toast = document.getElementById('toast'); | |
| const toastMessage = document.getElementById('toastMessage'); | |
| toastMessage.textContent = message; | |
| if (type === 'error') { | |
| toast.querySelector('i').classList.remove('text-green-400'); | |
| toast.querySelector('i').classList.add('text-red-400'); | |
| toast.querySelector('i').setAttribute('data-lucide', 'alert-circle'); | |
| } else { | |
| toast.querySelector('i').classList.remove('text-red-400'); | |
| toast.querySelector('i').classList.add('text-green-400'); | |
| toast.querySelector('i').setAttribute('data-lucide', 'check-circle'); | |
| } | |
| lucide.createIcons(); | |
| toast.classList.remove('translate-y-20', 'opacity-0'); | |
| setTimeout(() => { | |
| toast.classList.add('translate-y-20', 'opacity-0'); | |
| }, 3000); | |
| } | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', function(e) { | |
| const sidebar = document.getElementById('sidebar'); | |
| const toggleBtn = document.querySelector('button[onclick="toggleSidebar()"]'); | |
| if (window.innerWidth < 1024) { | |
| if (!sidebar.contains(e.target) && !toggleBtn.contains(e.target)) { | |
| sidebar.classList.add('-translate-x-full'); | |
| document.getElementById('sidebarOverlay').classList.remove('active'); | |
| } | |
| } | |
| }); |