640 lines
15 KiB
PHP
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>
|