38960-vm/includes/pages/dashboard.php
2026-03-28 10:57:32 +00:00

899 lines
44 KiB
PHP

<?php
// --- AJAX HANDLER FOR PATIENT SEARCH ---
if (isset($_GET['ajax_search'])) {
$search_query = $_GET['search'] ?? '';
$limit = 10;
$where = "WHERE 1=1";
$params = [];
if ($search_query) {
$where .= " AND (p.name LIKE ? OR p.phone LIKE ? OR p.civil_id LIKE ?)";
$params[] = "%$search_query%";
$params[] = "%$search_query%";
$params[] = "%$search_query%";
}
$query = "
SELECT p.*, ic.name_$lang as insurance_name
FROM patients p
LEFT JOIN insurance_companies ic ON p.insurance_company_id = ic.id
$where
ORDER BY p.id DESC
LIMIT $limit";
$stmt = $db->prepare($query);
$stmt->execute($params);
$patients = $stmt->fetchAll();
ob_start();
if (empty($patients)): ?>
<tr><td colspan="5" class="text-center py-3 text-muted"><?php echo __('no_patients_found'); ?></td></tr>
<?php else:
foreach ($patients as $p): ?>
<tr>
<td>
<div class="fw-bold"><?php echo htmlspecialchars($p['name']); ?></div>
<small class="text-muted"><?php echo $p['dob']; ?> (<?php echo calculate_age($p['dob']); ?>)</small>
</td>
<td><?php echo htmlspecialchars($p['phone']); ?></td>
<td>
<span class="badge <?php echo $p['insurance_name'] ? 'bg-primary' : 'bg-secondary'; ?>">
<?php echo $p['insurance_name'] ?: __('not_insured'); ?>
</span>
</td>
<td class="text-end">
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $p['id']; ?>)" title="<?php echo __('add_visit'); ?>">
<i class="bi bi-clipboard-plus"></i>
</button>
<button class="btn btn-sm btn-outline-primary" onclick="showEditPatientModal(<?php echo htmlspecialchars(json_encode($p, JSON_UNESCAPED_UNICODE)); ?>)" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
</td>
</tr>
<?php endforeach;
endif;
$html = ob_get_clean();
header('Content-Type: application/json');
echo json_encode(['html' => $html]);
exit;
}
// Fetch Stats
$total_patients = $db->query("SELECT COUNT(*) FROM patients")->fetchColumn();
$today_appointments_count = $db->query("SELECT COUNT(*) FROM appointments WHERE DATE(start_time) = CURDATE()")->fetchColumn();
$total_visits = $db->query("SELECT COUNT(*) FROM visits")->fetchColumn();
$total_revenue = $db->query("SELECT SUM(total_amount) FROM bills WHERE status = 'Paid'")->fetchColumn() ?: 0;
$pending_revenue = $db->query("SELECT SUM(total_amount) FROM bills WHERE status = 'Pending'")->fetchColumn() ?: 0;
$total_xrays = $db->query("SELECT COUNT(*) FROM xray_inquiries")->fetchColumn();
$total_labs = $db->query("SELECT COUNT(*) FROM laboratory_inquiries")->fetchColumn();
// Fetch Running Visits (Not Completed/Cancelled)
$running_visits_sql = "
SELECT v.*, p.name as patient_name, doc.name_$lang as doctor_name, nur.name_$lang as nurse_name
FROM visits v
JOIN patients p ON v.patient_id = p.id
LEFT JOIN employees doc ON v.doctor_id = doc.id
LEFT JOIN employees nur ON v.nurse_id = nur.id
WHERE v.status IN ('CheckIn', 'In Progress')
ORDER BY v.visit_date ASC";
$running_visits = $db->query($running_visits_sql)->fetchAll();
// Initial Patients Load
$patients_sql = "
SELECT p.*, ic.name_$lang as insurance_name
FROM patients p
LEFT JOIN insurance_companies ic ON p.insurance_company_id = ic.id
ORDER BY p.id DESC LIMIT 5";
$patients = $db->query($patients_sql)->fetchAll();
// Today's Appointments
$appointments_sql = "
SELECT a.*, p.name as patient_name, doc.name_$lang as doctor_name, nur.name_$lang as nurse_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
LEFT JOIN employees doc ON a.doctor_id = doc.id
LEFT JOIN employees nur ON a.nurse_id = nur.id
WHERE DATE(a.start_time) = CURDATE()
ORDER BY a.start_time ASC";
$appointments = $db->query($appointments_sql)->fetchAll();
?>
<!-- Dashboard Stats -->
<div class="row mb-4">
<div class="col-md-3 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-people"></i>
<h3><?php echo $total_patients; ?></h3>
<p class="text-muted mb-0"><?php echo __('total_patients'); ?></p>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-calendar-check"></i>
<h3><?php echo $today_appointments_count; ?></h3>
<p class="text-muted mb-0"><?php echo __('today_appointments'); ?></p>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-flask text-info"></i>
<h3><?php echo $total_labs; ?></h3>
<p class="text-muted mb-0"><?php echo __('laboratory'); ?> <?php echo __('inquiries'); ?></p>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-x-diamond text-primary"></i>
<h3><?php echo $total_xrays; ?></h3>
<p class="text-muted mb-0"><?php echo __('xray'); ?> <?php echo __('inquiries'); ?></p>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-currency-dollar text-success"></i>
<h3><?php echo format_currency($total_revenue); ?></h3>
<p class="text-muted mb-0"><?php echo __('revenue'); ?></p>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card stat-card h-100">
<i class="bi bi-hourglass-split text-warning"></i>
<h3><?php echo format_currency($pending_revenue); ?></h3>
<p class="text-muted mb-0"><?php echo __('pending'); ?></p>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row mb-4">
<div class="col-12">
<div class="card p-3 d-flex flex-row justify-content-between align-items-center">
<h5 class="mb-0 fw-bold"><?php echo __('dashboard'); ?></h5>
<div class="d-flex flex-wrap gap-2">
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addPatientModal">
<i class="bi bi-plus-lg"></i> <?php echo __('add_patient'); ?>
</button>
<button class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#bookAppointmentModal">
<i class="bi bi-calendar-plus"></i> <?php echo __('book_appointment'); ?>
</button>
<button class="btn btn-info btn-sm text-white" onclick="showReceptionistVisitModal()">
<i class="bi bi-clipboard-plus"></i> <?php echo __('add_visit'); ?>
</button>
<button class="btn btn-warning btn-sm text-white" data-bs-toggle="modal" data-bs-target="#addXrayInquiryModal">
<i class="bi bi-x-diamond"></i> <?php echo __('add_xray_inquiry'); ?>
</button>
</div>
</div>
</div>
</div>
<!-- Running Visits Table -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header py-3 d-flex justify-content-between align-items-center bg-primary text-white">
<h6 class="mb-0 fw-bold"><i class="bi bi-play-circle-fill me-2"></i> <?php echo __('running_visits'); ?></h6>
<span class="badge bg-white text-primary"><?php echo count($running_visits); ?></span>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light">
<tr>
<th><?php echo __('time'); ?></th>
<th><?php echo __('patient'); ?></th>
<th><?php echo __('doctor'); ?></th>
<th><?php echo __('status'); ?></th>
<th class="text-end"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($running_visits as $v): ?>
<tr>
<td><?php echo date('Y-m-d H:i', strtotime($v['visit_date'])); ?></td>
<td>
<div class="fw-bold"><?php echo htmlspecialchars($v['patient_name']); ?></div>
</td>
<td>
<?php
if ($v['doctor_name']) echo htmlspecialchars($v['doctor_name']);
elseif ($v['nurse_name']) echo htmlspecialchars($v['nurse_name']) . ' (' . __('nurse') . ')';
else echo '-';
?>
</td>
<td><span class="badge bg-warning text-dark"><?php echo __($v['status']); ?></span></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-success" onclick="showCheckoutModal(<?php echo $v['id']; ?>)" title="<?php echo __('checkout_payment'); ?>">
<i class="bi bi-cash-stack"></i> <?php echo __('checkout_payment'); ?>
</button>
</td>
</tr>
<?php endforeach; if (empty($running_visits)): ?>
<tr><td colspan="5" class="text-center py-4 text-muted"><?php echo __('no_running_visits'); ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="row">
<!-- Today's Appointments -->
<div class="col-lg-6 mb-4">
<div class="card shadow-sm h-100">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-calendar-event-fill me-2"></i> <?php echo __('today_appointments'); ?></h6>
<span class="badge bg-white text-primary"><?php echo count($appointments); ?></span>
</div>
<div class="card-body p-0">
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-hover mb-0">
<thead class="table-light sticky-top">
<tr>
<th><?php echo __('time'); ?></th>
<th><?php echo __('patient'); ?></th>
<th><?php echo __('doctor'); ?></th>
<th><?php echo __('status'); ?></th>
<th class="text-end"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($appointments as $a): ?>
<tr>
<td><?php echo date('H:i', strtotime($a['start_time'])); ?></td>
<td>
<div class="fw-bold"><?php echo htmlspecialchars($a['patient_name']); ?></div>
</td>
<td>
<?php
if ($a['doctor_name']) echo htmlspecialchars($a['doctor_name']);
elseif ($a['nurse_name']) echo htmlspecialchars($a['nurse_name']) . ' (' . __('nurse') . ')';
else echo '-';
?>
</td>
<td><span class="badge <?php echo $a['status'] === 'Completed' ? 'bg-success' : 'bg-secondary'; ?>"><?php echo __($a['status']); ?></span></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $a['patient_id']; ?>)" title="<?php echo __('add_visit'); ?>">
<i class="bi bi-clipboard-plus"></i>
</button>
<button class="btn btn-sm btn-outline-primary" onclick='showDashboardEditModal(<?php echo json_encode($a, JSON_HEX_APOS | JSON_HEX_QUOT); ?>)' title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
</td>
</tr>
<?php endforeach; if (empty($appointments)): ?>
<tr><td colspan="5" class="text-center py-4 text-muted"><?php echo __('no_appointments_today'); ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Patient List / Search -->
<div class="col-lg-6 mb-4">
<div class="card shadow-sm h-100">
<div class="card-header py-2">
<div class="d-flex justify-content-between align-items-center">
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-people-fill me-2"></i> <?php echo __('patients'); ?></h6>
<div class="input-group input-group-sm" style="width: 200px;">
<input type="text" id="dashboardPatientSearch" class="form-control" placeholder="<?php echo __('search'); ?>...">
<button class="btn btn-light" type="button"><i class="bi bi-search"></i></button>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light sticky-top">
<tr>
<th><?php echo __('patient'); ?></th>
<th><?php echo __('phone'); ?></th>
<th><?php echo __('insurance'); ?></th>
<th class="text-end"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody id="dashboardPatientsTable">
<?php foreach ($patients as $p): ?>
<tr>
<td>
<div class="fw-bold"><?php echo htmlspecialchars($p['name']); ?></div>
<small class="text-muted"><?php echo $p['dob']; ?> (<?php echo calculate_age($p['dob']); ?>)</small>
</td>
<td><?php echo htmlspecialchars($p['phone']); ?></td>
<td><span class="badge <?php echo $p['insurance_name'] ? 'bg-primary' : 'bg-secondary'; ?>"><?php echo $p['insurance_name'] ?: __('not_insured'); ?></span></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $p['id']; ?>)" title="<?php echo __('add_visit'); ?>">
<i class="bi bi-clipboard-plus"></i>
</button>
<button class="btn btn-sm btn-outline-primary" onclick="showEditPatientModal(<?php echo htmlspecialchars(json_encode($p, JSON_UNESCAPED_UNICODE)); ?>)" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
</td>
</tr>
<?php endforeach; if (empty($patients)): ?>
<tr><td colspan="4" class="text-center py-4 text-muted">No patients found.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Dashboard Appointment Edit Modal -->
<div class="modal fade" id="dashboardAppointmentModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0 shadow">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold text-secondary" id="dashModalTitle">Edit Appointment</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pt-3">
<input type="hidden" id="dash_apt_id">
<div class="mb-3">
<label class="form-label small text-muted"><?php echo __('patient'); ?></label>
<select id="dash_apt_patient_id" class="form-select select2-modal-dash">
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : ''); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label small text-muted">Visit Type</label>
<select id="dash_apt_visit_type" class="form-select" onchange="dashboard_toggleAddressField()">
<option value="Clinic">Clinic Visit</option>
<option value="Home">Home Visit</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label small text-muted">Provider Type</label>
<select id="dash_apt_provider_type" class="form-select" onchange="dashboard_toggleProviderField()">
<option value="Doctor">Doctor</option>
<option value="Nurse">Nurse</option>
</select>
</div>
</div>
<div class="mb-3 d-none" id="dash_div_address">
<label class="form-label small text-muted">Address (For Home Visit)</label>
<textarea id="dash_apt_address" class="form-control" rows="2" placeholder="Enter patient address..."></textarea>
</div>
<div class="mb-3" id="dash_div_doctor">
<label class="form-label small text-muted"><?php echo __('doctor'); ?></label>
<select id="dash_apt_doctor_id" class="form-select select2-modal-dash">
<option value="">Select Doctor</option>
<?php foreach ($all_doctors as $d): ?>
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3 d-none" id="dash_div_nurse">
<label class="form-label small text-muted"><?php echo __('nurse'); ?></label>
<select id="dash_apt_nurse_id" class="form-select select2-modal-dash">
<option value="">Select Nurse</option>
<?php foreach ($all_nurses as $n): ?>
<option value="<?php echo $n['id']; ?>"><?php echo htmlspecialchars($n['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label small text-muted"><?php echo __('date'); ?> & <?php echo __('time'); ?></label>
<input type="datetime-local" id="dash_apt_start_time" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small text-muted"><?php echo __('status'); ?></label>
<select id="dash_apt_status" class="form-select">
<option value="Scheduled">Scheduled</option>
<option value="Completed">Completed</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label small text-muted"><?php echo __('reason'); ?></label>
<textarea id="dash_apt_reason" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer border-0 bg-light rounded-bottom">
<div class="d-flex justify-content-between w-100">
<button type="button" class="btn btn-outline-danger shadow-sm" onclick="dashboard_deleteAppointment()">
<i class="bi bi-trash"></i> <?php echo __('delete'); ?>
</button>
<div>
<button type="button" class="btn btn-secondary shadow-sm me-2" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="button" class="btn btn-primary shadow-sm px-4" onclick="dashboard_saveAppointment()">
<i class="bi bi-check2-circle me-1"></i> <?php echo __('save'); ?>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Receptionist Quick Visit Modal -->
<div class="modal fade" id="receptionistVisitModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<form action="dashboard.php" method="POST">
<input type="hidden" name="action" value="record_visit">
<div class="modal-content border-0 shadow">
<div class="modal-header">
<h5 class="modal-title fw-bold text-secondary"><?php echo __('new_visit'); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label"><?php echo __('patient'); ?></label>
<select name="patient_id" id="quick_visit_patient_id" class="form-select select2-modal-quick" required>
<option value=""><?php echo __('select'); ?>...</option>
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>">
<?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : '') . (!empty($p['civil_id']) ? ' (' . htmlspecialchars($p['civil_id']) . ')' : ''); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?php echo __('doctor'); ?></label>
<select name="doctor_id" id="quick_visit_doctor_id" class="form-select select2-modal-quick" required>
<option value=""><?php echo __('select'); ?>...</option>
<?php foreach ($all_doctors as $d): ?>
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="quick_visit_token" name="generate_token" value="1" checked>
<label class="form-check-label" for="quick_visit_token"><?php echo __('issue_token'); ?></label>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="submit" class="btn btn-primary px-4"><?php echo __('save'); ?></button>
</div>
</div>
</form>
</div>
</div>
<!-- Checkout/Billing Modal -->
<div class="modal fade" id="checkoutModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold text-secondary"><?php echo __('checkout_payment'); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pt-3">
<input type="hidden" id="checkout_bill_id">
<div class="alert alert-light border d-flex justify-content-between align-items-center mb-3">
<div>
<div class="small text-muted"><?php echo __('patient'); ?></div>
<div class="fw-bold" id="checkout_patient_name">-</div>
</div>
<div class="text-end">
<div class="small text-muted"><?php echo __('insurance'); ?></div>
<div class="fw-bold text-primary" id="checkout_insurance_name">-</div>
</div>
</div>
<!-- Add Items -->
<div class="card bg-light border-0 mb-3">
<div class="card-body p-2">
<div class="row g-2">
<div class="col-md-4">
<select id="checkout_service_select" class="form-select form-select-sm select2-modal-checkout">
<option value=""><?php echo __('select_service'); ?>...</option>
<?php foreach ($all_services as $s): ?>
<option value="<?php echo $s['name']; ?>" data-price="<?php echo $s['price']; ?>"><?php echo htmlspecialchars($s['name']); ?> (<?php echo format_currency($s['price']); ?>)</option>
<?php endforeach; ?>
<?php foreach ($all_tests as $t): ?>
<option value="<?php echo $t['name']; ?>" data-price="<?php echo $t['price']; ?>"><?php echo htmlspecialchars($t['name']); ?> (<?php echo format_currency($t['price']); ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<input type="text" id="checkout_custom_item" class="form-control form-control-sm" placeholder="Custom Item Description">
</div>
<div class="col-md-2">
<input type="number" id="checkout_item_price" class="form-control form-control-sm" placeholder="Price">
</div>
<div class="col-md-2">
<button class="btn btn-primary btn-sm w-100" onclick="addBillItem()"><i class="bi bi-plus-lg"></i> Add</button>
</div>
</div>
</div>
</div>
<!-- Items Table -->
<div class="table-responsive mb-3">
<table class="table table-sm table-bordered">
<thead class="table-light">
<tr>
<th>Description</th>
<th class="text-end" width="100">Amount</th>
<th width="50"></th>
</tr>
</thead>
<tbody id="checkout_items_table">
<!-- Items go here -->
</tbody>
<tfoot class="table-light fw-bold">
<tr>
<td class="text-end">Total</td>
<td class="text-end" id="checkout_total_amount">$0.00</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
<!-- Payment Details -->
<div class="row g-3">
<div class="col-md-4">
<label class="form-label small text-muted">Insurance Coverage %</label>
<div class="input-group input-group-sm">
<input type="number" id="checkout_insurance_percent" class="form-control" value="0" min="0" max="100" onchange="calculateTotals()">
<span class="input-group-text">%</span>
</div>
</div>
<div class="col-md-4">
<label class="form-label small text-muted">Insurance Pays</label>
<div class="input-group input-group-sm">
<span class="input-group-text"><?php echo $currency_symbol ?? '$'; ?></span>
<input type="number" id="checkout_insurance_amount" class="form-control" readonly>
</div>
</div>
<div class="col-md-4">
<label class="form-label small text-muted fw-bold text-dark">Patient Pays</label>
<div class="input-group input-group-sm">
<span class="input-group-text"><?php echo $currency_symbol ?? '$'; ?></span>
<input type="number" id="checkout_patient_amount" class="form-control fw-bold" readonly>
</div>
</div>
</div>
<hr>
<div class="row g-3 align-items-end">
<div class="col-md-6">
<label class="form-label small text-muted">Payment Method</label>
<select id="checkout_payment_method" class="form-select">
<option value="Cash">Cash</option>
<option value="Card">Card</option>
<option value="Online">Online</option>
<option value="Insurance">Insurance Only</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label small text-muted">Notes</label>
<input type="text" id="checkout_notes" class="form-control" placeholder="Optional notes...">
</div>
</div>
</div>
<div class="modal-footer border-0 bg-light rounded-bottom">
<button type="button" class="btn btn-outline-dark me-auto" onclick="printBill()"><i class="bi bi-printer"></i> <?php echo __('print_bill'); ?></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button>
<button type="button" class="btn btn-success px-4" onclick="completePayment()">
<i class="bi bi-check2-circle me-1"></i> Pay & Close Visit
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('dashboardPatientSearch');
const tableBody = document.getElementById('dashboardPatientsTable');
let timeout = null;
searchInput.addEventListener('input', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
const query = searchInput.value;
fetch('dashboard.php?ajax_search=1&search=' + encodeURIComponent(query))
.then(response => response.json())
.then(data => {
tableBody.innerHTML = data.html;
})
.catch(error => console.error('Error:', error));
}, 300);
});
// Initialize Select2
$('#dashboardAppointmentModal').on('shown.bs.modal', function() {
$('.select2-modal-dash').each(function() {
$(this).select2({ dropdownParent: $('#dashboardAppointmentModal'), theme: 'bootstrap-5', width: '100%' });
});
});
$('#receptionistVisitModal').on('shown.bs.modal', function() {
$('.select2-modal-quick').each(function() {
$(this).select2({ dropdownParent: $('#receptionistVisitModal'), theme: 'bootstrap-5', width: '100%' });
});
});
$('#checkoutModal').on('shown.bs.modal', function() {
$('.select2-modal-checkout').each(function() {
$(this).select2({ dropdownParent: $('#checkoutModal'), theme: 'bootstrap-5', width: '100%' });
// Helper for filling inputs on selection
$(this).on('select2:select', function(e) {
var data = e.params.data;
// Since select2 data doesn't keep data attributes easily without extra work,
// we'll access the underlying option
var element = $(this).find('option:selected');
var price = element.data('price');
var text = element.text();
if (price) {
document.getElementById('checkout_custom_item').value = text.split(' (')[0];
document.getElementById('checkout_item_price').value = price;
}
});
});
});
// Auto-fill address
$('#dash_apt_patient_id').on('change', function() {
if (document.getElementById('dash_apt_visit_type').value === 'Home') {
var addressField = document.getElementById('dash_apt_address');
if (!addressField.value) {
var selectedOption = this.options[this.selectedIndex];
if (selectedOption && selectedOption.dataset.address) {
addressField.value = selectedOption.dataset.address;
}
}
}
});
});
// ... existing functions ...
function dashboard_toggleAddressField() {
var type = document.getElementById('dash_apt_visit_type').value;
var div = document.getElementById('dash_div_address');
if (type === 'Home') div.classList.remove('d-none'); else div.classList.add('d-none');
}
function dashboard_toggleProviderField() {
var type = document.getElementById('dash_apt_provider_type').value;
var divDoc = document.getElementById('dash_div_doctor');
var divNurse = document.getElementById('dash_div_nurse');
if (type === 'Nurse') { divDoc.classList.add('d-none'); divNurse.classList.remove('d-none'); }
else { divDoc.classList.remove('d-none'); divNurse.classList.add('d-none'); }
}
function showDashboardEditModal(data) {
document.getElementById('dash_apt_id').value = data.id;
$('#dash_apt_patient_id').val(data.patient_id).trigger('change');
document.getElementById('dash_apt_visit_type').value = data.visit_type || 'Clinic';
document.getElementById('dash_apt_address').value = data.address || '';
dashboard_toggleAddressField();
var providerType = data.nurse_id ? 'Nurse' : 'Doctor';
document.getElementById('dash_apt_provider_type').value = providerType;
dashboard_toggleProviderField();
if (providerType === 'Doctor') $('#dash_apt_doctor_id').val(data.doctor_id).trigger('change');
else $('#dash_apt_nurse_id').val(data.nurse_id).trigger('change');
var start = new Date(data.start_time);
if (!isNaN(start)) {
var offset = start.getTimezoneOffset() * 60000;
document.getElementById('dash_apt_start_time').value = (new Date(start.getTime() - offset)).toISOString().slice(0, 16);
} else {
document.getElementById('dash_apt_start_time').value = data.start_time.replace(' ', 'T').slice(0, 16);
}
document.getElementById('dash_apt_status').value = data.status;
document.getElementById('dash_apt_reason').value = data.reason || '';
new bootstrap.Modal(document.getElementById('dashboardAppointmentModal')).show();
}
function showReceptionistVisitModal(patientId) {
$('#quick_visit_patient_id').val(patientId || '').trigger('change');
$('#quick_visit_doctor_id').val('').trigger('change');
new bootstrap.Modal(document.getElementById('receptionistVisitModal')).show();
}
function dashboard_saveAppointment() {
var id = document.getElementById('dash_apt_id').value;
var providerType = document.getElementById('dash_apt_provider_type').value;
var data = {
action: 'update',
id: id,
patient_id: document.getElementById('dash_apt_patient_id').value,
doctor_id: providerType === 'Doctor' ? document.getElementById('dash_apt_doctor_id').value : null,
nurse_id: providerType === 'Nurse' ? document.getElementById('dash_apt_nurse_id').value : null,
visit_type: document.getElementById('dash_apt_visit_type').value,
address: document.getElementById('dash_apt_address').value,
start_time: document.getElementById('dash_apt_start_time').value,
status: document.getElementById('dash_apt_status').value,
reason: document.getElementById('dash_apt_reason').value
};
fetch('api/appointments.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
.then(r => r.json()).then(res => { if(res.success) window.location.reload(); else alert(res.error); });
}
function dashboard_deleteAppointment() {
var id = document.getElementById('dash_apt_id').value;
if (id && confirm('Delete?')) {
fetch('api/appointments.php', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action:'delete', id:id}) })
.then(r => r.json()).then(res => { if(res.success) window.location.reload(); else alert(res.error); });
}
}
// --- BILLING FUNCTIONS ---
let currentBillId = 0;
let currentTotal = 0;
function fetchBillDetails(id, type = 'visit') {
const param = type === 'visit' ? `visit_id=${id}` : `bill_id=${id}`;
return fetch(`api/billing.php?action=get_bill_details&${param}`)
.then(r => r.json());
}
function showCheckoutModal(visitId) {
fetchBillDetails(visitId, 'visit')
.then(data => {
if (!data.success) { alert(data.error); return; }
updateCheckoutModalUI(data);
new bootstrap.Modal(document.getElementById('checkoutModal')).show();
});
}
function updateCheckoutModalUI(data) {
currentBillId = data.bill.id;
document.getElementById('checkout_bill_id').value = currentBillId;
document.getElementById('checkout_patient_name').innerText = data.visit.patient_name;
document.getElementById('checkout_insurance_name').innerText = data.insurance_name || 'None';
// Auto-set generic insurance percent if insured (e.g., 80% coverage)
// Only set if value is 0 (initial load) to avoid overwriting user input on refresh
if (data.has_insurance && data.bill.status === 'Pending' && parseFloat(data.bill.insurance_covered) === 0) {
// Do not force overwrite if user might have changed it, but for now kept simple
// document.getElementById('checkout_insurance_percent').value = 0;
}
renderBillItems(data.items);
}
function renderBillItems(items) {
const tbody = document.getElementById('checkout_items_table');
tbody.innerHTML = '';
let total = 0;
items.forEach(item => {
total += parseFloat(item.amount);
tbody.innerHTML += `
<tr>
<td>${item.description}</td>
<td class="text-end">${formatCurrency(item.amount)}</td>
<td class="text-center">
<button class="btn btn-sm btn-link text-danger p-0" onclick="removeBillItem(${item.id})"><i class="bi bi-x-lg"></i></button>
</td>
</tr>
`;
});
currentTotal = total;
document.getElementById('checkout_total_amount').innerText = formatCurrency(total);
calculateTotals();
}
function calculateTotals() {
const percent = parseFloat(document.getElementById('checkout_insurance_percent').value) || 0;
const insuranceAmount = (currentTotal * percent) / 100;
const patientAmount = currentTotal - insuranceAmount;
document.getElementById('checkout_insurance_amount').value = insuranceAmount.toFixed(2);
document.getElementById('checkout_patient_amount').value = patientAmount.toFixed(2);
}
function addBillItem() {
const desc = document.getElementById('checkout_custom_item').value;
const price = document.getElementById('checkout_item_price').value;
if (!desc || !price) { alert('Please enter description and price'); return; }
const formData = new FormData();
formData.append('action', 'add_item');
formData.append('bill_id', currentBillId);
formData.append('description', desc);
formData.append('amount', price);
fetch('api/billing.php', { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) {
refreshBillItems();
// Clear inputs
document.getElementById('checkout_custom_item').value = '';
document.getElementById('checkout_item_price').value = '';
$('#checkout_service_select').val('').trigger('change'); // Reset Select2
} else { alert(data.error); }
});
}
function removeBillItem(itemId) {
if (!confirm('Remove item?')) return;
const formData = new FormData();
formData.append('action', 'remove_item');
formData.append('item_id', itemId);
fetch('api/billing.php', { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) refreshBillItems();
else alert(data.error);
});
}
function refreshBillItems() {
fetchBillDetails(currentBillId, 'bill')
.then(data => {
if (data.success) {
updateCheckoutModalUI(data);
} else {
alert(data.error);
}
});
}
function completePayment() {
const formData = new FormData();
formData.append('action', 'complete_payment');
formData.append('bill_id', currentBillId);
formData.append('payment_method', document.getElementById('checkout_payment_method').value);
formData.append('notes', document.getElementById('checkout_notes').value);
const insurance = document.getElementById('checkout_insurance_amount').value;
const patient = document.getElementById('checkout_patient_amount').value;
const updateData = new FormData();
updateData.append('action', 'update_totals');
updateData.append('bill_id', currentBillId);
updateData.append('insurance_covered', insurance);
updateData.append('patient_payable', patient);
fetch('api/billing.php', { method: 'POST', body: updateData })
.then(() => {
return fetch('api/billing.php', { method: 'POST', body: formData });
})
.then(r => r.json())
.then(data => {
if (data.success) {
alert('Payment Recorded!');
window.location.reload();
} else { alert(data.error); }
});
}
function printBill() {
if (currentBillId) {
window.open('print_bill.php?bill_id=' + currentBillId, '_blank');
}
}
</script>