|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Silky Smooth | Client Management</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<style> |
|
.calendar-day:hover:not(.empty-day) { |
|
background-color: #f3f4f6; |
|
cursor: pointer; |
|
} |
|
.appointment-day { |
|
background-color: #f0fdf4; |
|
border: 1px solid #bbf7d0; |
|
} |
|
.today { |
|
background-color: #eff6ff; |
|
border: 1px solid #bfdbfe; |
|
} |
|
|
|
.custom-scrollbar::-webkit-scrollbar { |
|
width: 6px; |
|
} |
|
.custom-scrollbar::-webkit-scrollbar-track { |
|
background: #f1f1f1; |
|
} |
|
.custom-scrollbar::-webkit-scrollbar-thumb { |
|
background: #cbd5e1; |
|
border-radius: 3px; |
|
} |
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover { |
|
background: #94a3b8; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50 min-h-screen"> |
|
<div class="container mx-auto px-4 py-8 max-w-6xl"> |
|
|
|
<header class="flex flex-col md:flex-row justify-between items-center mb-8"> |
|
<div class="flex items-center mb-4 md:mb-0"> |
|
<i class="fas fa-spa text-pink-500 text-3xl mr-3"></i> |
|
<h1 class="text-2xl font-bold text-gray-800">Silky Smooth Client Manager</h1> |
|
</div> |
|
<div class="flex space-x-2"> |
|
<button id="showClientsBtn" class="px-4 py-2 bg-pink-500 text-white rounded-lg hover:bg-pink-600 transition"> |
|
<i class="fas fa-users mr-2"></i>Clients |
|
</button> |
|
<button id="showCalendarBtn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition"> |
|
<i class="far fa-calendar-alt mr-2"></i>Calendar |
|
</button> |
|
</div> |
|
</header> |
|
|
|
|
|
<main class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
|
|
|
<div id="clientsSection" class="lg:col-span-2 bg-white rounded-xl shadow-md overflow-hidden"> |
|
<div class="p-5 border-b border-gray-200"> |
|
<h2 class="text-xl font-semibold text-gray-800">Client Management</h2> |
|
</div> |
|
|
|
|
|
<div class="p-5 border-b border-gray-200"> |
|
<form id="clientForm" class="space-y-4"> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
<div> |
|
<label for="clientName" class="block text-sm font-medium text-gray-700 mb-1">Full Name</label> |
|
<input type="text" id="clientName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
<div> |
|
<label for="clientPhone" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label> |
|
<input type="tel" id="clientPhone" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
</div> |
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
|
<div> |
|
<label for="clientService" class="block text-sm font-medium text-gray-700 mb-1">Service</label> |
|
<select id="clientService" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500"> |
|
<option value="Brazilian Wax">Brazilian Wax</option> |
|
<option value="Bikini Wax">Bikini Wax</option> |
|
<option value="Leg Wax">Leg Wax</option> |
|
<option value="Arm Wax">Arm Wax</option> |
|
<option value="Underarm Wax">Underarm Wax</option> |
|
<option value="Facial Wax">Facial Wax</option> |
|
</select> |
|
</div> |
|
<div> |
|
<label for="clientDate" class="block text-sm font-medium text-gray-700 mb-1">Appointment Date</label> |
|
<input type="date" id="clientDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
<div> |
|
<label for="clientTime" class="block text-sm font-medium text-gray-700 mb-1">Time</label> |
|
<input type="time" id="clientTime" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
</div> |
|
<div class="flex justify-end"> |
|
<button type="submit" class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600 transition flex items-center"> |
|
<i class="fas fa-plus mr-2"></i> Add Client |
|
</button> |
|
</div> |
|
</form> |
|
</div> |
|
|
|
|
|
<div class="overflow-y-auto custom-scrollbar" style="max-height: 500px;"> |
|
<table class="min-w-full divide-y divide-gray-200"> |
|
<thead class="bg-gray-50"> |
|
<tr> |
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th> |
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Phone</th> |
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Service</th> |
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Appointment</th> |
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
|
</tr> |
|
</thead> |
|
<tbody id="clientsList" class="bg-white divide-y divide-gray-200"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="calendarSection" class="hidden lg:block bg-white rounded-xl shadow-md overflow-hidden"> |
|
<div class="p-5 border-b border-gray-200"> |
|
<h2 class="text-xl font-semibold text-gray-800">Appointment Calendar</h2> |
|
</div> |
|
<div class="p-4"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<button id="prevMonth" class="p-2 rounded-full hover:bg-gray-100"> |
|
<i class="fas fa-chevron-left text-gray-600"></i> |
|
</button> |
|
<h3 id="currentMonthYear" class="text-lg font-medium text-gray-800">June 2023</h3> |
|
<button id="nextMonth" class="p-2 rounded-full hover:bg-gray-100"> |
|
<i class="fas fa-chevron-right text-gray-600"></i> |
|
</button> |
|
</div> |
|
<div class="grid grid-cols-7 gap-1 mb-2"> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Sun</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Mon</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Tue</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Wed</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Thu</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Fri</div> |
|
<div class="text-center text-xs font-medium text-gray-500 py-1">Sat</div> |
|
</div> |
|
<div id="calendarDays" class="grid grid-cols-7 gap-1"> |
|
|
|
</div> |
|
</div> |
|
|
|
|
|
<div class="p-4 border-t border-gray-200"> |
|
<h3 id="selectedDayTitle" class="text-md font-medium text-gray-800 mb-3">Today's Appointments</h3> |
|
<div id="dayAppointments" class="space-y-3 max-h-64 overflow-y-auto custom-scrollbar"> |
|
|
|
</div> |
|
</div> |
|
|
|
|
|
<div class="p-4 border-t border-gray-200"> |
|
<button id="sendRemindersBtn" class="w-full py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition flex items-center justify-center"> |
|
<i class="fas fa-sms mr-2"></i> Send Tomorrow's Reminders |
|
</button> |
|
<div id="smsStatus" class="mt-2 text-sm text-center hidden"></div> |
|
</div> |
|
</div> |
|
</main> |
|
</div> |
|
|
|
|
|
<div id="editModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
|
<div class="bg-white rounded-lg p-6 w-full max-w-md"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="text-lg font-semibold">Edit Client</h3> |
|
<button id="closeEditModal" class="text-gray-500 hover:text-gray-700"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
<form id="editClientForm" class="space-y-4"> |
|
<input type="hidden" id="editClientId"> |
|
<div> |
|
<label for="editClientName" class="block text-sm font-medium text-gray-700 mb-1">Full Name</label> |
|
<input type="text" id="editClientName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
<div> |
|
<label for="editClientPhone" class="block text-sm font-medium text-gray-700 mb-1">Phone Number</label> |
|
<input type="tel" id="editClientPhone" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
<div> |
|
<label for="editClientService" class="block text-sm font-medium text-gray-700 mb-1">Service</label> |
|
<select id="editClientService" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500"> |
|
<option value="Brazilian Wax">Brazilian Wax</option> |
|
<option value="Bikini Wax">Bikini Wax</option> |
|
<option value="Leg Wax">Leg Wax</option> |
|
<option value="Arm Wax">Arm Wax</option> |
|
<option value="Underarm Wax">Underarm Wax</option> |
|
<option value="Facial Wax">Facial Wax</option> |
|
</select> |
|
</div> |
|
<div class="grid grid-cols-2 gap-4"> |
|
<div> |
|
<label for="editClientDate" class="block text-sm font-medium text-gray-700 mb-1">Appointment Date</label> |
|
<input type="date" id="editClientDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
<div> |
|
<label for="editClientTime" class="block text-sm font-medium text-gray-700 mb-1">Time</label> |
|
<input type="time" id="editClientTime" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-pink-500" required> |
|
</div> |
|
</div> |
|
<div class="flex justify-end space-x-3 pt-4"> |
|
<button type="button" id="cancelEdit" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition"> |
|
Cancel |
|
</button> |
|
<button type="submit" class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600 transition"> |
|
Save Changes |
|
</button> |
|
</div> |
|
</form> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
// Client data storage |
|
let clients = JSON.parse(localStorage.getItem('clients')) || []; |
|
|
|
// Current date for calendar |
|
let currentDate = new Date(); |
|
let selectedDate = new Date(); |
|
|
|
// DOM Elements |
|
const clientsSection = document.getElementById('clientsSection'); |
|
const calendarSection = document.getElementById('calendarSection'); |
|
const showClientsBtn = document.getElementById('showClientsBtn'); |
|
const showCalendarBtn = document.getElementById('showCalendarBtn'); |
|
const clientForm = document.getElementById('clientForm'); |
|
const clientsList = document.getElementById('clientsList'); |
|
const currentMonthYear = document.getElementById('currentMonthYear'); |
|
const calendarDays = document.getElementById('calendarDays'); |
|
const prevMonthBtn = document.getElementById('prevMonth'); |
|
const nextMonthBtn = document.getElementById('nextMonth'); |
|
const dayAppointments = document.getElementById('dayAppointments'); |
|
const selectedDayTitle = document.getElementById('selectedDayTitle'); |
|
const sendRemindersBtn = document.getElementById('sendRemindersBtn'); |
|
const smsStatus = document.getElementById('smsStatus'); |
|
const editModal = document.getElementById('editModal'); |
|
const editClientForm = document.getElementById('editClientForm'); |
|
const closeEditModal = document.getElementById('closeEditModal'); |
|
const cancelEdit = document.getElementById('cancelEdit'); |
|
|
|
// Initialize the app |
|
document.addEventListener('DOMContentLoaded', () => { |
|
renderClients(); |
|
generateCalendar(); |
|
showTodaysAppointments(); |
|
|
|
// Set default date to today in the form |
|
const today = new Date().toISOString().split('T')[0]; |
|
document.getElementById('clientDate').value = today; |
|
|
|
// Set default time to next hour |
|
const nextHour = new Date(); |
|
nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0); |
|
const timeString = nextHour.toTimeString().substring(0, 5); |
|
document.getElementById('clientTime').value = timeString; |
|
}); |
|
|
|
// Toggle between clients and calendar on mobile |
|
showClientsBtn.addEventListener('click', () => { |
|
clientsSection.classList.remove('hidden'); |
|
calendarSection.classList.add('hidden'); |
|
showClientsBtn.classList.remove('bg-gray-200', 'text-gray-700'); |
|
showClientsBtn.classList.add('bg-pink-500', 'text-white'); |
|
showCalendarBtn.classList.remove('bg-pink-500', 'text-white'); |
|
showCalendarBtn.classList.add('bg-gray-200', 'text-gray-700'); |
|
}); |
|
|
|
showCalendarBtn.addEventListener('click', () => { |
|
clientsSection.classList.add('hidden'); |
|
calendarSection.classList.remove('hidden'); |
|
showCalendarBtn.classList.remove('bg-gray-200', 'text-gray-700'); |
|
showCalendarBtn.classList.add('bg-pink-500', 'text-white'); |
|
showClientsBtn.classList.remove('bg-pink-500', 'text-white'); |
|
showClientsBtn.classList.add('bg-gray-200', 'text-gray-700'); |
|
}); |
|
|
|
// Add new client |
|
clientForm.addEventListener('submit', (e) => { |
|
e.preventDefault(); |
|
|
|
const name = document.getElementById('clientName').value; |
|
const phone = document.getElementById('clientPhone').value; |
|
const service = document.getElementById('clientService').value; |
|
const date = document.getElementById('clientDate').value; |
|
const time = document.getElementById('clientTime').value; |
|
|
|
const newClient = { |
|
id: Date.now().toString(), |
|
name, |
|
phone, |
|
service, |
|
date, |
|
time, |
|
status: 'scheduled' |
|
}; |
|
|
|
clients.push(newClient); |
|
saveClients(); |
|
renderClients(); |
|
generateCalendar(); |
|
showTodaysAppointments(); |
|
|
|
// Reset form |
|
clientForm.reset(); |
|
|
|
// Set default date to today |
|
document.getElementById('clientDate').value = new Date().toISOString().split('T')[0]; |
|
|
|
// Set default time to next hour |
|
const nextHour = new Date(); |
|
nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0); |
|
const timeString = nextHour.toTimeString().substring(0, 5); |
|
document.getElementById('clientTime').value = timeString; |
|
}); |
|
|
|
// Edit client modal |
|
function openEditModal(client) { |
|
document.getElementById('editClientId').value = client.id; |
|
document.getElementById('editClientName').value = client.name; |
|
document.getElementById('editClientPhone').value = client.phone; |
|
document.getElementById('editClientService').value = client.service; |
|
document.getElementById('editClientDate').value = client.date; |
|
document.getElementById('editClientTime').value = client.time; |
|
|
|
editModal.classList.remove('hidden'); |
|
} |
|
|
|
// Close edit modal |
|
closeEditModal.addEventListener('click', () => { |
|
editModal.classList.add('hidden'); |
|
}); |
|
|
|
cancelEdit.addEventListener('click', () => { |
|
editModal.classList.add('hidden'); |
|
}); |
|
|
|
// Save edited client |
|
editClientForm.addEventListener('submit', (e) => { |
|
e.preventDefault(); |
|
|
|
const id = document.getElementById('editClientId').value; |
|
const name = document.getElementById('editClientName').value; |
|
const phone = document.getElementById('editClientPhone').value; |
|
const service = document.getElementById('editClientService').value; |
|
const date = document.getElementById('editClientDate').value; |
|
const time = document.getElementById('editClientTime').value; |
|
|
|
const clientIndex = clients.findIndex(client => client.id === id); |
|
|
|
if (clientIndex !== -1) { |
|
clients[clientIndex] = { |
|
...clients[clientIndex], |
|
name, |
|
phone, |
|
service, |
|
date, |
|
time |
|
}; |
|
|
|
saveClients(); |
|
renderClients(); |
|
generateCalendar(); |
|
showTodaysAppointments(); |
|
editModal.classList.add('hidden'); |
|
} |
|
}); |
|
|
|
// Delete client |
|
function deleteClient(id) { |
|
if (confirm('Are you sure you want to delete this client?')) { |
|
clients = clients.filter(client => client.id !== id); |
|
saveClients(); |
|
renderClients(); |
|
generateCalendar(); |
|
showTodaysAppointments(); |
|
} |
|
} |
|
|
|
// Render clients list |
|
function renderClients() { |
|
if (clients.length === 0) { |
|
clientsList.innerHTML = ` |
|
<tr> |
|
<td colspan="5" class="px-6 py-4 text-center text-gray-500">No clients found. Add your first client above.</td> |
|
</tr> |
|
`; |
|
return; |
|
} |
|
|
|
// Sort clients by date and time |
|
const sortedClients = [...clients].sort((a, b) => { |
|
const dateA = new Date(`${a.date}T${a.time}`); |
|
const dateB = new Date(`${b.date}T${b.time}`); |
|
return dateA - dateB; |
|
}); |
|
|
|
clientsList.innerHTML = sortedClients.map(client => ` |
|
<tr class="hover:bg-gray-50"> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm font-medium text-gray-900">${client.name}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm text-gray-500">${formatPhoneNumber(client.phone)}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm text-gray-500">${client.service}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm text-gray-500">${formatDate(client.date)} at ${client.time}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> |
|
<button onclick="openEditModal(${JSON.stringify(client).replace(/"/g, '"')})" class="text-pink-600 hover:text-pink-900 mr-3"> |
|
<i class="fas fa-edit"></i> |
|
</button> |
|
<button onclick="deleteClient('${client.id}')" class="text-red-600 hover:text-red-900"> |
|
<i class="fas fa-trash-alt"></i> |
|
</button> |
|
</td> |
|
</tr> |
|
`).join(''); |
|
} |
|
|
|
// Generate calendar |
|
function generateCalendar() { |
|
const year = currentDate.getFullYear(); |
|
const month = currentDate.getMonth(); |
|
|
|
currentMonthYear.textContent = new Date(year, month).toLocaleDateString('en-US', { |
|
month: 'long', |
|
year: 'numeric' |
|
}); |
|
|
|
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(); |
|
|
|
calendarDays.innerHTML = ''; |
|
|
|
// Previous month's days |
|
for (let i = firstDay; i > 0; i--) { |
|
const day = daysInPrevMonth - i + 1; |
|
calendarDays.innerHTML += ` |
|
<div class="h-12 flex items-center justify-center text-gray-400 text-sm empty-day"> |
|
${day} |
|
</div> |
|
`; |
|
} |
|
|
|
// Current month's days |
|
for (let i = 1; i <= daysInMonth; i++) { |
|
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`; |
|
const hasAppointment = clients.some(client => client.date === dateStr); |
|
const isToday = isSameDay(new Date(dateStr), new Date()); |
|
|
|
let dayClass = 'h-12 flex items-center justify-center text-sm calendar-day'; |
|
if (hasAppointment) dayClass += ' appointment-day'; |
|
if (isToday) dayClass += ' today'; |
|
|
|
calendarDays.innerHTML += ` |
|
<div class="${dayClass}" data-date="${dateStr}"> |
|
${i} |
|
${hasAppointment ? '<span class="absolute bottom-1 w-1 h-1 bg-pink-500 rounded-full"></span>' : ''} |
|
</div> |
|
`; |
|
} |
|
|
|
// Next month's days |
|
const daysToAdd = 42 - (firstDay + daysInMonth); // 6 rows x 7 days = 42 cells |
|
for (let i = 1; i <= daysToAdd; i++) { |
|
calendarDays.innerHTML += ` |
|
<div class="h-12 flex items-center justify-center text-gray-400 text-sm empty-day"> |
|
${i} |
|
</div> |
|
`; |
|
} |
|
|
|
// Add click event to days |
|
document.querySelectorAll('.calendar-day').forEach(day => { |
|
day.addEventListener('click', () => { |
|
const dateStr = day.getAttribute('data-date'); |
|
selectedDate = new Date(dateStr); |
|
showDayAppointments(dateStr); |
|
}); |
|
}); |
|
} |
|
|
|
// Show today's appointments |
|
function showTodaysAppointments() { |
|
const today = new Date().toISOString().split('T')[0]; |
|
selectedDayTitle.textContent = "Today's Appointments"; |
|
showDayAppointments(today); |
|
} |
|
|
|
// Show appointments for a specific day |
|
function showDayAppointments(dateStr) { |
|
const dayAppointmentsList = clients.filter(client => client.date === dateStr) |
|
.sort((a, b) => a.time.localeCompare(b.time)); |
|
|
|
if (dayAppointmentsList.length === 0) { |
|
dayAppointments.innerHTML = ` |
|
<div class="text-center text-gray-500 py-4"> |
|
No appointments scheduled for ${formatDate(dateStr)} |
|
</div> |
|
`; |
|
} else { |
|
dayAppointments.innerHTML = dayAppointmentsList.map(client => ` |
|
<div class="bg-white border border-gray-200 rounded-lg p-3 shadow-sm"> |
|
<div class="flex justify-between items-start"> |
|
<div> |
|
<h4 class="font-medium text-gray-800">${client.name}</h4> |
|
<p class="text-sm text-gray-600">${client.service}</p> |
|
</div> |
|
<span class="text-sm font-medium">${client.time}</span> |
|
</div> |
|
<div class="mt-2 flex justify-between items-center"> |
|
<span class="text-xs text-gray-500">${formatPhoneNumber(client.phone)}</span> |
|
<button onclick="deleteClient('${client.id}')" class="text-red-500 hover:text-red-700 text-xs"> |
|
<i class="fas fa-times mr-1"></i>Cancel |
|
</button> |
|
</div> |
|
</div> |
|
`).join(''); |
|
} |
|
|
|
// Update selected day title if not today |
|
if (!isSameDay(new Date(dateStr), new Date())) { |
|
selectedDayTitle.textContent = `Appointments for ${formatDate(dateStr)}`; |
|
} |
|
} |
|
|
|
// Navigate calendar months |
|
prevMonthBtn.addEventListener('click', () => { |
|
currentDate.setMonth(currentDate.getMonth() - 1); |
|
generateCalendar(); |
|
}); |
|
|
|
nextMonthBtn.addEventListener('click', () => { |
|
currentDate.setMonth(currentDate.getMonth() + 1); |
|
generateCalendar(); |
|
}); |
|
|
|
// Send SMS reminders for tomorrow's appointments |
|
sendRemindersBtn.addEventListener('click', () => { |
|
const tomorrow = new Date(); |
|
tomorrow.setDate(tomorrow.getDate() + 1); |
|
const tomorrowStr = tomorrow.toISOString().split('T')[0]; |
|
|
|
const tomorrowsAppointments = clients.filter(client => client.date === tomorrowStr); |
|
|
|
if (tomorrowsAppointments.length === 0) { |
|
smsStatus.textContent = "No appointments tomorrow to send reminders for."; |
|
smsStatus.className = "mt-2 text-sm text-center text-gray-600"; |
|
smsStatus.classList.remove('hidden'); |
|
return; |
|
} |
|
|
|
// Simulate sending SMS (in a real app, you would integrate with an SMS API) |
|
smsStatus.textContent = `Sending ${tomorrowsAppointments.length} reminder(s)...`; |
|
smsStatus.className = "mt-2 text-sm text-center text-blue-600"; |
|
smsStatus.classList.remove('hidden'); |
|
|
|
setTimeout(() => { |
|
smsStatus.textContent = `Successfully sent ${tomorrowsAppointments.length} reminder(s) for tomorrow's appointments!`; |
|
smsStatus.className = "mt-2 text-sm text-center text-green-600"; |
|
|
|
// In a real app, you would track which reminders were sent |
|
tomorrowsAppointments.forEach(appointment => { |
|
console.log(`Sent SMS to ${appointment.phone}: Reminder: Your ${appointment.service} appointment is tomorrow at ${appointment.time}.`); |
|
}); |
|
}, 2000); |
|
}); |
|
|
|
// Helper functions |
|
function saveClients() { |
|
localStorage.setItem('clients', JSON.stringify(clients)); |
|
} |
|
|
|
function formatDate(dateStr) { |
|
const options = { weekday: 'short', month: 'short', day: 'numeric' }; |
|
return new Date(dateStr).toLocaleDateString('en-US', options); |
|
} |
|
|
|
function formatPhoneNumber(phone) { |
|
// Simple formatting for display |
|
return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'); |
|
} |
|
|
|
function isSameDay(date1, date2) { |
|
return date1.getFullYear() === date2.getFullYear() && |
|
date1.getMonth() === date2.getMonth() && |
|
date1.getDate() === date2.getDate(); |
|
} |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Cezarxil/client-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |