thors1's picture
🐳 27/02 - 07:23 - Add a "Call" button with phone icon in main header
eadfd07 verified
// Sample data
const appointments = [
{
id: 'APT-2024-0092',
date: '2024-01-15',
time: '09:00 AM',
duration: '30 min',
provider: { name: 'Dr. Emily Chen', specialty: 'Internal Medicine', avatar: 'http://static.photos/people/320x240/2' },
category: 'Primary Care',
type: 'In-person',
location: 'Main Clinic, Room 304',
status: 'Confirmed',
notes: 2,
reason: 'Annual physical examination',
isUpcoming: true,
history: [
{ status: 'Created', time: 'Jan 8, 2024 2:30 PM', user: 'System' },
{ status: 'Confirmed', time: 'Jan 8, 2024 2:35 PM', user: 'Dr. Chen' }
]
},
{
id: 'APT-2024-0095',
date: '2024-01-18',
time: '02:30 PM',
duration: '45 min',
provider: { name: 'Dr. James Wilson', specialty: 'Dermatology', avatar: 'http://static.photos/people/320x240/3' },
category: 'Dermatology',
type: 'Telehealth',
location: 'Video Consultation',
status: 'Pending',
notes: 0,
reason: 'Rash follow-up consultation',
isUpcoming: true,
history: [
{ status: 'Created', time: 'Jan 10, 2024 10:00 AM', user: 'Sarah Mitchell' }
]
},
{
id: 'APT-2024-0100',
date: '2024-02-05',
time: '11:00 AM',
duration: '60 min',
provider: { name: 'Dr. Sarah Park', specialty: 'Lab Services', avatar: 'http://static.photos/people/320x240/4' },
category: 'Lab',
type: 'In-person',
location: 'Lab Center, 2nd Floor',
status: 'Confirmed',
notes: 1,
reason: 'Blood work and cholesterol panel',
isUpcoming: true,
history: [
{ status: 'Created', time: 'Jan 20, 2024 9:00 AM', user: 'System' },
{ status: 'Confirmed', time: 'Jan 20, 2024 9:15 AM', user: 'Staff' }
]
},
{
id: 'APT-2024-0085',
date: '2023-12-15',
time: '10:00 AM',
duration: '30 min',
provider: { name: 'Dr. Emily Chen', specialty: 'Internal Medicine', avatar: 'http://static.photos/people/320x240/2' },
category: 'Follow-up',
type: 'In-person',
location: 'Main Clinic, Room 304',
status: 'Completed',
notes: 3,
reason: 'Medication review',
isUpcoming: false,
history: [
{ status: 'Created', time: 'Dec 1, 2023 2:00 PM', user: 'System' },
{ status: 'Confirmed', time: 'Dec 1, 2023 2:05 PM', user: 'Dr. Chen' },
{ status: 'Checked-in', time: 'Dec 15, 2023 9:45 AM', user: 'Reception' },
{ status: 'Completed', time: 'Dec 15, 2023 10:30 AM', user: 'Dr. Chen' }
]
},
{
id: 'APT-2024-0078',
date: '2023-11-20',
time: '03:00 PM',
duration: '30 min',
provider: { name: 'Dr. James Wilson', specialty: 'Dermatology', avatar: 'http://static.photos/people/320x240/3' },
category: 'Dermatology',
type: 'In-person',
location: 'Dermatology Suite, Room 105',
status: 'Completed',
notes: 2,
reason: 'Skin examination',
isUpcoming: false,
history: [
{ status: 'Created', time: 'Nov 10, 2023 11:00 AM', user: 'System' },
{ status: 'Confirmed', time: 'Nov 10, 2023 11:05 AM', user: 'Auto-confirm' },
{ status: 'Completed', time: 'Nov 20, 2023 3:30 PM', user: 'Dr. Wilson' }
]
},
{
id: 'APT-2024-0072',
date: '2023-10-28',
time: '09:30 AM',
duration: '30 min',
provider: { name: 'Dr. Emily Chen', specialty: 'Internal Medicine', avatar: 'http://static.photos/people/320x240/2' },
category: 'Primary Care',
type: 'Telehealth',
location: 'Phone Consultation',
status: 'Canceled',
notes: 0,
reason: 'Prescription refill',
isUpcoming: false,
history: [
{ status: 'Created', time: 'Oct 20, 2023 9:00 AM', user: 'Patient Portal' },
{ status: 'Confirmed', time: 'Oct 20, 2023 9:05 AM', user: 'System' },
{ status: 'Canceled', time: 'Oct 27, 2023 4:00 PM', user: 'Sarah Mitchell' }
]
}
];
// State
let currentView = 'list';
let currentCalendarView = 'month';
let selectedAppointment = null;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderListView();
renderCalendar();
renderUpcomingWeek();
// Re-initialize icons after all rendering is done
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
// Sidebar Toggle
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.toggle('-translate-x-full');
overlay.classList.toggle('hidden');
}
// Profile Dropdown
function toggleProfileDropdown() {
const dropdown = document.getElementById('profileDropdown');
dropdown.classList.toggle('hidden');
}
// More Dropdown
function toggleMoreDropdown() {
const dropdown = document.getElementById('moreDropdown');
dropdown.classList.toggle('hidden');
}
// Close dropdowns when clicking outside
document.addEventListener('click', (e) => {
if (!e.target.closest('.relative')) {
const profileDropdown = document.getElementById('profileDropdown');
const moreDropdown = document.getElementById('moreDropdown');
if (profileDropdown) profileDropdown.classList.add('hidden');
if (moreDropdown) moreDropdown.classList.add('hidden');
}
});
// View Switching
function switchView(view) {
currentView = view;
const listView = document.getElementById('listView');
const calendarView = document.getElementById('calendarView');
const listBtn = document.getElementById('listViewBtn');
const calendarBtn = document.getElementById('calendarViewBtn');
const sortDropdown = document.getElementById('sortDropdown');
if (view === 'list') {
listView.classList.remove('hidden');
calendarView.classList.add('hidden');
listBtn.classList.add('bg-teal-50', 'text-teal-700');
listBtn.classList.remove('text-gray-600', 'hover:text-gray-900');
calendarBtn.classList.remove('bg-teal-50', 'text-teal-700');
calendarBtn.classList.add('text-gray-600', 'hover:text-gray-900');
if (sortDropdown && sortDropdown.parentElement) {
sortDropdown.parentElement.style.opacity = '1';
sortDropdown.parentElement.style.pointerEvents = 'auto';
}
} else {
listView.classList.add('hidden');
calendarView.classList.remove('hidden');
calendarBtn.classList.add('bg-teal-50', 'text-teal-700');
calendarBtn.classList.remove('text-gray-600', 'hover:text-gray-900');
listBtn.classList.remove('bg-teal-50', 'text-teal-700');
listBtn.classList.add('text-gray-600', 'hover:text-gray-900');
if (sortDropdown && sortDropdown.parentElement) {
sortDropdown.parentElement.style.opacity = '0.5';
sortDropdown.parentElement.style.pointerEvents = 'none';
}
}
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
// Render List View
function renderListView() {
const upcomingList = document.getElementById('upcomingList');
const historyList = document.getElementById('historyList');
const upcoming = appointments.filter(a => a.isUpcoming);
const history = appointments.filter(a => !a.isUpcoming);
if (upcomingList) {
upcomingList.innerHTML = upcoming.map(appt => createAppointmentRow(appt)).join('');
}
if (historyList) {
historyList.innerHTML = history.map(appt => createAppointmentRow(appt)).join('');
}
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
function createAppointmentRow(appt) {
if (!appt) return '';
const statusColors = {
'Confirmed': 'bg-green-100 text-green-700 border-green-200',
'Pending': 'bg-yellow-100 text-yellow-700 border-yellow-200',
'Completed': 'bg-gray-100 text-gray-700 border-gray-200',
'Canceled': 'bg-red-100 text-red-700 border-red-200',
'No-show': 'bg-orange-100 text-orange-700 border-orange-200'
};
const categoryColors = {
'Primary Care': 'bg-blue-50 text-blue-700 border-blue-200',
'Dermatology': 'bg-purple-50 text-purple-700 border-purple-200',
'Lab': 'bg-pink-50 text-pink-700 border-pink-200',
'Follow-up': 'bg-indigo-50 text-indigo-700 border-indigo-200'
};
const statusClass = statusColors[appt.status] || 'bg-gray-100 text-gray-700 border-gray-200';
const categoryClass = categoryColors[appt.category] || 'bg-gray-100 text-gray-700 border-gray-200';
return `
<tr class="hover:bg-gray-50 transition-colors cursor-pointer" onclick="openDrawer('${appt.id}')">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">${formatDate(appt.date)}</div>
<div class="text-xs text-gray-500">${appt.time || ''}${appt.duration || ''}</div>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<img src="${appt.provider?.avatar || ''}" class="w-8 h-8 rounded-full object-cover" alt="">
<div>
<div class="text-sm font-medium text-gray-900">${appt.provider?.name || ''}</div>
<div class="text-xs text-gray-500">${appt.provider?.specialty || ''}</div>
</div>
</div>
</td>
<td class="px-6 py-4">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border ${categoryClass}">
${appt.category || ''}
</span>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-1.5 text-sm text-gray-600">
<i data-lucide="${appt.type === 'Telehealth' ? 'video' : 'map-pin'}" class="w-3.5 h-3.5"></i>
${appt.type || ''}
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-600 max-w-xs truncate">
${appt.location || ''}
</td>
<td class="px-6 py-4">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border ${statusClass} status-pill">
${appt.status || ''}
</span>
</td>
<td class="px-6 py-4 text-center">
${(appt.notes || 0) > 0 ? `<div class="inline-flex items-center gap-1 text-teal-600"><i data-lucide="file-text" class="w-4 h-4"></i><span class="text-xs font-medium">${appt.notes}</span></div>` : '<span class="text-gray-300">-</span>'}
</td>
<td class="px-6 py-4 text-right">
<div class="flex items-center justify-end gap-2">
<button onclick="event.stopPropagation(); openNewAppointmentModal()" class="p-1.5 hover:bg-gray-200 rounded-lg transition-colors text-gray-400 hover:text-gray-600">
<i data-lucide="pencil" class="w-4 h-4"></i>
</button>
<button onclick="event.stopPropagation()" class="p-1.5 hover:bg-gray-200 rounded-lg transition-colors text-gray-400 hover:text-gray-600">
<i data-lucide="more-vertical" class="w-4 h-4"></i>
</button>
</div>
</td>
</tr>
`;
}
// Calendar Functions
function setCalendarView(view) {
currentCalendarView = view;
document.getElementById('monthViewBtn').className = view === 'month' ? 'px-3 py-1.5 text-sm font-medium rounded-lg bg-teal-50 text-teal-700' : 'px-3 py-1.5 text-sm font-medium rounded-lg text-gray-600 hover:bg-gray-50';
document.getElementById('weekViewBtn').className = view === 'week' ? 'px-3 py-1.5 text-sm font-medium rounded-lg bg-teal-50 text-teal-700' : 'px-3 py-1.5 text-sm font-medium rounded-lg text-gray-600 hover:bg-gray-50';
document.getElementById('dayViewBtn').className = view === 'day' ? 'px-3 py-1.5 text-sm font-medium rounded-lg bg-teal-50 text-teal-700' : 'px-3 py-1.5 text-sm font-medium rounded-lg text-gray-600 hover:bg-gray-50';
}
function renderCalendar() {
const grid = document.getElementById('calendarGrid');
if (!grid) return;
const daysInMonth = 31; // simplified
const startDay = 1; // Monday
let html = '';
// Empty cells for start of month
for (let i = 0; i < startDay; i++) {
html += `<div class="calendar-day bg-gray-50"></div>`;
}
// Days
for (let day = 1; day <= daysInMonth; day++) {
const dayAppointments = appointments.filter(a => {
if (!a || !a.date) return false;
const apptDate = new Date(a.date);
return apptDate && apptDate.getDate() === day;
});
const isToday = day === 15; // Example
html += `
<div class="calendar-day p-2 ${isToday ? 'bg-teal-50/30' : ''}">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium ${isToday ? 'text-teal-700 bg-teal-100 w-6 h-6 rounded-full flex items-center justify-center' : 'text-gray-700'}">${day}</span>
${dayAppointments.length > 3 ? `<span class="text-xs text-gray-500 cursor-pointer hover:text-teal-600">+${dayAppointments.length - 2} more</span>` : ''}
</div>
<div class="space-y-1">
${dayAppointments.slice(0, 3).map(appt => {
if (!appt || !appt.status) return '';
const statusColors = {
'Confirmed': 'border-l-teal-500',
'Pending': 'border-l-yellow-500',
'Completed': 'border-l-gray-400',
'Canceled': 'border-l-red-400'
};
const lastName = appt.provider && appt.provider.name ? appt.provider.name.split(' ')[1] || '' : '';
return `
<div onclick="openDrawer('${appt.id}')" class="calendar-event text-xs p-1.5 bg-white border-l-2 ${statusColors[appt.status] || 'border-l-gray-300'} rounded shadow-sm cursor-pointer hover:bg-gray-50">
<div class="font-medium text-gray-900 truncate">${appt.time || ''}</div>
<div class="text-gray-500 truncate">${lastName}</div>
<div class="flex items-center gap-1 mt-0.5">
<span class="w-1.5 h-1.5 rounded-full bg-teal-500"></span>
<span class="text-[10px] text-gray-400">${appt.category || ''}</span>
</div>
</div>
`;
}).join('')}
</div>
</div>
`;
}
grid.innerHTML = html;
}
function renderUpcomingWeek() {
const container = document.getElementById('upcomingWeekList');
if (!container) return;
const upcoming = appointments.filter(a => a && a.isUpcoming).slice(0, 5);
container.innerHTML = upcoming.map(appt => `
<div onclick="openDrawer('${appt.id}')" class="p-3 rounded-lg border border-gray-200 hover:border-teal-300 hover:shadow-sm cursor-pointer transition-all bg-white slide-in">
<div class="flex items-start justify-between mb-2">
<span class="text-xs font-semibold text-teal-700 bg-teal-50 px-2 py-0.5 rounded">${formatDayOfWeek(appt.date)}</span>
<span class="text-xs text-gray-500">${appt.time || ''}</span>
</div>
<div class="text-sm font-medium text-gray-900 mb-0.5">${appt.category || ''}</div>
<div class="text-xs text-gray-500 flex items-center gap-1">
<img src="${appt.provider?.avatar || ''}" class="w-4 h-4 rounded-full">
${appt.provider?.name || ''}
</div>
</div>
`).join('');
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
// Drawer Functions
function openDrawer(appointmentId) {
const appt = appointments.find(a => a && a.id === appointmentId);
if (!appt) return;
selectedAppointment = appt;
const overlay = document.getElementById('drawerOverlay');
const drawer = document.getElementById('appointmentDrawer');
const content = document.getElementById('drawerContent');
const apptIdEl = document.getElementById('drawerApptId');
if (!overlay || !drawer || !content || !apptIdEl) return;
apptIdEl.textContent = appt.id;
const statusColors = {
'Confirmed': 'text-green-700 bg-green-50 border-green-200',
'Pending': 'text-yellow-700 bg-yellow-50 border-yellow-200',
'Completed': 'text-gray-700 bg-gray-100 border-gray-200',
'Canceled': 'text-red-700 bg-red-50 border-red-200'
};
const statusColors = {
'Confirmed': 'text-green-700 bg-green-50 border-green-200',
'Pending': 'text-yellow-700 bg-yellow-50 border-yellow-200',
'Completed': 'text-gray-700 bg-gray-100 border-gray-200',
'Canceled': 'text-red-700 bg-red-50 border-red-200'
};
content.innerHTML = `
<div class="space-y-6">
<!-- Header Info -->
<div class="text-center pb-6 border-b border-gray-100">
<div class="text-3xl font-bold text-gray-900 mb-1">${formatDateFull(appt.date)}</div>
<div class="text-lg text-gray-500 mb-3">${appt.time || ''}${appt.duration || ''}</div>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium border ${statusColors[appt.status] || 'bg-gray-100 text-gray-700 border-gray-200'}">
${appt.status || ''}
</span>
</div>
<!-- Status Timeline -->
<div>
<h4 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">Status Timeline</h4>
<div class="space-y-3">
${(appt.history || []).map((h, i, arr) => `
<div class="flex gap-3">
<div class="flex flex-col items-center">
<div class="w-2.5 h-2.5 rounded-full ${i === arr.length - 1 ? 'bg-teal-500' : 'bg-gray-300'}"></div>
${i < arr.length - 1 ? '<div class="w-0.5 flex-1 bg-gray-200 my-1"></div>' : ''}
</div>
<div class="pb-3">
<div class="text-sm font-medium text-gray-900">${h.status || ''}</div>
<div class="text-xs text-gray-500">${h.time || ''}${h.user || ''}</div>
</div>
</div>
`).join('') || 'No history available'}
</div>
</div>
<!-- Provider -->
<div class="flex items-start gap-3 p-4 bg-gray-50 rounded-lg">
<img src="${appt.provider?.avatar || ''}" class="w-12 h-12 rounded-full object-cover border-2 border-white shadow-sm">
<div class="flex-1">
<div class="font-semibold text-gray-900">${appt.provider?.name || 'Unknown Provider'}</div>
<div class="text-sm text-gray-500 mb-2">${appt.provider?.specialty || ''}</div>
<button class="text-xs text-teal-600 hover:text-teal-700 font-medium flex items-center gap-1">
<i data-lucide="message-circle" class="w-3 h-3"></i>
Message Provider
</button>
</div>
</div>
<!-- Category & Type -->
<div class="grid grid-cols-2 gap-4">
<div>
<div class="text-xs text-gray-500 mb-1">Category</div>
<div class="inline-flex items-center px-2.5 py-1 rounded-md text-sm font-medium bg-blue-50 text-blue-700 border border-blue-200">
${appt.category || ''}
</div>
</div>
<div>
<div class="text-xs text-gray-500 mb-1">Type</div>
<div class="flex items-center gap-1.5 text-sm font-medium text-gray-900">
<i data-lucide="${appt.type === 'Telehealth' ? 'video' : 'map-pin'}" class="w-4 h-4 text-gray-400"></i>
${appt.type || ''}
</div>
${appt.type === 'Telehealth' ? '<button class="mt-2 w-full px-3 py-1.5 bg-teal-600 text-white text-xs font-medium rounded hover:bg-teal-700 transition-colors">Join Video Link</button>' : ''}
</div>
</div>
<!-- Location -->
<div>
<div class="text-xs text-gray-500 mb-1 uppercase tracking-wider">Location</div>
<div class="flex items-start gap-2 text-sm text-gray-900">
<i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5"></i>
<span>${appt.location || ''}</span>
</div>
</div>
<!-- Reason -->
<div>
<div class="text-xs text-gray-500 mb-1 uppercase tracking-wider">Reason for Visit</div>
<p class="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg">${appt.reason || ''}</p>
</div>
<!-- Notes Timeline -->
<div>
<div class="flex items-center justify-between mb-3">
<div class="text-xs font-semibold text-gray-500 uppercase tracking-wider">Notes</div>
<button class="text-xs text-teal-600 hover:text-teal-700 font-medium">+ Add Note</button>
</div>
${(appt.notes || 0) > 0 ? `
<div class="space-y-3">
<div class="border-l-2 border-teal-200 pl-3 py-1">
<div class="text-xs text-gray-500 mb-1">Jan 8, 2024 2:35 PM • Dr. Chen</div>
<p class="text-sm text-gray-700">Patient confirmed appointment via portal. No special requirements noted.</p>
</div>
${appt.notes > 1 ? `
<div class="border-l-2 border-gray-200 pl-3 py-1">
<div class="text-xs text-gray-500 mb-1">Jan 5, 2024 9:00 AM • Sarah M.</div>
<p class="text-sm text-gray-700">Requested morning slot due to work schedule.</p>
</div>
` : ''}
</div>
` : '<div class="text-sm text-gray-400 italic">No notes added yet</div>'}
</div>
<!-- Actions -->
<div class="pt-4 border-t border-gray-100 flex gap-2">
<button onclick="event.stopPropagation(); cancelAppointment('${appt.id}')" class="flex-1 px-4 py-2 border border-red-200 text-red-600 rounded-lg hover:bg-red-50 text-sm font-medium transition-colors">
Cancel Appointment
</button>
<button onclick="event.stopPropagation(); sendReminder('${appt.id}')" class="flex-1 px-4 py-2 border border-gray-200 text-gray-700 rounded-lg hover:bg-gray-50 text-sm font-medium transition-colors flex items-center justify-center gap-2">
<i data-lucide="bell" class="w-4 h-4"></i>
Send Reminder
</button>
</div>
</div>
`;
overlay.classList.remove('hidden');
setTimeout(() => overlay.classList.remove('opacity-0'), 10);
drawer.classList.remove('translate-x-full');
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
function closeDrawer() {
const overlay = document.getElementById('drawerOverlay');
const drawer = document.getElementById('appointmentDrawer');
overlay.classList.add('opacity-0');
drawer.classList.add('translate-x-full');
setTimeout(() => {
overlay.classList.add('hidden');
}, 300);
selectedAppointment = null;
}
// Modal Functions
function openNewAppointmentModal() {
const modal = document.getElementById('newAppointmentModal');
modal.classList.remove('hidden');
setTimeout(() => {
modal.querySelector('div[class*="scale-100"]').classList.remove('scale-95', 'opacity-0');
}, 10);
}
function closeNewAppointmentModal() {
const modal = document.getElementById('newAppointmentModal');
modal.classList.add('hidden');
}
function saveAppointment() {
// Simulate saving
closeNewAppointmentModal();
// Show success notification (simplified)
alert('Appointment scheduled successfully!');
}
// New Note Modal Functions
function openNewNoteModal() {
const modal = document.getElementById('newNoteModal');
if (modal) {
modal.classList.remove('hidden');
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
}
function closeNewNoteModal() {
const modal = document.getElementById('newNoteModal');
modal.classList.add('hidden');
}
function saveNote() {
closeNewNoteModal();
alert('Note saved successfully!');
}
// Notes view toggle (for tab switching - basic implementation)
function showNotesView() {
document.getElementById('listView').classList.add('hidden');
document.getElementById('calendarView').classList.add('hidden');
document.getElementById('notesSection').classList.remove('hidden');
}
function hideNotesView() {
document.getElementById('notesSection').classList.add('hidden');
}
// Helper Functions
function formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
if (isNaN(date.getTime())) return '';
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}
function formatDateFull(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
if (isNaN(date.getTime())) return '';
return date.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });
}
function formatDayOfWeek(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
if (isNaN(date.getTime())) return '';
return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
}
function cancelAppointment(id) {
if (confirm('Are you sure you want to cancel this appointment?')) {
alert('Appointment canceled');
closeDrawer();
}
}
function sendReminder(id) {
alert('Reminder sent to patient');
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeDrawer();
closeNewAppointmentModal();
}
});