Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Epiliz - Laser Hair Removal</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> | |
.fade-in { | |
animation: fadeIn 0.3s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.sidebar { | |
transition: all 0.3s ease; | |
} | |
@media (max-width: 768px) { | |
.sidebar { | |
transform: translateX(-100%); | |
} | |
.sidebar.open { | |
transform: translateX(0); | |
} | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 font-sans"> | |
<!-- Login Page --> | |
<div id="login-page" class="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-100 to-pink-100"> | |
<div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md"> | |
<div class="text-center mb-8"> | |
<h1 class="text-3xl font-bold text-purple-800">Epiliz</h1> | |
<p class="text-gray-600 mt-2">Laser Hair Removal Management</p> | |
</div> | |
<form id="login-form" class="space-y-6"> | |
<div> | |
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label> | |
<input type="text" id="username" name="username" required | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
</div> | |
<div> | |
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> | |
<input type="password" id="password" name="password" required | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
</div> | |
<div> | |
<button type="submit" | |
class="w-full bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2"> | |
Sign In | |
</button> | |
</div> | |
</form> | |
<div id="login-error" class="mt-4 text-red-500 text-sm hidden"></div> | |
</div> | |
</div> | |
<!-- Main App (hidden initially) --> | |
<div id="app-container" class="hidden min-h-screen"> | |
<!-- Mobile Header --> | |
<div class="md:hidden bg-purple-700 text-white p-4 flex justify-between items-center"> | |
<button id="menu-toggle" class="text-white focus:outline-none"> | |
<i class="fas fa-bars text-xl"></i> | |
</button> | |
<h1 class="text-xl font-bold">Epiliz</h1> | |
<div class="w-6"></div> <!-- Spacer for alignment --> | |
</div> | |
<!-- Sidebar --> | |
<div id="sidebar" class="sidebar fixed inset-y-0 left-0 w-64 bg-purple-800 text-white transform md:translate-x-0 z-10"> | |
<div class="p-4 flex items-center border-b border-purple-700"> | |
<h1 class="text-xl font-bold">Epiliz</h1> | |
</div> | |
<nav class="mt-6"> | |
<div class="px-4 py-3 bg-purple-900"> | |
<span class="text-sm font-semibold">Menu</span> | |
</div> | |
<a href="#" id="dashboard-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200"> | |
<i class="fas fa-calendar-alt mr-3"></i> Appointments | |
</a> | |
<a href="#" id="add-appointment-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200"> | |
<i class="fas fa-plus-circle mr-3"></i> Add Appointment | |
</a> | |
<a href="#" id="logout-link" class="block px-4 py-3 mt-2 text-white hover:bg-purple-700 transition duration-200"> | |
<i class="fas fa-sign-out-alt mr-3"></i> Logout | |
</a> | |
</nav> | |
</div> | |
<!-- Main Content --> | |
<div class="md:ml-64 transition-all duration-300"> | |
<!-- Dashboard --> | |
<div id="dashboard" class="p-6 fade-in"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-bold text-gray-800">Appointments</h2> | |
<button id="add-appointment-btn" class="bg-purple-600 text-white px-4 py-2 rounded-md hover:bg-purple-700 transition duration-200 md:hidden"> | |
<i class="fas fa-plus mr-2"></i> Add | |
</button> | |
</div> | |
<div class="bg-white rounded-lg shadow overflow-hidden"> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead class="bg-gray-50"> | |
<tr> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Treatment Area</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contact</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> | |
</tr> | |
</thead> | |
<tbody id="appointments-list" class="bg-white divide-y divide-gray-200"> | |
<!-- Appointments will be loaded here --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div id="no-appointments" class="mt-8 text-center text-gray-500 hidden"> | |
<i class="fas fa-calendar-times text-4xl mb-4"></i> | |
<p class="text-lg">No appointments scheduled</p> | |
</div> | |
</div> | |
<!-- Add Appointment Form --> | |
<div id="add-appointment" class="p-6 hidden fade-in"> | |
<div class="flex items-center mb-6"> | |
<button id="back-to-dashboard" class="mr-4 text-purple-600 hover:text-purple-800"> | |
<i class="fas fa-arrow-left text-xl"></i> | |
</button> | |
<h2 class="text-2xl font-bold text-gray-800">Add New Appointment</h2> | |
</div> | |
<div class="bg-white rounded-lg shadow p-6 max-w-2xl mx-auto"> | |
<form id="appointment-form"> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<div> | |
<label for="client-name" class="block text-sm font-medium text-gray-700 mb-1">Client Name*</label> | |
<input type="text" id="client-name" name="client-name" required | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
</div> | |
<div> | |
<label for="appointment-date" class="block text-sm font-medium text-gray-700 mb-1">Appointment Date*</label> | |
<input type="datetime-local" id="appointment-date" name="appointment-date" required | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
</div> | |
<div> | |
<label for="treatment-area" class="block text-sm font-medium text-gray-700 mb-1">Treatment Area*</label> | |
<select id="treatment-area" name="treatment-area" required | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
<option value="">Select area</option> | |
<option value="Face">Face</option> | |
<option value="Underarms">Underarms</option> | |
<option value="Arms">Arms</option> | |
<option value="Legs">Legs</option> | |
<option value="Bikini">Bikini</option> | |
<option value="Back">Back</option> | |
<option value="Chest">Chest</option> | |
<option value="Brazilian">Brazilian</option> | |
</select> | |
</div> | |
<div> | |
<label for="contact-info" class="block text-sm font-medium text-gray-700 mb-1">Contact Info</label> | |
<input type="text" id="contact-info" name="contact-info" | |
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
</div> | |
</div> | |
<div class="mt-8"> | |
<button type="submit" | |
class="w-full md:w-auto bg-purple-600 text-white py-2 px-6 rounded-md hover:bg-purple-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2"> | |
Save Appointment | |
</button> | |
</div> | |
</form> | |
<div id="form-error" class="mt-4 text-red-500 text-sm hidden"></div> | |
<div id="form-success" class="mt-4 text-green-500 text-sm hidden"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// DOM Elements | |
const loginPage = document.getElementById('login-page'); | |
const appContainer = document.getElementById('app-container'); | |
const loginForm = document.getElementById('login-form'); | |
const loginError = document.getElementById('login-error'); | |
const menuToggle = document.getElementById('menu-toggle'); | |
const sidebar = document.getElementById('sidebar'); | |
const dashboardLink = document.getElementById('dashboard-link'); | |
const addAppointmentLink = document.getElementById('add-appointment-link'); | |
const logoutLink = document.getElementById('logout-link'); | |
const dashboard = document.getElementById('dashboard'); | |
const addAppointment = document.getElementById('add-appointment'); | |
const backToDashboard = document.getElementById('back-to-dashboard'); | |
const addAppointmentBtn = document.getElementById('add-appointment-btn'); | |
const appointmentsList = document.getElementById('appointments-list'); | |
const noAppointments = document.getElementById('no-appointments'); | |
const appointmentForm = document.getElementById('appointment-form'); | |
const formError = document.getElementById('form-error'); | |
const formSuccess = document.getElementById('form-success'); | |
// Login | |
loginForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const username = document.getElementById('username').value; | |
const password = document.getElementById('password').value; | |
if (username === 'Epiliz' && password === 'Epilat2110') { | |
loginError.classList.add('hidden'); | |
loginPage.classList.add('hidden'); | |
appContainer.classList.remove('hidden'); | |
loadAppointments(); | |
} else { | |
loginError.textContent = 'Invalid username or password'; | |
loginError.classList.remove('hidden'); | |
} | |
}); | |
// Navigation | |
dashboardLink.addEventListener('click', function(e) { | |
e.preventDefault(); | |
showDashboard(); | |
}); | |
addAppointmentLink.addEventListener('click', function(e) { | |
e.preventDefault(); | |
showAddAppointment(); | |
}); | |
addAppointmentBtn.addEventListener('click', function(e) { | |
e.preventDefault(); | |
showAddAppointment(); | |
}); | |
backToDashboard.addEventListener('click', function(e) { | |
e.preventDefault(); | |
showDashboard(); | |
}); | |
logoutLink.addEventListener('click', function(e) { | |
e.preventDefault(); | |
appContainer.classList.add('hidden'); | |
loginPage.classList.remove('hidden'); | |
loginForm.reset(); | |
}); | |
// Mobile menu toggle | |
menuToggle.addEventListener('click', function() { | |
sidebar.classList.toggle('open'); | |
}); | |
// Show dashboard view | |
function showDashboard() { | |
dashboard.classList.remove('hidden'); | |
addAppointment.classList.add('hidden'); | |
if (window.innerWidth < 768) { | |
sidebar.classList.remove('open'); | |
} | |
loadAppointments(); | |
} | |
// Show add appointment view | |
function showAddAppointment() { | |
dashboard.classList.add('hidden'); | |
addAppointment.classList.remove('hidden'); | |
appointmentForm.reset(); | |
formError.classList.add('hidden'); | |
formSuccess.classList.add('hidden'); | |
if (window.innerWidth < 768) { | |
sidebar.classList.remove('open'); | |
} | |
} | |
// Load appointments from database | |
function loadAppointments() { | |
// In a real app, this would fetch from your PHP backend | |
// For demo purposes, we'll use mock data | |
const mockAppointments = [ | |
{ | |
id: 1, | |
client_name: "Sarah Johnson", | |
appointment_date: "2023-06-15 14:30:00", | |
treatment_area: "Legs", | |
contact_info: "[email protected]" | |
}, | |
{ | |
id: 2, | |
client_name: "Michael Chen", | |
appointment_date: "2023-06-16 10:00:00", | |
treatment_area: "Back", | |
contact_info: "555-123-4567" | |
}, | |
{ | |
id: 3, | |
client_name: "Emma Williams", | |
appointment_date: "2023-06-17 15:45:00", | |
treatment_area: "Bikini", | |
contact_info: "[email protected]" | |
} | |
]; | |
appointmentsList.innerHTML = ''; | |
if (mockAppointments.length === 0) { | |
noAppointments.classList.remove('hidden'); | |
} else { | |
noAppointments.classList.add('hidden'); | |
mockAppointments.forEach(appointment => { | |
const row = document.createElement('tr'); | |
row.className = 'hover:bg-gray-50'; | |
const formattedDate = new Date(appointment.appointment_date).toLocaleString('en-US', { | |
month: 'short', | |
day: 'numeric', | |
year: 'numeric', | |
hour: '2-digit', | |
minute: '2-digit' | |
}); | |
row.innerHTML = ` | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="font-medium text-gray-900">${appointment.client_name}</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-gray-700">${formattedDate}</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-purple-100 text-purple-800"> | |
${appointment.treatment_area} | |
</span> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-gray-700"> | |
${appointment.contact_info || 'N/A'} | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-red-600 hover:text-red-900 delete-btn" data-id="${appointment.id}"> | |
<i class="fas fa-trash-alt"></i> | |
</button> | |
</td> | |
`; | |
appointmentsList.appendChild(row); | |
}); | |
// Add event listeners to delete buttons | |
document.querySelectorAll('.delete-btn').forEach(btn => { | |
btn.addEventListener('click', function() { | |
const id = this.getAttribute('data-id'); | |
deleteAppointment(id); | |
}); | |
}); | |
} | |
} | |
// Delete appointment | |
function deleteAppointment(id) { | |
if (confirm('Are you sure you want to delete this appointment?')) { | |
// In a real app, this would send a request to your PHP backend | |
console.log(`Deleting appointment with ID: ${id}`); | |
// Simulate successful deletion | |
setTimeout(() => { | |
loadAppointments(); | |
}, 300); | |
} | |
} | |
// Add new appointment | |
appointmentForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const clientName = document.getElementById('client-name').value; | |
const appointmentDate = document.getElementById('appointment-date').value; | |
const treatmentArea = document.getElementById('treatment-area').value; | |
const contactInfo = document.getElementById('contact-info').value; | |
// Simple validation | |
if (!clientName || !appointmentDate || !treatmentArea) { | |
formError.textContent = 'Please fill in all required fields'; | |
formError.classList.remove('hidden'); | |
formSuccess.classList.add('hidden'); | |
return; | |
} | |
// In a real app, this would send data to your PHP backend | |
console.log('Saving appointment:', { | |
clientName, | |
appointmentDate, | |
treatmentArea, | |
contactInfo | |
}); | |
// Simulate successful save | |
formError.classList.add('hidden'); | |
formSuccess.textContent = 'Appointment saved successfully!'; | |
formSuccess.classList.remove('hidden'); | |
// Reset form and show success message | |
setTimeout(() => { | |
appointmentForm.reset(); | |
showDashboard(); | |
}, 1500); | |
}); | |
// Initialize | |
document.addEventListener('DOMContentLoaded', function() { | |
// Set min date for appointment date input to today | |
const today = new Date(); | |
const tomorrow = new Date(today); | |
tomorrow.setDate(tomorrow.getDate() + 1); | |
const minDate = tomorrow.toISOString().slice(0, 16); | |
document.getElementById('appointment-date').min = minDate; | |
}); | |
</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/awesome-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |