2026-04-13 19:04:12 +05:30

352 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>Doctors</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<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/doctors.css') ?>">
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.7/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.2/css/buttons.bootstrap5.min.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>
<a href="<?= base_url('admin/doctors') ?>" class="ov-nav__link active"><i class="bi bi-person-badge"></i> Doctors</a>
<div class="ov-nav__dropdown">
<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>
<a href="<?= base_url('admin/patients/add') ?>" class="ov-nav__sublink">Add Patient</a>
</div>
</div>
<a href="<?= base_url('admin/appointments') ?>" class="ov-nav__link"><i class="bi bi-calendar2-check"></i> Appointments</a>
<a href="<?= base_url('admin/doctors/add') ?>" class="ov-nav__link"><i class="bi bi-person-plus"></i> Add Doctor</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()" title="Toggle Sidebar"><i class="bi bi-list" id="toggleIcon"></i></button>
<p class="ov-topbar__title mb-0">Doctors</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="ov-panel">
<div class="ov-panel__header">
<h2 class="ov-panel__title">Doctor List</h2>
<div class="d-flex flex-wrap gap-2 align-items-center">
<div class="dropdown">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" onclick="exportTable('csv'); return false;"><i class="bi bi-file-earmark-text me-2"></i> CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportTable('excel'); return false;"><i class="bi bi-file-earmark-excel me-2"></i> Excel</a></li>
<li><a class="dropdown-item" href="#" onclick="exportTable('pdf'); return false;"><i class="bi bi-file-earmark-pdf me-2"></i> PDF</a></li>
</ul>
</div>
</div>
</div>
<div class="p-0">
<table id="doctorsTable" class="table table-hover" style="width:100%">
<thead class="table-light">
<tr>
<th width="60">User ID</th>
<th>Doctor Name</th>
<th>Email</th>
<th>Specialization</th>
<th>Experience</th>
<th>Consultation Fee</th>
<th width="120">Status</th>
<th width="130">Actions</th>
</tr>
</thead>
</table>
</div>
</div>
</main>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.7/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.7/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.bootstrap5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/vfs_fonts.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.print.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
let doctorsTable = null;
const expandedSpecializations = new Set();
function escapeHtml(value) {
return String(value ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function renderSpecializations(data, row) {
if (!data) {
return '<span class="text-muted">N/A</span>';
}
const specs = String(data)
.split(',')
.map(spec => spec.trim())
.filter(Boolean);
let html = specs.slice(0, 2).map(spec =>
`<span class="badge bg-light text-dark me-1">${escapeHtml(spec)}</span>`
).join('');
if (specs.length > 2) {
const extraSpecializations = specs.slice(2);
const extraId = `extra-spec-${escapeHtml(row.user_id ?? '')}`;
const isExpanded = expandedSpecializations.has(extraId);
const hiddenClass = isExpanded ? '' : ' d-none';
const outerToggleHiddenClass = isExpanded ? ' d-none' : '';
const innerToggleHiddenClass = isExpanded ? '' : ' d-none';
const toggleLabel = isExpanded ? 'Show less' : `+${extraSpecializations.length} more`;
html += `<div id="${extraId}" class="extra-specializations mt-2${hiddenClass}">`;
html += extraSpecializations.map(spec =>
`<span class="badge bg-light text-dark me-1 mb-1">${escapeHtml(spec)}</span>`
).join('');
html += `<button type="button" class="badge bg-secondary border-0 specialization-toggle ms-1 mb-1${innerToggleHiddenClass}" data-target="${extraId}" data-show-label="+${extraSpecializations.length} more" data-hide-label="Show less">Show less</button>`;
html += `</div>`;
html += `<button type="button" class="badge bg-secondary border-0 specialization-toggle${outerToggleHiddenClass}" data-target="${extraId}" data-show-label="+${extraSpecializations.length} more" data-hide-label="Show less">${toggleLabel}</button>`;
}
return html;
}
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');
}
$(document).ready(function() {
doctorsTable = $('#doctorsTable').DataTable({
processing: false,
ajax: {
url: "<?= base_url('admin/doctors/data') ?>",
type: 'GET',
dataSrc: 'data'
},
columns: [
{
data: 'formatted_user_id',
defaultContent: 'N/A'
},
{
data: 'name',
render: function (data) {
return `<div class="fw-medium">${escapeHtml(data ?? 'Unknown Doctor')}</div>`;
}
},
{
data: 'email',
render: function (data) {
if (!data) {
return '<span class="text-muted">N/A</span>';
}
const safeEmail = escapeHtml(data);
return `<a href="mailto:${safeEmail}" class="text-decoration-none">${safeEmail}</a>`;
}
},
{
data: 'specialization',
render: function (data, type, row) {
return renderSpecializations(data, row);
}
},
{
data: 'experience',
render: function (data) {
return data ? escapeHtml(data) : '<span class="text-muted">N/A</span>';
}
},
{
data: 'fees',
render: function (data) {
return data && parseFloat(data) > 0
? parseFloat(data).toFixed(2)
: '<span class="text-muted">N/A</span>';
}
},
{
data: 'status',
render: function (data) {
const status = String(data ?? 'active').toLowerCase();
const statusClass = status === 'active' ? 'success' : 'secondary';
const label = status.charAt(0).toUpperCase() + status.slice(1);
return `
<span class="badge bg-${statusClass} rounded-pill">
<i class="bi bi-circle-fill me-1" style="font-size: 0.5rem;"></i>
${escapeHtml(label)}
</span>
`;
}
},
{
data: null,
orderable: false,
searchable: false,
render: function (data, type, row) {
const editUrl = "<?= base_url('admin/doctors/edit') ?>/" + encodeURIComponent(row.edit_token);
const deleteUrl = "<?= base_url('admin/deleteDoctor') ?>/" + encodeURIComponent(row.user_id);
return `
<div class="btn-group" role="group">
<a href="${editUrl}" class="btn btn-outline-primary btn-sm" title="Edit Doctor">
<i class="bi bi-pencil"></i>
</a>
<a href="${deleteUrl}" class="btn btn-outline-danger btn-sm" title="Delete Doctor"
onclick="return confirm('Are you sure you want to delete this doctor? This action cannot be undone.');">
<i class="bi bi-trash"></i>
</a>
</div>
`;
}
}
],
pageLength: 10,
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]],
order: [[0, 'asc']],
dom: '<"row mb-3"<"col-md-6 d-flex align-items-center"l><"col-md-6 d-flex justify-content-end"f>>rtip',
buttons: [
{
extend: 'csvHtml5',
className: 'buttons-csv',
title: 'Doctors',
filename: 'doctors_list_' + new Date().toISOString().split('T')[0],
exportOptions: {
columns: [0, 1, 2, 3, 4, 5, 6]
}
},
{
extend: 'excelHtml5',
className: 'buttons-excel',
title: 'Doctors',
filename: 'doctors_list_' + new Date().toISOString().split('T')[0],
exportOptions: {
columns: [0, 1, 2, 3, 4, 5, 6]
}
},
{
extend: 'pdfHtml5',
className: 'buttons-pdf',
title: 'Doctors List',
filename: 'doctors_list_' + new Date().toISOString().split('T')[0],
orientation: 'landscape',
pageSize: 'A4',
exportOptions: {
columns: [0, 1, 2, 3, 4, 5, 6]
}
}
],
language: {
search: 'Search doctors:',
lengthMenu: 'Show _MENU_ doctors',
info: 'Showing _START_ to _END_ of _TOTAL_ doctors',
paginate: {
first: 'First',
last: 'Last',
next: 'Next',
previous: 'Previous'
},
emptyTable: 'No doctors found',
zeroRecords: 'No matching doctors found'
}
});
setInterval(function () {
if (doctorsTable) {
doctorsTable.ajax.reload(null, false);
}
}, 5000);
});
function exportTable(format) {
if (!doctorsTable) {
return;
}
if (format === 'csv') {
doctorsTable.button('.buttons-csv').trigger();
} else if (format === 'excel') {
doctorsTable.button('.buttons-excel').trigger();
} else if (format === 'pdf') {
doctorsTable.button('.buttons-pdf').trigger();
}
}
$(document).on('click', '.specialization-toggle', function () {
const targetId = $(this).data('target');
const showLabel = $(this).data('show-label');
const hideLabel = $(this).data('hide-label');
const $target = $('#' + targetId);
const $allToggles = $('.specialization-toggle[data-target="' + targetId + '"]');
$target.toggleClass('d-none');
if ($target.hasClass('d-none')) {
expandedSpecializations.delete(targetId);
} else {
expandedSpecializations.add(targetId);
}
$allToggles.each(function () {
const isInsideExpandedArea = $(this).closest('.extra-specializations').length > 0;
$(this).toggleClass('d-none', $target.hasClass('d-none') ? isInsideExpandedArea : !isInsideExpandedArea);
});
$allToggles.text($target.hasClass('d-none') ? showLabel : hideLabel);
});
</script>
</body>
</html>