Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>MediCare SaaS | Appointment Booking</title> | |
| <!-- Google Fonts: Inter for a clean, modern medical look --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> | |
| <!-- FontAwesome for Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| /* Color Palette: Healthcare White & Teal */ | |
| --primary-color: #0d9488; /* Teal 600 */ | |
| --primary-hover: #0f766e; /* Teal 700 */ | |
| --primary-light: #ccfbf1; /* Teal 100 */ | |
| --accent-color: #2dd4bf; /* Teal 400 */ | |
| --text-dark: #1e293b; | |
| --text-gray: #64748b; | |
| --bg-light: #f8fafc; | |
| --white: #ffffff; | |
| --border-color: #e2e8f0; | |
| --success-color: #10b981; | |
| --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); | |
| --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); | |
| --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); | |
| --radius: 12px; | |
| --transition: all 0.3s ease; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: var(--bg-light); | |
| color: var(--text-dark); | |
| line-height: 1.6; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| /* --- Layout Container --- */ | |
| .app-container { | |
| width: 100%; | |
| max-width: 1200px; | |
| background: var(--white); | |
| border-radius: var(--radius); | |
| box-shadow: var(--shadow-lg); | |
| overflow: hidden; | |
| display: grid; | |
| grid-template-columns: 1fr 1.5fr; /* Sidebar vs Form */ | |
| min-height: 85vh; | |
| } | |
| /* --- Sidebar (Branding & Info) --- */ | |
| .sidebar { | |
| background: linear-gradient(135deg, #0f766e 0%, #0d9488 100%); | |
| color: var(--white); | |
| padding: 40px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: space-between; | |
| position: relative; | |
| } | |
| .brand-logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| font-size: 1.5rem; | |
| font-weight: 800; | |
| letter-spacing: -0.5px; | |
| } | |
| .brand-logo i { | |
| font-size: 2rem; | |
| color: var(--white); | |
| } | |
| .clinic-info { | |
| margin-top: 60px; | |
| } | |
| .clinic-info h2 { | |
| font-size: 1.75rem; | |
| margin-bottom: 20px; | |
| font-weight: 600; | |
| } | |
| .info-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| font-size: 0.95rem; | |
| opacity: 0.9; | |
| } | |
| .info-item i { | |
| background: rgba(255,255,255,0.2); | |
| padding: 8px; | |
| border-radius: 8px; | |
| width: 35px; | |
| height: 35px; | |
| text-align: center; | |
| line-height: 19px; | |
| } | |
| .testimonial { | |
| margin-top: auto; | |
| padding-top: 30px; | |
| border-top: 1px solid rgba(255,255,255,0.2); | |
| font-style: italic; | |
| font-size: 0.9rem; | |
| } | |
| .stars { | |
| color: #fbbf24; | |
| margin-bottom: 10px; | |
| } | |
| /* --- Main Content (Booking Form) --- */ | |
| .main-content { | |
| padding: 50px; | |
| overflow-y: auto; | |
| } | |
| .header-section { | |
| margin-bottom: 40px; | |
| } | |
| .header-section h1 { | |
| font-size: 2rem; | |
| font-weight: 700; | |
| color: var(--text-dark); | |
| margin-bottom: 10px; | |
| } | |
| .header-section p { | |
| color: var(--text-gray); | |
| } | |
| /* --- Form Steps Logic --- */ | |
| .booking-form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 35px; | |
| } | |
| .step-container { | |
| animation: fadeIn 0.5s ease; | |
| } | |
| .hidden { | |
| display: none ; | |
| } | |
| /* Step 1: Service & Doctor */ | |
| .form-grid { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 25px; | |
| } | |
| @media (min-width: 768px) { | |
| .form-grid { | |
| grid-template-columns: 1fr 1fr; | |
| } | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| label { | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| color: var(--text-dark); | |
| } | |
| select, input { | |
| padding: 12px 16px; | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| font-family: inherit; | |
| font-size: 1rem; | |
| transition: var(--transition); | |
| background-color: var(--white); | |
| outline: none; | |
| } | |
| select:focus, input:focus { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 3px var(--primary-light); | |
| } | |
| /* Step 2: Calendar */ | |
| .calendar-widget { | |
| background: var(--white); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius); | |
| padding: 20px; | |
| width: 100%; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .calendar-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| font-weight: 600; | |
| font-size: 1.1rem; | |
| color: var(--text-dark); | |
| } | |
| .nav-btn { | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| color: var(--text-gray); | |
| padding: 8px; | |
| border-radius: 6px; | |
| transition: var(--transition); | |
| } | |
| .nav-btn:hover { | |
| background-color: var(--bg-light); | |
| color: var(--primary-color); | |
| } | |
| .weekdays { | |
| display: grid; | |
| grid-template-columns: repeat(7, 1fr); | |
| text-align: center; | |
| font-size: 0.75rem; | |
| color: var(--text-gray); | |
| font-weight: 600; | |
| margin-bottom: 10px; | |
| padding: 0 5px; | |
| } | |
| .days-grid { | |
| display: grid; | |
| grid-template-columns: repeat(7, 1fr); | |
| gap: 5px; | |
| } | |
| .day { | |
| height: 45px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border-radius: 8px; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| border: 1px solid transparent; | |
| } | |
| .day:hover:not(.empty) { | |
| background-color: var(--primary-light); | |
| color: var(--primary-hover); | |
| } | |
| .day.selected { | |
| background-color: var(--primary-color); | |
| color: var(--white); | |
| font-weight: 600; | |
| } | |
| .day.empty { | |
| cursor: default; | |
| } | |
| .day.disabled { | |
| color: #cbd5e1; | |
| cursor: not-allowed; | |
| } | |
| .day.today { | |
| border: 1px solid var(--primary-color); | |
| color: var(--primary-color); | |
| font-weight: 700; | |
| } | |
| /* Step 3: Time Slots */ | |
| .time-slots-container { | |
| background: var(--bg-light); | |
| padding: 25px; | |
| border-radius: var(--radius); | |
| } | |
| .slots-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); | |
| gap: 12px; | |
| margin-top: 15px; | |
| } | |
| .time-slot { | |
| padding: 12px 0; | |
| text-align: center; | |
| background: var(--white); | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-size: 0.95rem; | |
| color: var(--text-dark); | |
| } | |
| .time-slot:hover { | |
| border-color: var(--primary-color); | |
| color: var(--primary-color); | |
| } | |
| .time-slot.selected { | |
| background-color: var(--primary-color); | |
| color: var(--white); | |
| border-color: var(--primary-color); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .time-slot.full { | |
| background-color: #f1f5f9; | |
| color: #cbd5e1; | |
| cursor: not-allowed; | |
| border-color: #e2e8f0; | |
| } | |
| /* Step 4: Details */ | |
| .full-width { | |
| grid-column: 1 / -1; | |
| } | |
| .form-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| gap: 15px; | |
| margin-top: 30px; | |
| padding-top: 30px; | |
| border-top: 1px solid var(--border-color); | |
| } | |
| .btn { | |
| padding: 14px 28px; | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .btn-secondary { | |
| background-color: transparent; | |
| color: var(--text-gray); | |
| border: 1px solid var(--border-color); | |
| } | |
| .btn-secondary:hover { | |
| background-color: var(--bg-light); | |
| color: var(--text-dark); | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| color: var(--white); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--primary-hover); | |
| transform: translateY(-1px); | |
| box-shadow: var(--shadow-lg); | |
| } | |
| /* Success Modal */ | |
| .modal-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0,0,0,0.5); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 1000; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: var(--transition); | |
| } | |
| .modal-overlay.active { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .modal-content { | |
| background: var(--white); | |
| padding: 40px; | |
| border-radius: var(--radius); | |
| text-align: center; | |
| max-width: 400px; | |
| width: 90%; | |
| box-shadow: var(--shadow-lg); | |
| animation: slideUp 0.4s ease; | |
| } | |
| .success-icon { | |
| width: 80px; | |
| height: 80px; | |
| background: var(--success-color); | |
| color: var(--white); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 2.5rem; | |
| margin: 0 auto 20px; | |
| } | |
| .confirmation-details { | |
| background: var(--bg-light); | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| text-align: left; | |
| } | |
| .detail-row { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 10px; | |
| font-size: 0.95rem; | |
| border-bottom: 1px dashed var(--border-color); | |
| padding-bottom: 5px; | |
| } | |
| .detail-row:last-child { | |
| border-bottom: none; | |
| margin-bottom: 0; | |
| padding-bottom: 0; | |
| } | |
| .detail-label { | |
| color: var(--text-gray); | |
| } | |
| .detail-value { | |
| font-weight: 600; | |
| color: var(--text-dark); | |
| } | |
| /* Footer Link */ | |
| .footer-link { | |
| text-align: center; | |
| margin-top: 30px; | |
| font-size: 0.8rem; | |
| color: var(--text-gray); | |
| } | |
| .footer-link a { | |
| color: var(--primary-color); | |
| text-decoration: none; | |
| font-weight: 600; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes slideUp { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* Responsive Mobile */ | |
| @media (max-width: 900px) { | |
| .app-container { | |
| grid-template-columns: 1fr; | |
| height: auto; | |
| } | |
| .sidebar { | |
| padding: 30px 20px; | |
| text-align: center; | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .clinic-info { | |
| margin-top: 30px; | |
| } | |
| .testimonial { | |
| display: none; /* Hide testimonial on mobile to save space */ | |
| } | |
| .main-content { | |
| padding: 30px 20px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Application Container --> | |
| <div class="app-container"> | |
| <!-- Sidebar / Branding --> | |
| <aside class="sidebar"> | |
| <div> | |
| <div class="brand-logo"> | |
| <i class="fa-solid fa-heart-pulse"></i> | |
| <span>MediCare SaaS</span> | |
| </div> | |
| <div class="clinic-info"> | |
| <h2>Dr. Sarah Jenkins</h2> | |
| <p style="color: rgba(255,255,255,0.8); margin-bottom: 20px;">General Practitioner & Internal Medicine</p> | |
| <div class="info-item"> | |
| <i class="fa-solid fa-location-dot"></i> | |
| <span>123 Health Street, Medical District</span> | |
| </div> | |
| <div class="info-item"> | |
| <i class="fa-solid fa-clock"></i> | |
| <span>Mon - Fri: 8:00 AM - 7:00 PM</span> | |
| </div> | |
| <div class="info-item"> | |
| <i class="fa-solid fa-phone"></i> | |
| <span>+1 (555) 123-4567</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="testimonial"> | |
| <div class="stars"> | |
| <i class="fa-solid fa-star"></i> | |
| <i class="fa-solid fa-star"></i> | |
| <i class="fa-solid fa-star"></i> | |
| <i class="fa-solid fa-star"></i> | |
| <i class="fa-solid fa-star"></i> | |
| </div> | |
| <p>"The most user-friendly booking system I've used. Quick, clean, and efficient. Highly recommend the clinic."</p> | |
| </div> | |
| </aside> | |
| <!-- Main Booking Interface --> | |
| <main class="main-content"> | |
| <div class="header-section"> | |
| <h1>Book an Appointment</h1> | |
| <p>Fill in the details below to schedule your visit with Dr. Jenkins.</p> | |
| </div> | |
| <form class="booking-form" id="bookingForm"> | |
| <!-- STEP 1: Patient & Service Selection --> | |
| <div class="step-container" id="step1"> | |
| <h3 style="margin-bottom: 20px; font-size: 1.2rem; color: var(--primary-color);">Step 1: Patient & Service</h3> | |
| <div class="form-grid"> | |
| <div class="form-group"> | |
| <label for="fullName">Full Name</label> | |
| <input type="text" id="fullName" placeholder="e.g. John Doe" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="email">Email Address</label> | |
| <input type="email" id="email" placeholder="e.g. john@example.com" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="phone">Phone Number</label> | |
| <input type="tel" id="phone" placeholder="e.g. (555) 123-4567" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="service">Select Service</label> | |
| <select id="service" required> | |
| <option value="" disabled selected>Choose a service</option> | |
| <option value="general">General Consultation</option> | |
| <option value="cardiology">Cardiology Checkup</option> | |
| <option value="dentistry">Dental Inspection</option> | |
| <option value="neurology">Neurology Consultation</option> | |
| <option value="pediatrics">Pediatrics</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="form-actions" style="justify-content: flex-end;"> | |
| <button type="button" class="btn btn-primary" onclick="nextStep(2)"> | |
| Next Step <i class="fa-solid fa-arrow-right"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- STEP 2: Date Selection --> | |
| <div class="step-container hidden" id="step2"> | |
| <h3 style="margin-bottom: 20px; font-size: 1.2rem; color: var(--primary-color);">Step 2: Select Date</h3> | |
| <div class="calendar-widget"> | |
| <div class="calendar-header"> | |
| <button type="button" class="nav-btn" id="prevMonth"><i class="fa-solid fa-chevron-left"></i></button> | |
| <span id="currentDateDisplay">October 2023</span> | |
| <button type="button" class="nav-btn" id="nextMonth"><i class="fa-solid fa-chevron-right"></i></button> | |
| </div> | |
| <div class="weekdays"> | |
| <div>Sun</div> | |
| <div>Mon</div> | |
| <div>Tue</div> | |
| <div>Wed</div> | |
| <div>Thu</div> | |
| <div>Fri</div> | |
| <div>Sat</div> | |
| </div> | |
| <div class="days-grid" id="daysContainer"> | |
| <!-- Days generated by JS --> | |
| </div> | |
| </div> | |
| <div class="form-actions" style="justify-content: space-between;"> | |
| <button type="button" class="btn btn-secondary" onclick="prevStep(1)"> | |
| <i class="fa-solid fa-arrow-left"></i> Back | |
| </button> | |
| <button type="button" class="btn btn-primary" id="selectDateBtn" disabled onclick="nextStep(3)"> | |
| Select Time <i class="fa-solid fa-clock"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- STEP 3: Time Selection --> | |
| <div class="step-container hidden" id="step3"> | |
| <h3 style="margin-bottom: 20px; font-size: 1.2rem; color: var(--primary-color);">Step 3: Select Time</h3> | |
| <div class="time-slots-container"> | |
| <p style="margin-bottom: 15px; font-size: 0.9rem; color: var(--text-gray);">Available slots for <span id="selectedDateDisplay" style="font-weight: 700; color: var(--primary-color);">-</span></p> | |
| <div class="slots-grid" id="timeSlotsGrid"> | |
| <!-- Time slots generated by JS --> | |
| </div> | |
| </div> | |
| <div class="form-actions" style="justify-content: space-between;"> | |
| <button type="button" class="btn btn-secondary" onclick="prevStep(2)"> | |
| <i class="fa-solid fa-arrow-left"></i> Back | |
| </button> | |
| <button type="button" class="btn btn-primary" id="confirmBtn" disabled onclick="submitBooking()"> | |
| Confirm Booking <i class="fa-solid fa-check"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </form> | |
| <div class="footer-link"> | |
| By booking, you agree to our <a href="#">Privacy Policy</a> and <a href="#">Terms of Service</a>. | |
| <br><br> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: var(--primary-color); text-decoration: none; font-weight: 600;">Built with anycoder</a> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Success Modal --> | |
| <div class="modal-overlay" id="successModal"> | |
| <div class="modal-content"> | |
| <div class="success-icon"> | |
| <i class="fa-solid fa-check"></i> | |
| </div> | |
| <h2 style="margin-bottom: 15px; color: var(--text-dark);">Appointment Confirmed!</h2> | |
| <p style="color: var(--text-gray); margin-bottom: 20px;">Your appointment has been successfully scheduled.</p> | |
| <div class="confirmation-details"> | |
| <div class="detail-row"> | |
| <span class="detail-label">Patient Name</span> | |
| <span class="detail-value" id="modalName">-</span> | |
| </div> | |
| <div class="detail-row"> | |
| <span class="detail-label">Service</span> | |
| <span class="detail-value" id="modalService">-</span> | |
| </div> | |
| <div class="detail-row"> | |
| <span class="detail-label">Date</span> | |
| <span class="detail-value" id="modalDate">-</span> | |
| </div> | |
| <div class="detail-row"> | |
| <span class="detail-label">Time</span> | |
| <span class="detail-value" id="modalTime">-</span> | |
| </div> | |
| <div class="detail-row" style="border-bottom: none;"> | |
| <span class="detail-label">Booking ID</span> | |
| <span class="detail-value">#MED-8821</span> | |
| </div> | |
| </div> | |
| <p style="font-size: 0.9rem; color: var(--text-gray); margin-bottom: 20px;"> | |
| A confirmation email has been sent to <span id="modalEmail" style="font-weight: 600;"></span>. | |
| </p> | |
| <button class="btn btn-primary" onclick="closeModal()">Return to Dashboard</button> | |
| </div> | |
| </div> | |
| <script> | |
| // --- State Management --- | |
| const state = { | |
| currentMonth: 0, // 0 = current month | |
| selectedDate: null, | |
| selectedTime: null, | |
| selectedService: null, | |
| patientInfo: {} | |
| }; | |
| const services = { | |
| "general": "General Consultation", | |
| "cardiology": "Cardiology Checkup", | |
| "dentistry": "Dental Inspection", | |
| "neurology": "Neurology Consultation", | |
| "pediatrics": "Pediatrics" | |
| }; | |
| // --- Calendar Logic --- | |
| const calendar = { | |
| container: document.getElementById('daysContainer'), | |
| display: document.getElementById('currentDateDisplay'), | |
| prevBtn: document.getElementById('prevMonth'), | |
| nextBtn: document.getElementById('nextMonth'), | |
| init() { | |
| this.render(); | |
| this.prevBtn.addEventListener('click', () => this.changeMonth(-1)); | |
| this.nextBtn.addEventListener('click', () => this.changeMonth(1)); | |
| }, | |
| changeMonth(direction) { | |
| state.currentMonth += direction; | |
| this.render(); | |
| }, | |
| render() { | |
| this.container.innerHTML = ''; | |
| const date = new Date(); | |
| date.setMonth(date.getMonth() + state.currentMonth); | |
| const year = date.getFullYear(); | |
| const month = date.getMonth(); | |
| const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; | |
| this.display.textContent = `${monthNames[month]} ${year}`; | |
| // First day of the month (0-6) | |
| const firstDayIndex = new Date(year, month, 1).getDay(); | |
| // Total days in current month | |
| const lastDay = new Date(year, month + 1, 0).getDate(); | |
| // Add empty slots for previous month | |
| for (let i = 0; i < firstDayIndex; i++) { | |
| const emptyDiv = document.createElement('div'); | |
| emptyDiv.className = 'day empty'; | |
| this.container.appendChild(emptyDiv); | |
| } | |
| // Add days | |
| const today = new Date(); | |
| today.setHours(0,0,0,0); | |
| for (let i = 1; i <= lastDay; i++) { | |
| const dayDiv = document.createElement('div'); | |
| dayDiv.className = 'day'; | |
| dayDiv.textContent = i; | |
| const currentDayDate = new Date(year, month, i); | |
| currentDayDate.setHours(0,0,0,0); | |
| // Disable past dates | |
| if (currentDayDate < today) { | |
| dayDiv.classList.add('disabled'); | |
| } else { | |
| dayDiv.addEventListener('click', () => this.selectDate(dayDiv, currentDayDate)); | |
| } | |
| // Highlight today if visible | |
| if (i === today.getDate() && state.currentMonth === 0) { | |
| dayDiv.classList.add('today'); | |
| } | |
| this.container.appendChild(dayDiv); | |
| } | |
| }, | |
| selectDate(element, dateObj) { | |
| // Remove previous selection | |
| document.querySelectorAll('.day.selected').forEach(el => el.classList.remove('selected')); | |
| // Add new selection | |
| element.classList.add('selected'); | |
| state.selectedDate = dateObj; | |
| // Update UI | |
| const options = { weekday: 'short', month: 'short', day: 'numeric' }; | |
| document.getElementById('selectedDateDisplay').textContent = dateObj.toLocaleDateString('en-US', options); | |
| // Enable Next Button | |
| document.getElementById('selectDateBtn').disabled = false; | |
| // Reset time selection | |
| state.selectedTime = null; | |
| document.getElementById('confirmBtn').disabled = true; | |
| document.getElementById('timeSlotsGrid').innerHTML = ''; | |
| this.renderTimeSlots(); | |
| }, | |
| renderTimeSlots() { | |
| const grid = document.getElementById('timeSlotsGrid'); | |
| const startHour = 9; // 9 AM | |
| const endHour = 17; // 5 PM | |
| const interval = 60; // 60 minutes | |
| for (let hour = startHour; hour < endHour; hour++) { | |
| // Generate 2 slots per hour for demo (e.g., 10:00 and 10:30) | |
| const time1 = `${hour < 10 ? '0' + hour : hour}:00`; | |
| const time2 = `${hour < 10 ? '0' + hour : hour}:30`; | |
| this.createSlot(grid, time1); | |
| this.createSlot(grid, time2); | |
| } | |
| }, | |
| createSlot(grid, time) { | |
| const slot = document.createElement('div'); | |
| slot.className = 'time-slot'; | |
| slot.textContent = time; | |
| // Simulate random full slots | |
| const isFull = Math.random() > 0.8; | |
| if (isFull) { | |
| slot.classList.add('full'); | |
| } else { | |
| slot.addEventListener('click', () => { | |
| document.querySelectorAll('.time-slot.selected').forEach(el => el.classList.remove('selected')); | |
| slot.classList.add('selected'); | |
| state.selectedTime = time; | |
| document.getElementById('confirmBtn').disabled = false; | |
| }); | |
| } | |
| grid.appendChild(slot); | |
| } | |
| }; | |
| // --- Form Navigation Logic --- | |
| function nextStep(stepNum) { | |
| // Validation for Step 1 | |
| if (stepNum === 2) { | |
| const name = document.getElementById('fullName').value; | |
| const email = document.getElementById('email').value; | |
| const service = document.getElementById('service').value; | |
| if (!name || !email || !service) { | |
| alert("Please fill in all patient details and select a service."); | |
| return; | |
| } | |
| state.selectedService = services[service]; | |
| state.patientInfo = { name, email }; | |
| // Reset Calendar State | |
| state.currentMonth = 0; | |
| calendar.render(); | |
| document.getElementById('selectDateBtn').disabled = true; | |
| document.getElementById('timeSlotsGrid').innerHTML = ''; | |
| } | |
| // Hide all steps | |
| document.querySelectorAll('.step-container').forEach(el => el.classList.add('hidden')); | |
| // Show target step | |
| document.getElementById(`step${stepNum}`).classList.remove('hidden'); | |
| } | |
| function prevStep(stepNum) { | |
| document.querySelectorAll('.step-container').forEach(el => el.classList.add('hidden')); | |
| document.getElementById(`step${stepNum}`).classList.remove('hidden'); | |
| } | |
| // --- Submit Logic --- | |
| function submitBooking() { | |
| if (!state.selectedDate || !state.selectedTime) { | |
| alert("Please select a date and time for your appointment."); | |
| return; | |
| } | |
| // Populate Modal | |
| document.getElementById('modalName').textContent = state.patientInfo.name; | |
| document.getElementById('modalEmail').textContent = state.patientInfo.email; | |
| document.getElementById('modalService').textContent = state.selectedService; | |
| const dateOptions = { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }; | |
| document.getElementById('modalDate').textContent = state.selectedDate.toLocaleDateString('en-US', dateOptions); | |
| document.getElementById('modalTime').textContent = state.selectedTime; | |
| // Show Modal | |
| document.getElementById('successModal').classList.add('active'); | |
| } | |
| function closeModal() { | |
| document.getElementById('successModal').classList.remove('active'); | |
| // Reset Form | |
| document.getElementById('bookingForm').reset(); | |
| document.querySelectorAll('.step-container').forEach(el => el.classList.add('hidden')); | |
| document.getElementById('step1').classList.remove('hidden'); | |
| // Reset Calendar | |
| state.selectedDate = null; | |
| state.selectedTime = null; | |
| document.getElementById('selectDateBtn').disabled = true; | |
| document.getElementById('confirmBtn').disabled = true; | |
| document.getElementById('selectedDateDisplay').textContent = "-"; | |
| } | |
| // Initialize Calendar on Load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| calendar.init(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |