2026-05-08 12:22:08 +05:30

640 lines
15 KiB
PHP

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Create Appointment</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="<?= base_url('css/app.css') ?>">
<link rel="stylesheet" href="<?= base_url('css/dashboard.css') ?>">
<link rel="stylesheet"href="<?= base_url('css/appointment_form.css') ?>">
</head>
<body class="app-body overview-layout">
<aside class="ov-sidebar" id="sidebar">
<div class="ov-brand">
<h1><i class="bi bi-hospital me-1"></i> DoctGuide</h1>
<span>Control Panel</span>
</div>
<nav class="ov-nav">
<div class="ov-nav__section">Main</div>
<a href="<?= base_url('admin/dashboard') ?>"class="ov-nav__link"><i class="bi bi-speedometer2"></i>Dashboard</a>
<div class="ov-nav__section">Manage</div>
<div class="ov-nav__dropdown active">
<a href="#"class="ov-nav__link d-flex justify-content-between align-items-center"onclick="toggleNavDropdown(event,this)">
<span><i class="bi bi-people"></i> Patients</span>
<i class="bi bi-chevron-down dropdown-icon"></i>
</a>
<div class="ov-dropdown-menu">
<a href="<?= base_url('admin/patients') ?>"
class="ov-nav__sublink">
Patient List
</a>
</div>
</div>
<a href="<?= base_url('admin/appointments') ?>"
class="ov-nav__link active">
<i class="bi bi-calendar2-check"></i>
Appointments
</a>
</nav>
<div class="ov-sidebar__footer">
<a href="<?= base_url('logout') ?>">
<i class="bi bi-box-arrow-left"></i>Logout
</a>
</div>
</aside>
<div class="ov-main" id="mainContent">
<header class="ov-topbar">
<div class="d-flex align-items-center">
<button
class="ov-toggle-btn"
onclick="toggleSidebar()">
<i class="bi bi-list"
id="toggleIcon"></i>
</button>
<p class="ov-topbar__title mb-0">
Create Appointment
</p>
</div>
</header>
<main class="ov-content">
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success app-alert"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger app-alert"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<div class="appointment-card">
<div class="card-header-custom">
<h4>Create Appointment</h4>
<a href="<?= base_url('admin/doctor-search') ?>" class="back-btn">
Doctor's Info
</a>
</div>
<div class="form-body">
<div class="info-badge">
Fields marked with <span class="req">*</span> are required.
</div>
<form
id="appointmentForm"
novalidate
method="post"
action="<?= base_url('admin/appointments/create') ?>">
<input type="hidden"
name="patient_id"
value="<?= $patient['id'] ?>">
<div class="row g-4">
<div class="col-md-6">
<label class="form-label">Patient ID <span class="req">*</span>
</label>
<input
type="text"
readonly
class="form-control readonly-box"
value="<?= $formattedPatientId ?>">
</div>
<div class="col-md-6">
<label class="form-label">
Patient Name <span class="req">*</span>
</label>
<input
type="text"
readonly
class="form-control readonly-box"cdddbcce
value="<?= ($user['first_name'] ?? '') .' '. ($user['last_name'] ?? '') ?>">
</div>
<div class="col-md-6">
<label class="form-label">Service Request <span class="req">*</span></label>
<div class="multi-select-arrow-wrap">
<select
id="apptSpecialization"
class="form-select select2"
name="services[]"
multiple="multiple">
<!-- <option value="">
Select Services
</option> -->
</select>
<i class="bi bi-chevron-down select2-arrow-hint" aria-hidden="true"></i>
</div>
<div id="specError" class="error-text"></div>
</div>
<div class="col-md-6">
<label class="form-label">
Appointment Date & Time <span class="req">*</span>
</label>
<input
type="datetime-local"
id="apptDateTime"
name="appointment_datetime"
class="form-control">
<div id="dateTimeError" class="error-text"></div>
</div>
<div class="col-md-12">
<div id="doctorsContainer">
<label class="form-label fw-medium">
Preference (Physician)
<!-- <span class="text-danger">*</span> -->
</label>
<!-- <input
type="text"
id="doctorSearchBox"
class="form-control mb-3"
placeholder="Search doctors...">
<div id="doctorsList" class="doctor-list-container"></div> -->
<div class="single-select-wrap">
<select
id="doctorSelect"
class="form-select"
name="doctor_pick">
<option value=""></option>
</select>
<i class="bi bi-chevron-down single-arrow"></i>
</div>
<div id="doctorError" class="text-danger mt-1"></div>
<input
type="hidden"
id="selectedDoctorId"
name="doctor_id">
</div>
</div>
<div class="col-12">
<label class="form-label">
Notes
</label>
<textarea
rows="2"
name="note"
class="form-control"
placeholder="Enter notes..."></textarea>
</div>
</div>
<div class="action-row">
<button type="button"
onclick="history.back()"
class="cancel-btn">
Cancel
</button>
<button type="submit"
class="save-btn">
Save Appointment
</button>
</div>
</form>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
function loadSpecializations(){
fetch("<?= base_url('admin/specializations') ?>",{
headers:{
'X-Requested-With':'XMLHttpRequest'
}
})
.then(response=>response.json())
.then(data=>{
if(data.success){
let select=document.getElementById('apptSpecialization');
data.specializations.forEach(spec=>{
let option=document.createElement('option');
option.value=spec.id;
option.textContent=spec.name;
select.appendChild(option);
});
$('#apptSpecialization').select2({
placeholder:'Select one or more services',
closeOnSelect:false,
allowClear:false,
width:'100%',
dropdownParent:$('body'),
minimumResultsForSearch: Infinity
});
}
})
.catch(error=>{
console.error(error);
});
}
document.addEventListener('DOMContentLoaded', function(){
const dateTimeInput = document.getElementById('apptDateTime');
if (dateTimeInput) {
const now = new Date();
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
dateTimeInput.min = now.toISOString().slice(0, 16);
}
loadSpecializations();
/* Initialize doctor select2 */
$('#doctorSelect').select2({
placeholder: '--Select--',
allowClear: true,
width: '100%'
});
/* trigger doctor fetch when changed */
$('#apptSpecialization').on('select2:select', function () {
console.log('Specialization selected');
fetchAvailableDoctors();
});
$('#apptSpecialization').on('select2:unselect', function () {
console.log('Specialization unselected');
fetchAvailableDoctors();
});
$('#apptSpecialization').on('change', function () {
console.log('Specialization changed');
fetchAvailableDoctors();
});
/* Chevron animation on open */
$('#apptSpecialization').on('select2:open', function () {
const $wrapper = $(this).closest('.multi-select-arrow-wrap');
$wrapper.find('.select2-arrow-hint').removeClass('bi-chevron-down').addClass('bi-chevron-up');
});
/* Chevron animation on close */
$('#apptSpecialization').on('select2:close', function () {
const $wrapper = $(this).closest('.multi-select-arrow-wrap');
$wrapper.find('.select2-arrow-hint').removeClass('bi-chevron-up').addClass('bi-chevron-down');
});
/* Click chevron to toggle dropdown */
$('.select2-arrow-hint').on('mousedown', function (event) {
event.preventDefault();
event.stopPropagation();
const $select = $(this).siblings('select');
if ($select.data('select2') && $select.data('select2').isOpen()) {
$select.select2('close');
} else {
$select.select2('open');
}
});
$('.single-arrow').on('click', function(){
let select=$(this).siblings('select');
if(select.data('select2') && select.data('select2').isOpen()){
select.select2('close');
}
else{
select.select2('open');
}
});
document
.getElementById('apptDateTime')
.addEventListener('change',fetchAvailableDoctors);
/* Doctor search functionality */
$('#doctorSearchBox').on('keyup', function(){
let searchTerm = $(this).val().toLowerCase();
$('.doctor-option').each(function(){
let doctorName = $(this).data('doctor-name').toLowerCase();
if(doctorName.includes(searchTerm)){
$(this).show();
} else {
$(this).hide();
}
});
});
});
function fetchAvailableDoctors(){
console.log("fetch fired");
let spec = $('#apptSpecialization').val();
console.log('Specializations selected:', spec);
if(!spec || spec.length===0){
let $doctorSelect = $('#doctorSelect');
if ($doctorSelect.hasClass("select2-hidden-accessible")) {
$doctorSelect.select2('destroy');
}
$doctorSelect.html('<option value=""></option>');
$doctorSelect.prop('disabled', true);
$doctorSelect.select2({
placeholder:'Select specialization first...',
allowClear:true,
width:'100%'
});
return;
}
let dateTime = document.getElementById('apptDateTime').value;
// Split date and time if available
let date = '';
let time = '';
if(dateTime){
let parts = dateTime.split("T");
date = parts[0];
time = parts[1];
}
let formData=new FormData();
formData.append('date', date);
formData.append('time', time);
spec.forEach(function(id){
formData.append('specialization_id[]', id);
});
fetch("<?= base_url('admin/available-doctors') ?>",{
method:'POST',
body:formData,
headers:{
'X-Requested-With':'XMLHttpRequest'
}
})
.then(response=>response.json())
.then(data=>{
if(data.success && data.doctors){
displayDoctors(data.doctors);
}else{
displayDoctors([]);
console.log(data);
}
})
.catch(error=>{
console.error(error);
});
}
function displayDoctors(doctors){
let select = $('#doctorSelect');
/* Destroy old select2 first */
if(select.hasClass("select2-hidden-accessible")){
select.select2('destroy');
}
select.empty();
select.append('<option value=""></option>');
if(!doctors.length){
select.select2({
placeholder: 'Search doctors...',
allowClear: true,
width: '100%'
});
return;
}
/* Add doctors */
doctors.forEach(function(doc){
let genderText = '';
if(doc.gender){
genderText = ' | ' + doc.gender.trim();
}
let label = 'Dr. ' + doc.name + genderText;
select.append(
new Option(label, doc.id, false, false)
);
});
/* Reinitialize once */
select.select2({
placeholder: 'Search doctors...',
allowClear: true,
width: '100%',
templateResult: function(data){
return data.text; // dropdown text
},
templateSelection: function(data){
return data.text; // selected field text
}
});
/* When user selects doctor */
select.off('select2:select').on('select2:select', function(e){
let selectedId = e.params.data.id;
$('#selectedDoctorId').val(selectedId);
/* force selected text into box */
$(this).val(selectedId).trigger('change');
});
}
function selectDoctor(id){
document.getElementById('selectedDoctorId').value = id;
}
document
.getElementById('appointmentForm')
.addEventListener('submit',function(e){
let spec =
document.getElementById('apptSpecialization');
let dateTime =
document.getElementById('apptDateTime');
let doctor =
document.getElementById('selectedDoctorId');
let valid=true;
/* reset */
[spec,dateTime].forEach(el=>{
el.classList.remove('field-error');
});
document.getElementById('specError').innerHTML='';
document.getElementById('dateTimeError').innerHTML='';
document.getElementById('doctorError').innerHTML='';
if($('#apptSpecialization').val().length===0){
spec.classList.add('field-error');
document.getElementById('specError')
.innerHTML='Please select at least one service';
valid=false;
}
let selectedServices = $('#apptSpecialization').val() || [];
let uniqueServices = new Set(selectedServices);
if (selectedServices.length !== uniqueServices.size) {
spec.classList.add('field-error');
document.getElementById('specError').innerHTML='Duplicate services are not allowed';
valid=false;
}
if(!dateTime.value){
dateTime.classList.add('field-error');
document.getElementById('dateTimeError')
.innerHTML='Please choose appointment date and time';
valid=false;
}
else if (new Date(dateTime.value) < new Date()) {
dateTime.classList.add('field-error');
document.getElementById('dateTimeError')
.innerHTML='Past date or time booking is not allowed';
valid=false;
}
// if(!doctor.value){
// document.getElementById('doctorError').innerHTML =
// 'Please select a doctor';
// valid=false;
// }
if(!valid){
e.preventDefault();
}
});
</script>
</main>
</div>
<script>
function toggleSidebar() {
const sidebar =
document.getElementById('sidebar');
const main =
document.getElementById('mainContent');
const icon =
document.getElementById('toggleIcon');
sidebar.classList.toggle('collapsed');
main.classList.toggle('expanded');
icon.className =
sidebar.classList.contains('collapsed')
? 'bi bi-layout-sidebar'
: 'bi bi-list';
}
function toggleNavDropdown(event,element){
event.preventDefault();
element.parentElement.classList.toggle('active');
}
</script>
</body>
</html>