updating HR and integrate it to modules

This commit is contained in:
Flatlogic Bot 2026-03-22 10:04:41 +00:00
parent f62878214d
commit 6933c13c3e
31 changed files with 431 additions and 1466 deletions

View File

@ -36,12 +36,12 @@ if ($method === 'GET') {
a.id, a.start_time as start, a.end_time as end, a.reason, a.status,
a.patient_id, a.doctor_id, a.nurse_id, a.visit_type, a.address,
p.name as patient_name,
d.name_$lang as doctor_name,
n.name_$lang as nurse_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 doctors d ON a.doctor_id = d.id
LEFT JOIN nurses n ON a.nurse_id = n.id
LEFT JOIN employees doc ON a.doctor_id = doc.id
LEFT JOIN employees nur ON a.nurse_id = nur.id
WHERE 1=1";
$params = [];
@ -148,19 +148,27 @@ if ($method === 'GET') {
];
}
// Fetch Doctor Holidays
$docHolidayQuery = "SELECT dh.*, d.name_$lang as doctor_name FROM doctor_holidays dh JOIN doctors d ON dh.doctor_id = d.id WHERE 1=1";
// Fetch Doctor Holidays (from Leave Requests)
// Updated to join employees instead of doctors
$docHolidayQuery = "
SELECT lr.id, lr.start_date, lr.end_date, lr.reason as note,
lr.employee_id as doctor_id,
e.name_$lang as doctor_name
FROM leave_requests lr
JOIN employees e ON lr.employee_id = e.id
WHERE lr.status = 'Approved'";
$docHolidayParams = [];
// Date filtering for doctor holidays (ranges)
if ($startStr && $endStr) {
$docHolidayQuery .= " AND dh.start_date <= ? AND dh.end_date >= ?";
$docHolidayQuery .= " AND lr.start_date <= ? AND lr.end_date >= ?";
$docHolidayParams[] = date('Y-m-d', strtotime($endStr));
$docHolidayParams[] = date('Y-m-d', strtotime($startStr));
}
if ($doctor_id) {
$docHolidayQuery .= " AND dh.doctor_id = ?";
$docHolidayQuery .= " AND lr.employee_id = ?";
$docHolidayParams[] = $doctor_id;
}
@ -223,36 +231,16 @@ if ($method === 'GET') {
];
}
// Fetch Doctor Business Hours
if ($doctor_id) {
$scheduleStmt = $db->prepare("SELECT day_of_week as day, start_time as start, end_time as end FROM doctor_schedules WHERE doctor_id = ?");
$scheduleStmt->execute([$doctor_id]);
$schedules = $scheduleStmt->fetchAll(PDO::FETCH_ASSOC);
$bhMap = [];
foreach ($schedules as $s) {
$key = $s['start'] . '-' . $s['end'];
if (!isset($bhMap[$key])) {
$bhMap[$key] = [
'daysOfWeek' => [],
'startTime' => $s['start'],
'endTime' => $s['end']
];
}
$bhMap[$key]['daysOfWeek'][] = (int)$s['day'];
}
$businessHours = array_values($bhMap);
} else {
$st = $s['working_hours_start'] ?? '08:00';
$et = $s['working_hours_end'] ?? '17:00';
$businessHours = [
[
'daysOfWeek' => [0, 1, 2, 3, 4, 5, 6],
'startTime' => $st,
'endTime' => $et
]
];
}
// Set Business Hours (Global Default since individual schedules are removed)
$st = $s['working_hours_start'] ?? '08:00';
$et = $s['working_hours_end'] ?? '17:00';
$businessHours = [
[
'daysOfWeek' => [0, 1, 2, 3, 4, 5, 6],
'startTime' => $st,
'endTime' => $et
]
];
echo json_encode([
'events' => $events,
@ -269,7 +257,8 @@ function checkDoctorHoliday($db, $doctor_id, $start_time) {
if (!$doctor_id || !$start_time) return false;
$date = date('Y-m-d', strtotime($start_time));
try {
$stmt = $db->prepare("SELECT COUNT(*) FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
// Query leave_requests directly using employee_id (which is $doctor_id)
$stmt = $db->prepare("SELECT COUNT(*) FROM leave_requests WHERE employee_id = ? AND status = 'Approved' AND ? BETWEEN start_date AND end_date");
$stmt->execute([$doctor_id, $date]);
return $stmt->fetchColumn() > 0;
} catch (PDOException $e) {

View File

@ -2,52 +2,31 @@
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../helpers.php';
// Prevent caching
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
header('Content-Type: application/json');
$db = db();
$method = $_SERVER['REQUEST_METHOD'];
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$doctor_id = $_GET['doctor_id'] ?? null;
if ($method === 'GET') {
$doctor_id = $_GET['doctor_id'] ?? null;
if (!$doctor_id) {
echo json_encode(['success' => false, 'error' => 'Missing doctor_id']);
exit;
}
if (!$doctor_id) {
echo json_encode(['success' => false, 'error' => 'Missing doctor_id']);
exit;
}
$stmt = $db->prepare("SELECT * FROM doctor_holidays WHERE doctor_id = ? ORDER BY start_date DESC");
try {
// $doctor_id is expected to be employee_id now
$stmt = $db->prepare("
SELECT start_date, end_date, reason
FROM leave_requests
WHERE employee_id = ? AND status = 'Approved' AND end_date >= CURDATE()
");
$stmt->execute([$doctor_id]);
$holidays = $stmt->fetchAll();
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'holidays' => $holidays]);
exit;
}
if ($method === 'POST') {
$action = $input['action'] ?? '';
if ($action === 'create') {
$doctor_id = $input['doctor_id'] ?? null;
$start_date = $input['start_date'] ?? null;
$end_date = $input['end_date'] ?? null;
$note = $input['note'] ?? '';
if ($doctor_id && $start_date && $end_date) {
$stmt = $db->prepare("INSERT INTO doctor_holidays (doctor_id, start_date, end_date, note) VALUES (?, ?, ?, ?)");
$stmt->execute([$doctor_id, $start_date, $end_date, $note]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'Missing fields']);
}
} elseif ($action === 'delete') {
$id = $input['id'] ?? null;
if ($id) {
$stmt = $db->prepare("DELETE FROM doctor_holidays WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'Missing ID']);
}
}
exit;
} catch (PDOException $e) {
echo json_encode(['success' => false, 'error' => 'DB Error: ' . $e->getMessage()]);
}

View File

@ -85,7 +85,7 @@ try {
FROM patient_queue q
JOIN patients p ON q.patient_id = p.id
JOIN departments dept ON q.department_id = dept.id
LEFT JOIN doctors d ON q.doctor_id = d.id
LEFT JOIN employees d ON q.doctor_id = d.id
$where
ORDER BY
CASE WHEN q.status = 'serving' THEN 1 WHEN q.status = 'waiting' THEN 2 ELSE 3 END,

View File

@ -0,0 +1,2 @@
ALTER TABLE nurses ADD COLUMN IF NOT EXISTS employee_id INT;
ALTER TABLE nurses ADD CONSTRAINT fk_nurse_employee FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE SET NULL;

View File

@ -0,0 +1,59 @@
-- Migration to merge Doctors and Nurses into HR (Employees)
-- Step 1: Add new columns to hold Employee IDs
ALTER TABLE visits ADD COLUMN IF NOT EXISTS doctor_employee_id INT NULL;
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS doctor_employee_id INT NULL;
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS nurse_employee_id INT NULL;
-- Step 2: Migrate data (if doctors/nurses have employee_id set)
-- Update Visits
UPDATE visits v
JOIN doctors d ON v.doctor_id = d.id
SET v.doctor_employee_id = d.employee_id
WHERE d.employee_id IS NOT NULL;
-- Update Appointments (Doctor)
UPDATE appointments a
JOIN doctors d ON a.doctor_id = d.id
SET a.doctor_employee_id = d.employee_id
WHERE d.employee_id IS NOT NULL;
-- Update Appointments (Nurse)
UPDATE appointments a
JOIN nurses n ON a.nurse_id = n.id
SET a.nurse_employee_id = n.employee_id
WHERE n.employee_id IS NOT NULL;
-- Step 3: Drop old Foreign Keys (Constraint names might vary, so we try standard names or rely on DROP COLUMN to drop FKs in some DBs, but explicitly dropping FK is safer)
-- Finding constraint names is hard in SQL script without dynamic SQL.
-- However, in MariaDB/MySQL, dropping the column usually drops the FK.
-- But to be safe, we will try to drop the standard named constraints if known, or just proceed with DROP COLUMN which should work if no other constraints block it.
ALTER TABLE visits DROP FOREIGN KEY IF EXISTS visits_ibfk_2; -- doctor_id
ALTER TABLE appointments DROP FOREIGN KEY IF EXISTS appointments_ibfk_2; -- doctor_id
ALTER TABLE appointments DROP FOREIGN KEY IF EXISTS appointments_ibfk_3; -- nurse_id
-- Also drop keys/indexes if they exist separate from FK
ALTER TABLE visits DROP KEY IF EXISTS doctor_id;
ALTER TABLE appointments DROP KEY IF EXISTS doctor_id;
ALTER TABLE appointments DROP KEY IF EXISTS nurse_id;
-- Step 4: Drop old columns
ALTER TABLE visits DROP COLUMN doctor_id;
ALTER TABLE appointments DROP COLUMN doctor_id;
ALTER TABLE appointments DROP COLUMN nurse_id;
-- Step 5: Rename new columns to match standard naming (or keep them and add FK)
-- Let's rename them back to doctor_id and nurse_id but now they point to employees
ALTER TABLE visits CHANGE COLUMN doctor_employee_id doctor_id INT NULL;
ALTER TABLE appointments CHANGE COLUMN doctor_employee_id doctor_id INT NULL;
ALTER TABLE appointments CHANGE COLUMN nurse_employee_id nurse_id INT NULL;
-- Step 6: Add new Foreign Keys to employees
ALTER TABLE visits ADD CONSTRAINT fk_visit_doctor_employee FOREIGN KEY (doctor_id) REFERENCES employees(id) ON DELETE SET NULL;
ALTER TABLE appointments ADD CONSTRAINT fk_appt_doctor_employee FOREIGN KEY (doctor_id) REFERENCES employees(id) ON DELETE SET NULL;
ALTER TABLE appointments ADD CONSTRAINT fk_appt_nurse_employee FOREIGN KEY (nurse_id) REFERENCES employees(id) ON DELETE SET NULL;
-- Step 7: Drop obsolete tables
DROP TABLE IF EXISTS doctor_holidays; -- If exists
DROP TABLE IF EXISTS doctors;
DROP TABLE IF EXISTS nurses;

View File

@ -0,0 +1,25 @@
-- Final cleanup for Doctors/Nurses migration
-- Fix Patient Queue Doctor ID (References doctors)
ALTER TABLE patient_queue ADD COLUMN IF NOT EXISTS doctor_employee_id INT NULL;
-- Migrate data
UPDATE patient_queue q
JOIN doctors d ON q.doctor_id = d.id
SET q.doctor_employee_id = d.employee_id
WHERE d.employee_id IS NOT NULL;
-- Drop old FK
ALTER TABLE patient_queue DROP FOREIGN KEY IF EXISTS patient_queue_ibfk_3;
-- Drop old column
ALTER TABLE patient_queue DROP COLUMN doctor_id;
-- Rename new column
ALTER TABLE patient_queue CHANGE COLUMN doctor_employee_id doctor_id INT NULL;
-- Add new FK to employees
ALTER TABLE patient_queue ADD CONSTRAINT fk_queue_doctor_employee FOREIGN KEY (doctor_id) REFERENCES employees(id) ON DELETE SET NULL;
-- Now drop doctors table
DROP TABLE IF EXISTS doctors;

View File

@ -0,0 +1,56 @@
-- Fix Migration: Merge Doctors and Nurses into HR (Employees) - Cleanup
-- 1. Drop dependent tables that reference doctors
DROP TABLE IF EXISTS doctor_schedules;
-- 2. Fix Visits Nurse ID
-- Add temp column if it doesn't exist (it wasn't added in previous migration)
ALTER TABLE visits ADD COLUMN IF NOT EXISTS nurse_employee_id INT NULL;
-- Migrate data from nurses table to visits.nurse_employee_id
UPDATE visits v
JOIN nurses n ON v.nurse_id = n.id
SET v.nurse_employee_id = n.employee_id
WHERE n.employee_id IS NOT NULL;
-- Drop old FK on visits.nurse_id
ALTER TABLE visits DROP FOREIGN KEY IF EXISTS fk_visit_nurse;
-- Drop old column visits.nurse_id
-- We use a check to avoid error if it was already dropped (though unlikely)
ALTER TABLE visits DROP COLUMN nurse_id;
-- Rename new column to nurse_id
ALTER TABLE visits CHANGE COLUMN nurse_employee_id nurse_id INT NULL;
-- Add new FK to employees
ALTER TABLE visits ADD CONSTRAINT fk_visit_nurse_employee FOREIGN KEY (nurse_id) REFERENCES employees(id) ON DELETE SET NULL;
-- 3. Fix Appointments Nurse ID
-- Ensure nurse_employee_id exists (might have been created in previous migration)
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS nurse_employee_id INT NULL;
-- Migrate data again just in case
UPDATE appointments a
JOIN nurses n ON a.nurse_id = n.id
SET a.nurse_employee_id = n.employee_id
WHERE n.employee_id IS NOT NULL;
-- Drop old FK on appointments.nurse_id
ALTER TABLE appointments DROP FOREIGN KEY IF EXISTS fk_appointment_nurse;
-- Drop old column appointments.nurse_id
ALTER TABLE appointments DROP COLUMN nurse_id;
-- Rename new column to nurse_id
ALTER TABLE appointments CHANGE COLUMN nurse_employee_id nurse_id INT NULL;
-- Add new FK to employees
ALTER TABLE appointments ADD CONSTRAINT fk_appt_nurse_employee FOREIGN KEY (nurse_id) REFERENCES employees(id) ON DELETE SET NULL;
-- 4. Drop obsolete tables
-- Now that FKs are gone, this should succeed.
DROP TABLE IF EXISTS doctors;
DROP TABLE IF EXISTS nurses;

View File

@ -1,4 +0,0 @@
<?php
require_once 'includes/layout/header.php';
require_once 'includes/pages/doctor_holidays.php';
require_once 'includes/layout/footer.php';

View File

@ -1,25 +0,0 @@
<?php
$section = 'doctors';
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$db = db();
$lang = $_SESSION['lang'];
require_once __DIR__ . '/includes/actions.php';
require_once __DIR__ . '/includes/common_data.php';
$is_ajax = isset($_GET['ajax_search']) || isset($_POST['ajax_search']);
if (!$is_ajax) {
require_once __DIR__ . '/includes/layout/header.php';
}
require_once __DIR__ . '/includes/pages/doctors.php';
if (!$is_ajax) {
require_once __DIR__ . '/includes/layout/footer.php';
}

View File

@ -1 +1,32 @@
<?php require_once 'includes/auth.php'; require_once 'includes/header.php'; require_once 'includes/pages/hr_attendance.php'; require_once 'includes/footer.php'; ?>
<?php
// Enable detailed error reporting for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$section = 'hr_attendance';
require_once __DIR__ . '/db/config.php';
// Try to connect to DB first
try {
$db = db();
} catch (PDOException $e) {
die("Database Connection Error: " . $e->getMessage());
} catch (Exception $e) {
die("General Error: " . $e->getMessage());
}
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$lang = $_SESSION['lang'];
require_once __DIR__ . '/includes/actions.php';
require_once __DIR__ . '/includes/common_data.php';
require_once __DIR__ . '/includes/layout/header.php';
require_once __DIR__ . '/includes/pages/hr_attendance.php';
require_once __DIR__ . '/includes/layout/footer.php';

View File

@ -1,8 +1,32 @@
<?php
require_once 'includes/auth.php';
require_once 'includes/header.php';
// Enable detailed error reporting for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Logic is in includes/pages/hr_dashboard.php
require_once 'includes/pages/hr_dashboard.php';
$section = 'hr_dashboard';
require_once __DIR__ . '/db/config.php';
require_once 'includes/footer.php';
// Try to connect to DB first
try {
$db = db();
} catch (PDOException $e) {
die("Database Connection Error: " . $e->getMessage());
} catch (Exception $e) {
die("General Error: " . $e->getMessage());
}
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$lang = $_SESSION['lang'];
require_once __DIR__ . '/includes/actions.php';
require_once __DIR__ . '/includes/common_data.php';
require_once __DIR__ . '/includes/layout/header.php';
require_once __DIR__ . '/includes/pages/hr_dashboard.php';
require_once __DIR__ . '/includes/layout/footer.php';

View File

@ -1 +1,32 @@
<?php require_once 'includes/auth.php'; require_once 'includes/header.php'; require_once 'includes/pages/hr_leaves.php'; require_once 'includes/footer.php'; ?>
<?php
// Enable detailed error reporting for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$section = 'hr_leaves';
require_once __DIR__ . '/db/config.php';
// Try to connect to DB first
try {
$db = db();
} catch (PDOException $e) {
die("Database Connection Error: " . $e->getMessage());
} catch (Exception $e) {
die("General Error: " . $e->getMessage());
}
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$lang = $_SESSION['lang'];
require_once __DIR__ . '/includes/actions.php';
require_once __DIR__ . '/includes/common_data.php';
require_once __DIR__ . '/includes/layout/header.php';
require_once __DIR__ . '/includes/pages/hr_leaves.php';
require_once __DIR__ . '/includes/layout/footer.php';

View File

@ -146,80 +146,6 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$_SESSION['flash_message'] = __('delete') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'add_doctor') {
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$tel = $_POST['tel'] ?? '';
$email = $_POST['email'] ?? '';
$spec_en = $_POST['specialization_en'] ?? '';
$spec_ar = $_POST['specialization_ar'] ?? '';
$dept_id = $_POST['department_id'] ?: null;
if ($name_en && $name_ar) {
$stmt = $db->prepare("INSERT INTO doctors (name_en, name_ar, tel, email, specialization_en, specialization_ar, department_id) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $tel, $email, $spec_en, $spec_ar, $dept_id]);
$_SESSION['flash_message'] = __('add_doctor') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'edit_doctor') {
$id = $_POST['id'] ?? '';
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$tel = $_POST['tel'] ?? '';
$email = $_POST['email'] ?? '';
$spec_en = $_POST['specialization_en'] ?? '';
$spec_ar = $_POST['specialization_ar'] ?? '';
$dept_id = $_POST['department_id'] ?: null;
if ($id && $name_en && $name_ar) {
$stmt = $db->prepare("UPDATE doctors SET name_en = ?, name_ar = ?, tel = ?, email = ?, specialization_en = ?, specialization_ar = ?, department_id = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $tel, $email, $spec_en, $spec_ar, $dept_id, $id]);
$_SESSION['flash_message'] = __('edit_doctor') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'delete_doctor') {
$id = $_POST['id'] ?? '';
if ($id) {
$stmt = $db->prepare("DELETE FROM doctors WHERE id = ?");
$stmt->execute([$id]);
$_SESSION['flash_message'] = __('delete') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'add_nurse') {
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$tel = $_POST['tel'] ?? '';
$email = $_POST['email'] ?? '';
$dept_id = $_POST['department_id'] ?: null;
if ($name_en && $name_ar) {
$stmt = $db->prepare("INSERT INTO nurses (name_en, name_ar, tel, email, department_id) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $tel, $email, $dept_id]);
$_SESSION['flash_message'] = __('add_nurse') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'edit_nurse') {
$id = $_POST['id'] ?? '';
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$tel = $_POST['tel'] ?? '';
$email = $_POST['email'] ?? '';
$dept_id = $_POST['department_id'] ?: null;
if ($id && $name_en && $name_ar) {
$stmt = $db->prepare("UPDATE nurses SET name_en = ?, name_ar = ?, tel = ?, email = ?, department_id = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $tel, $email, $dept_id, $id]);
$_SESSION['flash_message'] = __('edit_nurse') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'delete_nurse') {
$id = $_POST['id'] ?? '';
if ($id) {
$stmt = $db->prepare("DELETE FROM nurses WHERE id = ?");
$stmt->execute([$id]);
$_SESSION['flash_message'] = __('delete') . ' ' . __('successfully');
$redirect = true;
}
} elseif ($_POST['action'] === 'add_department') {
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
@ -322,7 +248,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
// Token Generation (Only for Doctor visits in Clinic usually)
if (isset($_POST['generate_token']) && $_POST['generate_token'] == '1' && $doctor_id) {
$stmtDoc = $db->prepare("SELECT department_id FROM doctors WHERE id = ?");
// Updated to query employees table
$stmtDoc = $db->prepare("SELECT department_id FROM employees WHERE id = ?");
$stmtDoc->execute([$doctor_id]);
$docData = $stmtDoc->fetch();
$dept_id = $docData ? $docData['department_id'] : null;

View File

@ -83,4 +83,8 @@ function require_permission($permission) {
http_response_code(403);
die("Access Denied: You do not have the required permission: " . htmlspecialchars($permission));
}
}
}
function is_admin() {
return has_role('admin');
}

View File

@ -1,8 +1,20 @@
<?php
// Common data for selects
$all_doctors = $db->query("SELECT id, name_$lang as name FROM doctors")->fetchAll();
$all_patients = $db->query("SELECT id, name, phone, civil_id, dob, gender FROM patients")->fetchAll();
$all_nurses = $db->query("SELECT id, name_$lang as name FROM nurses")->fetchAll();
$all_doctors = $db->query("
SELECT e.id, e.name_$lang as name
FROM employees e
JOIN positions p ON e.position_id = p.id
WHERE UPPER(p.name_en) = 'DOCTOR'
")->fetchAll();
$all_nurses = $db->query("
SELECT e.id, e.name_$lang as name
FROM employees e
JOIN positions p ON e.position_id = p.id
WHERE UPPER(p.name_en) = 'NURSE'
")->fetchAll();
$all_patients = $db->query("SELECT id, name, phone, civil_id, dob, gender, address FROM patients")->fetchAll();
$all_departments = $db->query("SELECT id, name_$lang as name FROM departments")->fetchAll();
$all_employees = $db->query("SELECT id, name_$lang as name FROM employees")->fetchAll();
$all_positions = $db->query("SELECT id, name_$lang as name FROM positions")->fetchAll();
@ -19,12 +31,14 @@ $all_xrays = $db->query("SELECT id, name_$lang as name, price FROM xray_tests")-
$all_drugs = $db->query("SELECT id, name_$lang as name, default_dosage, default_instructions, price FROM drugs")->fetchAll();
$scheduled_appointments = $db->query("
SELECT a.id, p.name as patient_name, a.start_time, a.patient_id, a.doctor_id
SELECT a.id, p.name as patient_name, a.start_time, a.patient_id, a.doctor_id,
e.name_$lang as doctor_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
LEFT JOIN employees e ON a.doctor_id = e.id
WHERE a.status = 'Scheduled'
ORDER BY a.start_time ASC")->fetchAll();
$all_countries = require __DIR__ . "/countries.php";
$all_cities = $db->query("SELECT id, name_$lang as name FROM cities")->fetchAll();
$all_cities = $db->query("SELECT id, name_$lang as name FROM cities")->fetchAll();

View File

@ -327,6 +327,15 @@
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?php echo __('linked_employee'); ?> (HR/Leaves)</label>
<select name="employee_id" class="form-select select2-modal">
<option value=""><?php echo __('none'); ?></option>
<?php foreach ($all_employees as $emp): ?>
<option value="<?php echo $emp['id']; ?>"><?php echo htmlspecialchars($emp['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
@ -384,6 +393,15 @@
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?php echo __('linked_employee'); ?> (HR/Leaves)</label>
<select name="employee_id" id="edit_doctor_employee_id" class="form-select select2-modal">
<option value=""><?php echo __('none'); ?></option>
<?php foreach ($all_employees as $emp): ?>
<option value="<?php echo $emp['id']; ?>"><?php echo htmlspecialchars($emp['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
@ -1593,6 +1611,7 @@ function showEditDoctorModal(data) {
document.getElementById('edit_doctor_spec_en').value = data.specialization_en || '';
document.getElementById('edit_doctor_spec_ar').value = data.specialization_ar || '';
document.getElementById('edit_doctor_dept_id').value = data.department_id || '';
$('#edit_doctor_employee_id').val(data.employee_id).trigger('change');
var modal = new bootstrap.Modal(document.getElementById('editDoctorModal'));
modal.show();
@ -1752,4 +1771,3 @@ $(document).ready(function() {
});
</script>
</body>
</html>

View File

@ -217,12 +217,6 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp
<?php if (has_permission('insurance')): ?>
<a href="insurance.php" class="sidebar-link <?php echo $section === 'insurance' ? 'active' : ''; ?>"><i class="bi bi-shield-check me-2"></i> <?php echo __('insurance'); ?></a>
<?php endif; ?>
<?php if (has_permission('doctors')): ?>
<a href="doctors.php" class="sidebar-link <?php echo $section === 'doctors' ? 'active' : ''; ?>"><i class="bi bi-person-badge me-2"></i> <?php echo __('doctors'); ?></a>
<a href="doctor_holidays.php" class="sidebar-link <?php echo $section === 'doctor_holidays' ? 'active' : ''; ?>"><i class="bi bi-calendar-x me-2"></i> <?php echo __('doctor_holidays'); ?></a>
<a href="nurses.php" class="sidebar-link <?php echo $section === 'nurses' ? 'active' : ''; ?>"><i class="bi bi-person-heart me-2"></i> <?php echo __('nurses'); ?></a>
<?php endif; ?>
<?php if (has_permission('reports')): ?>
<a href="reports.php" class="sidebar-link <?php echo $section === 'reports' ? 'active' : ''; ?>"><i class="bi bi-bar-chart-line me-2"></i> <?php echo __('admin_reports'); ?></a>

View File

@ -70,11 +70,11 @@ $total_labs = $db->query("SELECT COUNT(*) FROM laboratory_inquiries")->fetchColu
// Fetch Running Visits (Not Completed/Cancelled)
$running_visits_sql = "
SELECT v.*, p.name as patient_name, d.name_$lang as doctor_name, n.name_$lang as nurse_name
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 doctors d ON v.doctor_id = d.id
LEFT JOIN nurses n ON v.nurse_id = n.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();
@ -89,11 +89,11 @@ $patients = $db->query($patients_sql)->fetchAll();
// Today's Appointments
$appointments_sql = "
SELECT a.*, p.name as patient_name, d.name_$lang as doctor_name, n.name_$lang as nurse_name
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 doctors d ON a.doctor_id = d.id
LEFT JOIN nurses n ON a.nurse_id = n.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();

View File

@ -1,179 +0,0 @@
<?php
$section = 'doctor_holidays';
$db = db();
$lang = $_SESSION['lang'];
// Handle Form Submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create' || $action === 'update') {
$doctor_id = $_POST['doctor_id'] ?? '';
$start_date = $_POST['start_date'] ?? '';
$end_date = $_POST['end_date'] ?? '';
$note = $_POST['note'] ?? '';
$id = $_POST['id'] ?? '';
if ($doctor_id && $start_date && $end_date) {
if ($action === 'create') {
$stmt = $db->prepare("INSERT INTO doctor_holidays (doctor_id, start_date, end_date, note) VALUES (?, ?, ?, ?)");
$stmt->execute([$doctor_id, $start_date, $end_date, $note]);
$_SESSION['flash_message'] = __('holiday_added_successfully');
} else {
$stmt = $db->prepare("UPDATE doctor_holidays SET doctor_id = ?, start_date = ?, end_date = ?, note = ? WHERE id = ?");
$stmt->execute([$doctor_id, $start_date, $end_date, $note, $id]);
$_SESSION['flash_message'] = __('holiday_updated_successfully');
}
}
} elseif ($action === 'delete') {
$id = $_POST['id'] ?? '';
if ($id) {
$stmt = $db->prepare("DELETE FROM doctor_holidays WHERE id = ?");
$stmt->execute([$id]);
$_SESSION['flash_message'] = __('holiday_deleted_successfully');
}
}
// Redirect to avoid resubmission
echo "<script>window.location.href='doctor_holidays.php';</script>";
exit;
}
// Fetch Doctors for dropdown
$doctors = $db->query("SELECT id, name_$lang as name FROM doctors ORDER BY name_$lang ASC")->fetchAll(PDO::FETCH_ASSOC);
// Fetch Holidays
$holidays = $db->query("SELECT dh.*, d.name_$lang as doctor_name FROM doctor_holidays dh JOIN doctors d ON dh.doctor_id = d.id ORDER BY dh.start_date DESC")->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold text-secondary"><?php echo __('doctor_holidays'); ?></h3>
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#holidayModal" onclick="showCreateModal()">
<i class="bi bi-plus-lg me-1"></i> <?php echo __('add_holiday'); ?>
</button>
</div>
<div class="card shadow-sm border-0">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th><?php echo __('doctor'); ?></th>
<th><?php echo __('start_date'); ?></th>
<th><?php echo __('end_date'); ?></th>
<th><?php echo __('note'); ?></th>
<th class="text-end"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($holidays)): ?>
<tr>
<td colspan="5" class="text-center py-4 text-muted">
<i class="bi bi-calendar-x display-6 d-block mb-2"></i>
<?php echo __('no_holidays_found'); ?>
</td>
</tr>
<?php else: ?>
<?php foreach ($holidays as $h): ?>
<tr>
<td><?php echo htmlspecialchars($h['doctor_name']); ?></td>
<td><?php echo htmlspecialchars($h['start_date']); ?></td>
<td><?php echo htmlspecialchars($h['end_date']); ?></td>
<td><?php echo htmlspecialchars($h['note']); ?></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-primary me-1" onclick="showEditModal(<?php echo htmlspecialchars(json_encode($h)); ?>)">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteHoliday(<?php echo $h['id']; ?>)">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="holidayModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<form method="POST" class="modal-content border-0 shadow">
<input type="hidden" name="action" id="modalAction" value="create">
<input type="hidden" name="id" id="holidayId">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold text-secondary" id="modalTitle"><?php echo __('add_holiday'); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pt-3">
<div class="mb-3">
<label class="form-label small text-muted"><?php echo __('doctor'); ?></label>
<select name="doctor_id" id="doctorId" class="form-select" required>
<option value=""><?php echo __('select') . ' ' . __('doctor'); ?></option>
<?php foreach ($doctors as $d): ?>
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['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 __('start_date'); ?></label>
<input type="date" name="start_date" id="startDate" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label small text-muted"><?php echo __('end_date'); ?></label>
<input type="date" name="end_date" id="endDate" class="form-control" required>
</div>
</div>
<div class="mb-3">
<label class="form-label small text-muted"><?php echo __('note'); ?></label>
<textarea name="note" id="note" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer border-0 bg-light rounded-bottom">
<button type="button" class="btn btn-secondary shadow-sm me-2" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="submit" class="btn btn-primary shadow-sm px-4"><i class="bi bi-check2-circle me-1"></i> <?php echo __('save'); ?></button>
</div>
</form>
</div>
</div>
<!-- Delete Form -->
<form id="deleteForm" method="POST" style="display:none;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" id="deleteId">
</form>
<script>
function showCreateModal() {
document.getElementById('modalTitle').innerText = '<?php echo __('add_holiday'); ?>';
document.getElementById('modalAction').value = 'create';
document.getElementById('holidayId').value = '';
document.getElementById('doctorId').value = '';
document.getElementById('startDate').value = '';
document.getElementById('endDate').value = '';
document.getElementById('note').value = '';
}
function showEditModal(data) {
document.getElementById('modalTitle').innerText = '<?php echo __('edit_holiday'); ?>';
document.getElementById('modalAction').value = 'update';
document.getElementById('holidayId').value = data.id;
document.getElementById('doctorId').value = data.doctor_id;
document.getElementById('startDate').value = data.start_date;
document.getElementById('endDate').value = data.end_date;
document.getElementById('note').value = data.note;
var modal = new bootstrap.Modal(document.getElementById('holidayModal'));
modal.show();
}
function deleteHoliday(id) {
if (confirm('<?php echo __('are_you_sure_delete_holiday'); ?>')) {
document.getElementById('deleteId').value = id;
document.getElementById('deleteForm').submit();
}
}
</script>

View File

@ -1,521 +0,0 @@
<?php
$search_name = $_GET['name'] ?? '';
$search_dept = $_GET['department_id'] ?? '';
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$where = "WHERE 1=1";
$params = [];
if ($search_name) {
$where .= " AND (d.name_en LIKE ? OR d.name_ar LIKE ?)";
$params[] = "%$search_name%";
$params[] = "%$search_name%";
}
if ($search_dept) {
$where .= " AND d.department_id = ?";
$params[] = $search_dept;
}
// Count Total
$countQuery = "SELECT COUNT(*) FROM doctors d $where";
$stmt = $db->prepare($countQuery);
$stmt->execute($params);
$totalDoctors = $stmt->fetchColumn();
$totalPages = ceil($totalDoctors / $limit);
// Fetch Data
$query = "
SELECT d.*, dept.name_$lang as department_name
FROM doctors d
LEFT JOIN departments dept ON d.department_id = dept.id
$where
ORDER BY d.id DESC
LIMIT $limit OFFSET $offset";
$stmt = $db->prepare($query);
$stmt->execute($params);
$doctors = $stmt->fetchAll();
// --- AJAX HANDLER ---
if (isset($_GET['ajax_search'])) {
ob_start();
if (empty($doctors)):
?>
<tr>
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-person-badge display-4 d-block mb-3"></i>
<?php echo __('no_doctors_found'); ?>
</td>
</tr>
<?php else: ?>
<?php foreach ($doctors as $d): ?>
<tr>
<td class="px-4">
<div class="fw-semibold text-dark"><?php echo htmlspecialchars($d['name_'.$lang]); ?></div>
<small class="text-muted"><?php echo htmlspecialchars($d['name_'.($lang == 'en' ? 'ar' : 'en')]); ?></small>
</td>
<td><?php echo htmlspecialchars($d['specialization_'.$lang]); ?></td>
<td>
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-25 px-2 py-1">
<?php echo htmlspecialchars($d['department_name'] ?: '-'); ?>
</span>
</td>
<td>
<div class="small text-secondary"><i class="bi bi-telephone me-1"></i> <?php echo htmlspecialchars($d['tel'] ?: '-'); ?></div>
<div class="small text-secondary"><i class="bi bi-envelope me-1"></i> <?php echo htmlspecialchars($d['email'] ?: '-'); ?></div>
</td>
<td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end"
onclick="showEditDoctorModal(<?php echo htmlspecialchars(json_encode($d, JSON_UNESCAPED_UNICODE)); ?>)"
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
<button class="btn btn-link text-warning py-1 px-2 border-end"
onclick="showDoctorHolidays(<?php echo $d['id']; ?>, '<?php echo htmlspecialchars($d['name_'.$lang]); ?>')"
data-bs-toggle="tooltip" title="Holidays">
<i class="bi bi-calendar-x"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
<i class="bi bi-trash3"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif;
$table_html = ob_get_clean();
ob_start();
if ($totalPages > 1): ?>
<div class="d-flex justify-content-between align-items-center p-3 border-top">
<div class="text-muted small">
<?php echo __('showing'); ?> <?php echo $offset + 1; ?> - <?php echo min($offset + $limit, $totalDoctors); ?> <?php echo __('of'); ?> <?php echo $totalDoctors; ?>
</div>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page - 1; ?>" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<?php
$range = 2;
$pages_to_show = [];
$pages_to_show[] = 1;
if ($totalPages > 1) { $pages_to_show[] = $totalPages; }
for ($i = $page - $range; $i <= $page + $range; $i++) {
if ($i > 1 && $i < $totalPages) { $pages_to_show[] = $i; }
}
$pages_to_show = array_unique($pages_to_show);
sort($pages_to_show);
$prev_page = 0;
foreach ($pages_to_show as $p):
if ($prev_page > 0 && $p > $prev_page + 1): ?>
<li class="page-item disabled"><span class="page-link">...</span></li>
<?php endif; ?>
<li class="page-item <?php echo $page == $p ? 'active' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $p; ?>">
<?php echo $p; ?>
</a>
</li>
<?php
$prev_page = $p;
endforeach;
?>
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page + 1; ?>" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
<?php endif;
$pagination_html = ob_get_clean();
header('Content-Type: application/json');
echo json_encode(['html' => $table_html, 'pagination' => $pagination_html]);
exit;
}
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold text-secondary"><?php echo __('doctors'); ?></h3>
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addDoctorModal">
<i class="bi bi-person-plus-fill me-1"></i> <?php echo __('add_doctor'); ?>
</button>
</div>
<!-- Search Bar -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<form id="doctorsSearchForm" class="row g-3" onsubmit="return false;">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text bg-light border-end-0 text-muted"><i class="bi bi-search"></i></span>
<input type="text" name="name" id="doctorSearchName" class="form-control bg-light border-start-0" placeholder="<?php echo __('name'); ?>" value="<?php echo htmlspecialchars($search_name); ?>">
</div>
</div>
<div class="col-md-4">
<select name="department_id" id="doctorSearchDept" class="form-select bg-light">
<option value=""><?php echo __('department'); ?> (<?php echo __('all'); ?>)</option>
<?php foreach ($all_departments as $dept): ?>
<option value="<?php echo $dept['id']; ?>" <?php echo $search_dept == $dept['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($dept['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-secondary w-100" onclick="fetchDoctors(1)"><?php echo __('search'); ?></button>
</div>
</form>
</div>
</div>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light text-secondary">
<tr>
<th class="px-4 py-3"><?php echo __('name'); ?></th>
<th class="py-3"><?php echo __('specialization'); ?></th>
<th class="py-3"><?php echo __('department'); ?></th>
<th class="py-3"><?php echo __('contact'); ?></th>
<th class="py-3 text-end px-4"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody id="doctorsTableBody">
<?php if (empty($doctors)): ?>
<tr>
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-person-badge display-4 d-block mb-3"></i>
<?php echo __('no_doctors_found'); ?>
</td>
</tr>
<?php else: ?>
<?php foreach ($doctors as $d): ?>
<tr>
<td class="px-4">
<div class="fw-semibold text-dark"><?php echo htmlspecialchars($d['name_'.$lang]); ?></div>
<small class="text-muted"><?php echo htmlspecialchars($d['name_'.($lang == 'en' ? 'ar' : 'en')]); ?></small>
</td>
<td><?php echo htmlspecialchars($d['specialization_'.$lang]); ?></td>
<td>
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-25 px-2 py-1">
<?php echo htmlspecialchars($d['department_name'] ?: '-'); ?>
</span>
</td>
<td>
<div class="small text-secondary"><i class="bi bi-telephone me-1"></i> <?php echo htmlspecialchars($d['tel'] ?: '-'); ?></div>
<div class="small text-secondary"><i class="bi bi-envelope me-1"></i> <?php echo htmlspecialchars($d['email'] ?: '-'); ?></div>
</td>
<td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end"
onclick="showEditDoctorModal(<?php echo htmlspecialchars(json_encode($d, JSON_UNESCAPED_UNICODE)); ?>)"
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
<button class="btn btn-link text-warning py-1 px-2 border-end"
onclick="showDoctorHolidays(<?php echo $d['id']; ?>, '<?php echo htmlspecialchars($d['name_'.$lang]); ?>')"
data-bs-toggle="tooltip" title="Holidays">
<i class="bi bi-calendar-x"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
<i class="bi bi-trash3"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<div id="doctorsPagination">
<?php if ($totalPages > 1): ?>
<div class="d-flex justify-content-between align-items-center p-3 border-top">
<div class="text-muted small">
<?php echo __('showing'); ?> <?php echo $offset + 1; ?> - <?php echo min($offset + $limit, $totalDoctors); ?> <?php echo __('of'); ?> <?php echo $totalDoctors; ?>
</div>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page - 1; ?>" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<?php
$range = 2;
$pages_to_show = [];
$pages_to_show[] = 1;
if ($totalPages > 1) { $pages_to_show[] = $totalPages; }
for ($i = $page - $range; $i <= $page + $range; $i++) {
if ($i > 1 && $i < $totalPages) { $pages_to_show[] = $i; }
}
$pages_to_show = array_unique($pages_to_show);
sort($pages_to_show);
$prev_page = 0;
foreach ($pages_to_show as $p):
if ($prev_page > 0 && $p > $prev_page + 1): ?>
<li class="page-item disabled"><span class="page-link">...</span></li>
<?php endif; ?>
<li class="page-item <?php echo $page == $p ? 'active' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $p; ?>">
<?php echo $p; ?>
</a>
</li>
<?php
$prev_page = $p;
endforeach;
?>
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page + 1; ?>" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Doctor Holidays Modal -->
<div class="modal fade" id="doctorHolidaysModal" 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="doctorHolidaysTitle">Doctor Holidays</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="holidayDoctorId">
<form id="addHolidayForm" onsubmit="addDoctorHoliday(); return false;">
<div class="row g-2 mb-3">
<div class="col-md-5">
<label class="small text-muted">Start Date</label>
<input type="date" id="holidayStartDate" class="form-control form-control-sm" required>
</div>
<div class="col-md-5">
<label class="small text-muted">End Date</label>
<input type="date" id="holidayEndDate" class="form-control form-control-sm" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary btn-sm w-100">Add</button>
</div>
<div class="col-12 mt-1">
<input type="text" id="holidayNote" class="form-control form-control-sm" placeholder="Note (optional)">
</div>
</div>
</form>
<h6 class="small text-muted text-uppercase fw-bold mb-2">Current Holidays</h6>
<div class="table-responsive" style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-bordered mb-0">
<thead class="table-light">
<tr>
<th>Start</th>
<th>End</th>
<th>Note</th>
<th style="width: 50px;"></th>
</tr>
</thead>
<tbody id="holidaysListBody">
<tr><td colspan="4" class="text-center text-muted">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchName = document.getElementById('doctorSearchName');
const searchDept = document.getElementById('doctorSearchDept');
const tableBody = document.getElementById('doctorsTableBody');
const paginationContainer = document.getElementById('doctorsPagination');
let timeout = null;
if (searchName) {
searchName.addEventListener('input', function() {
clearTimeout(timeout);
timeout = setTimeout(() => fetchDoctors(1), 300);
});
}
if (searchDept) {
searchDept.addEventListener('change', function() {
fetchDoctors(1);
});
}
// Event delegation for pagination clicks
if (paginationContainer) {
paginationContainer.addEventListener('click', function(e) {
e.preventDefault();
const link = e.target.closest('.page-link');
if (link && !link.parentElement.classList.contains('disabled')) {
const page = link.getAttribute('data-page');
if (page) fetchDoctors(page);
}
});
}
});
function fetchDoctors(page) {
const name = document.getElementById('doctorSearchName').value;
const dept = document.getElementById('doctorSearchDept').value;
const tableBody = document.getElementById('doctorsTableBody');
const paginationContainer = document.getElementById('doctorsPagination');
if (tableBody) tableBody.style.opacity = '0.5';
const params = new URLSearchParams();
if (name) params.append('name', name);
if (dept) params.append('department_id', dept);
params.append('page', page);
params.append('ajax_search', '1');
fetch('doctors.php?' + params.toString())
.then(response => response.json())
.then(data => {
if (tableBody) {
tableBody.innerHTML = data.html;
tableBody.style.opacity = '1';
}
if (paginationContainer) {
paginationContainer.innerHTML = data.pagination;
}
// Re-initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
.catch(error => {
console.error('Error fetching doctors:', error);
if (tableBody) tableBody.style.opacity = '1';
});
}
function showDoctorHolidays(doctorId, doctorName) {
document.getElementById('holidayDoctorId').value = doctorId;
document.getElementById('doctorHolidaysTitle').innerText = 'Holidays - ' + doctorName;
// Clear form
document.getElementById('holidayStartDate').value = '';
document.getElementById('holidayEndDate').value = '';
document.getElementById('holidayNote').value = '';
// Fetch holidays
fetchHolidays(doctorId);
var modal = new bootstrap.Modal(document.getElementById('doctorHolidaysModal'));
modal.show();
}
function fetchHolidays(doctorId) {
fetch('api/doctor_holidays.php?doctor_id=' + doctorId)
.then(response => response.json())
.then(data => {
const tbody = document.getElementById('holidaysListBody');
tbody.innerHTML = '';
if (data.success && data.holidays.length > 0) {
data.holidays.forEach(h => {
tbody.innerHTML += `
<tr>
<td>${h.start_date}</td>
<td>${h.end_date}</td>
<td>${h.note || '-'}</td>
<td class="text-center">
<button class="btn btn-link text-danger p-0" onclick="deleteDoctorHoliday(${h.id})">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
`;
});
} else {
tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted">No holidays found.</td></tr>';
}
})
.catch(err => console.error(err));
}
function addDoctorHoliday() {
const doctorId = document.getElementById('holidayDoctorId').value;
const start = document.getElementById('holidayStartDate').value;
const end = document.getElementById('holidayEndDate').value;
const note = document.getElementById('holidayNote').value;
if (!start || !end) return;
fetch('api/doctor_holidays.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'create',
doctor_id: doctorId,
start_date: start,
end_date: end,
note: note
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
fetchHolidays(doctorId); // Refresh list
document.getElementById('holidayStartDate').value = '';
document.getElementById('holidayEndDate').value = '';
document.getElementById('holidayNote').value = '';
} else {
alert('Error adding holiday: ' + (data.error || 'Unknown error'));
}
})
.catch(err => console.error(err));
}
function deleteDoctorHoliday(id) {
if (!confirm('Delete this holiday?')) return;
const doctorId = document.getElementById('holidayDoctorId').value;
fetch('api/doctor_holidays.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'delete',
id: id
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
fetchHolidays(doctorId); // Refresh list
} else {
alert('Error deleting holiday: ' + (data.error || 'Unknown error'));
}
})
.catch(err => console.error(err));
}
</script>

View File

@ -22,8 +22,8 @@ $query = "
n.name_$lang as nurse_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
LEFT JOIN doctors d ON a.doctor_id = d.id
LEFT JOIN nurses n ON a.nurse_id = n.id
LEFT JOIN employees d ON a.doctor_id = d.id
LEFT JOIN employees n ON a.nurse_id = n.id
$where
ORDER BY a.start_time ASC
";

View File

@ -66,7 +66,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-gray-800">Leave Requests</h1>
<button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#addLeaveModal">
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addLeaveModal">
<i class="fas fa-plus"></i> New Request
</button>
</div>
@ -74,14 +74,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<!-- Filter -->
<div class="card shadow mb-4">
<div class="card-body">
<form method="GET" class="form-inline">
<select name="status" class="form-control mr-2 mb-2">
<form method="GET" class="d-flex align-items-center">
<select name="status" class="form-select me-2" style="width: auto;">
<option value="">All Statuses</option>
<option value="Pending" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Pending') ? 'selected' : ''; ?>>Pending</option>
<option value="Approved" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Approved') ? 'selected' : ''; ?>>Approved</option>
<option value="Rejected" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Rejected') ? 'selected' : ''; ?>>Rejected</option>
</select>
<button type="submit" class="btn btn-primary mb-2">Filter</button>
<button type="submit" class="btn btn-primary">Filter</button>
</form>
</div>
</div>
@ -113,7 +113,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<td><?php echo $req['days']; ?></td>
<td><?php echo htmlspecialchars($req['reason']); ?></td>
<td>
<span class="badge badge-<?php
<span class="badge bg-<?php
echo $req['status'] == 'Approved' ? 'success' :
($req['status'] == 'Pending' ? 'warning' : 'danger');
?>">
@ -163,55 +163,53 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<!-- Add Modal -->
<div class="modal fade" id="addLeaveModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal fade" id="addLeaveModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="POST">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Request Leave</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" name="add_leave" value="1">
<div class="form-group">
<label>Employee</label>
<select name="employee_id" class="form-control" required>
<div class="mb-3">
<label class="form-label">Employee</label>
<select name="employee_id" class="form-select" required>
<?php foreach ($employees as $id => $name): ?>
<option value="<?php echo $id; ?>"><?php echo htmlspecialchars($name); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Leave Type</label>
<select name="leave_type" class="form-control">
<div class="mb-3">
<label class="form-label">Leave Type</label>
<select name="leave_type" class="form-select">
<option value="Annual">Annual</option>
<option value="Sick">Sick</option>
<option value="Casual">Casual</option>
<option value="Unpaid">Unpaid</option>
</select>
</div>
<div class="form-group row">
<div class="col-6">
<label>Start Date</label>
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Start Date</label>
<input type="date" name="start_date" class="form-control" required>
</div>
<div class="col-6">
<label>End Date</label>
<div class="col-md-6">
<label class="form-label">End Date</label>
<input type="date" name="end_date" class="form-control" required>
</div>
</div>
<div class="form-group">
<label>Reason</label>
<div class="mb-3">
<label class="form-label">Reason</label>
<textarea name="reason" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,468 +0,0 @@
<?php
$search_name = $_GET['name'] ?? '';
$search_dept = $_GET['department_id'] ?? '';
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$where = "WHERE 1=1";
$params = [];
if ($search_name) {
$where .= " AND (n.name_en LIKE ? OR n.name_ar LIKE ?)";
$params[] = "%$search_name%";
$params[] = "%$search_name%";
}
if ($search_dept) {
$where .= " AND n.department_id = ?";
$params[] = $search_dept;
}
// Count Total
$countQuery = "SELECT COUNT(*) FROM nurses n $where";
$stmt = $db->prepare($countQuery);
$stmt->execute($params);
$totalNurses = $stmt->fetchColumn();
$totalPages = ceil($totalNurses / $limit);
// Fetch Data
$query = "
SELECT n.*, dept.name_$lang as department_name
FROM nurses n
LEFT JOIN departments dept ON n.department_id = dept.id
$where
ORDER BY n.id DESC
LIMIT $limit OFFSET $offset";
$stmt = $db->prepare($query);
$stmt->execute($params);
$nurses = $stmt->fetchAll();
// --- AJAX HANDLER ---
if (isset($_GET['ajax_search'])) {
ob_start();
if (empty($nurses)):
?>
<tr>
<td colspan="4" class="text-center py-5 text-muted">
<i class="bi bi-person-heart display-4 d-block mb-3"></i>
<?php echo __('no_nurses_found'); ?>
</td>
</tr>
<?php else: ?>
<?php foreach ($nurses as $nurse): ?>
<tr>
<td class="px-4">
<div class="fw-semibold text-dark"><?php echo htmlspecialchars($nurse['name_'.$lang]); ?></div>
<small class="text-muted"><?php echo htmlspecialchars($nurse['name_'.($lang == 'en' ? 'ar' : 'en')]); ?></small>
</td>
<td>
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-25 px-2 py-1">
<?php echo htmlspecialchars($nurse['department_name'] ?: '-'); ?>
</span>
</td>
<td>
<div class="small text-secondary"><i class="bi bi-telephone me-1"></i> <?php echo htmlspecialchars($nurse['tel'] ?: '-'); ?></div>
<div class="small text-secondary"><i class="bi bi-envelope me-1"></i> <?php echo htmlspecialchars($nurse['email'] ?: '-'); ?></div>
</td>
<td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end"
onclick="showEditNurseModal(<?php echo htmlspecialchars(json_encode($nurse, JSON_UNESCAPED_UNICODE)); ?>)"
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteNurseModal(<?php echo $nurse['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
<i class="bi bi-trash3"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif;
$table_html = ob_get_clean();
ob_start();
if ($totalPages > 1): ?>
<div class="d-flex justify-content-between align-items-center p-3 border-top">
<div class="text-muted small">
<?php echo __('showing'); ?> <?php echo $offset + 1; ?> - <?php echo min($offset + $limit, $totalNurses); ?> <?php echo __('of'); ?> <?php echo $totalNurses; ?>
</div>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page - 1; ?>" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<?php
$range = 2;
$pages_to_show = [];
$pages_to_show[] = 1;
if ($totalPages > 1) { $pages_to_show[] = $totalPages; }
for ($i = $page - $range; $i <= $page + $range; $i++) {
if ($i > 1 && $i < $totalPages) { $pages_to_show[] = $i; }
}
$pages_to_show = array_unique($pages_to_show);
sort($pages_to_show);
$prev_page = 0;
foreach ($pages_to_show as $p):
if ($prev_page > 0 && $p > $prev_page + 1): ?>
<li class="page-item disabled"><span class="page-link">...</span></li>
<?php endif; ?>
<li class="page-item <?php echo $page == $p ? 'active' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $p; ?>">
<?php echo $p; ?>
</a>
</li>
<?php
$prev_page = $p;
endforeach;
?>
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page + 1; ?>" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
<?php endif;
$pagination_html = ob_get_clean();
header('Content-Type: application/json');
echo json_encode(['html' => $table_html, 'pagination' => $pagination_html]);
exit;
}
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold text-secondary"><?php echo __('nurses'); ?></h3>
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addNurseModal" onclick="resetNurseModal()">
<i class="bi bi-person-plus-fill me-1"></i> <?php echo __('add_nurse'); ?>
</button>
</div>
<!-- Search Bar -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<form id="nursesSearchForm" class="row g-3" onsubmit="return false;">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text bg-light border-end-0 text-muted"><i class="bi bi-search"></i></span>
<input type="text" name="name" id="nurseSearchName" class="form-control bg-light border-start-0" placeholder="<?php echo __('name'); ?>" value="<?php echo htmlspecialchars($search_name); ?>">
</div>
</div>
<div class="col-md-4">
<select name="department_id" id="nurseSearchDept" class="form-select bg-light">
<option value=""><?php echo __('department'); ?> (<?php echo __('all'); ?>)</option>
<?php foreach ($all_departments as $dept): ?>
<option value="<?php echo $dept['id']; ?>" <?php echo $search_dept == $dept['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($dept['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-secondary w-100" onclick="fetchNurses(1)"><?php echo __('search'); ?></button>
</div>
</form>
</div>
</div>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light text-secondary">
<tr>
<th class="px-4 py-3"><?php echo __('name'); ?></th>
<th class="py-3"><?php echo __('department'); ?></th>
<th class="py-3"><?php echo __('contact'); ?></th>
<th class="py-3 text-end px-4"><?php echo __('actions'); ?></th>
</tr>
</thead>
<tbody id="nursesTableBody">
<?php if (empty($nurses)): ?>
<tr>
<td colspan="4" class="text-center py-5 text-muted">
<i class="bi bi-person-heart display-4 d-block mb-3"></i>
<?php echo __('no_nurses_found'); ?>
</td>
</tr>
<?php else: ?>
<?php foreach ($nurses as $nurse): ?>
<tr>
<td class="px-4">
<div class="fw-semibold text-dark"><?php echo htmlspecialchars($nurse['name_'.$lang]); ?></div>
<small class="text-muted"><?php echo htmlspecialchars($nurse['name_'.($lang == 'en' ? 'ar' : 'en')]); ?></small>
</td>
<td>
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-25 px-2 py-1">
<?php echo htmlspecialchars($nurse['department_name'] ?: '-'); ?>
</span>
</td>
<td>
<div class="small text-secondary"><i class="bi bi-telephone me-1"></i> <?php echo htmlspecialchars($nurse['tel'] ?: '-'); ?></div>
<div class="small text-secondary"><i class="bi bi-envelope me-1"></i> <?php echo htmlspecialchars($nurse['email'] ?: '-'); ?></div>
</td>
<td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end"
onclick="showEditNurseModal(<?php echo htmlspecialchars(json_encode($nurse, JSON_UNESCAPED_UNICODE)); ?>)"
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteNurseModal(<?php echo $nurse['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
<i class="bi bi-trash3"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<div id="nursesPagination">
<?php if ($totalPages > 1): ?>
<div class="d-flex justify-content-between align-items-center p-3 border-top">
<div class="text-muted small">
<?php echo __('showing'); ?> <?php echo $offset + 1; ?> - <?php echo min($offset + $limit, $totalNurses); ?> <?php echo __('of'); ?> <?php echo $totalNurses; ?>
</div>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page - 1; ?>" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<?php
$range = 2;
$pages_to_show = [];
$pages_to_show[] = 1;
if ($totalPages > 1) { $pages_to_show[] = $totalPages; }
for ($i = $page - $range; $i <= $page + $range; $i++) {
if ($i > 1 && $i < $totalPages) { $pages_to_show[] = $i; }
}
$pages_to_show = array_unique($pages_to_show);
sort($pages_to_show);
$prev_page = 0;
foreach ($pages_to_show as $p):
if ($prev_page > 0 && $p > $prev_page + 1): ?>
<li class="page-item disabled"><span class="page-link">...</span></li>
<?php endif; ?>
<li class="page-item <?php echo $page == $p ? 'active' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $p; ?>">
<?php echo $p; ?>
</a>
</li>
<?php
$prev_page = $p;
endforeach;
?>
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
<a class="page-link" href="#" data-page="<?php echo $page + 1; ?>" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Add/Edit Nurse Modal -->
<div class="modal fade" id="addNurseModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="nurseModalTitle"><?php echo __('add_nurse'); ?></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" action="">
<input type="hidden" name="action" id="nurseAction" value="add_nurse">
<input type="hidden" name="id" id="nurseId">
<div class="modal-body p-4">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label"><?php echo __('name_en'); ?> <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="name_en" id="nurseNameEn" required>
</div>
<div class="col-md-6">
<label class="form-label"><?php echo __('name_ar'); ?> <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="name_ar" id="nurseNameAr" required>
</div>
<div class="col-md-6">
<label class="form-label"><?php echo __('email'); ?></label>
<input type="email" class="form-control" name="email" id="nurseEmail">
</div>
<div class="col-md-6">
<label class="form-label"><?php echo __('tel'); ?></label>
<input type="text" class="form-control" name="tel" id="nurseTel">
</div>
<div class="col-md-6">
<label class="form-label"><?php echo __('department'); ?></label>
<select class="form-select" name="department_id" id="nurseDeptId">
<option value=""><?php echo __('select_department'); ?></option>
<?php foreach ($all_departments as $dept): ?>
<option value="<?php echo $dept['id']; ?>">
<?php echo htmlspecialchars($dept['name_' . $lang]); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button>
<button type="submit" class="btn btn-primary"><?php echo __('save'); ?></button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Nurse Modal -->
<div class="modal fade" id="deleteNurseModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0 shadow">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title"><?php echo __('delete_nurse'); ?></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" action="">
<input type="hidden" name="action" value="delete_nurse">
<input type="hidden" name="id" id="deleteNurseId">
<div class="modal-body p-4 text-center">
<div class="mb-3 text-danger">
<i class="bi bi-exclamation-triangle display-1"></i>
</div>
<p class="mb-0 fs-5"><?php echo __('are_you_sure_delete'); ?></p>
<p class="text-muted small"><?php echo __('action_cannot_be_undone'); ?></p>
</div>
<div class="modal-footer bg-light justify-content-center">
<button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="submit" class="btn btn-danger px-4"><?php echo __('delete'); ?></button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchName = document.getElementById('nurseSearchName');
const searchDept = document.getElementById('nurseSearchDept');
const tableBody = document.getElementById('nursesTableBody');
const paginationContainer = document.getElementById('nursesPagination');
let timeout = null;
if (searchName) {
searchName.addEventListener('input', function() {
clearTimeout(timeout);
timeout = setTimeout(() => fetchNurses(1), 300);
});
}
if (searchDept) {
searchDept.addEventListener('change', function() {
fetchNurses(1);
});
}
if (paginationContainer) {
paginationContainer.addEventListener('click', function(e) {
e.preventDefault();
const link = e.target.closest('.page-link');
if (link && !link.parentElement.classList.contains('disabled')) {
const page = link.getAttribute('data-page');
if (page) fetchNurses(page);
}
});
}
});
function fetchNurses(page) {
const name = document.getElementById('nurseSearchName').value;
const dept = document.getElementById('nurseSearchDept').value;
const tableBody = document.getElementById('nursesTableBody');
const paginationContainer = document.getElementById('nursesPagination');
if (tableBody) tableBody.style.opacity = '0.5';
const params = new URLSearchParams();
if (name) params.append('name', name);
if (dept) params.append('department_id', dept);
params.append('page', page);
params.append('ajax_search', '1');
fetch('nurses.php?' + params.toString())
.then(response => response.json())
.then(data => {
if (tableBody) {
tableBody.innerHTML = data.html;
tableBody.style.opacity = '1';
}
if (paginationContainer) {
paginationContainer.innerHTML = data.pagination;
}
// Re-initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
.catch(error => {
console.error('Error fetching nurses:', error);
if (tableBody) tableBody.style.opacity = '1';
});
}
function resetNurseModal() {
document.getElementById('nurseModalTitle').textContent = '<?php echo __('add_nurse'); ?>';
document.getElementById('nurseAction').value = 'add_nurse';
document.getElementById('nurseId').value = '';
document.getElementById('nurseNameEn').value = '';
document.getElementById('nurseNameAr').value = '';
document.getElementById('nurseEmail').value = '';
document.getElementById('nurseTel').value = '';
document.getElementById('nurseDeptId').value = '';
}
function showEditNurseModal(nurse) {
document.getElementById('nurseModalTitle').textContent = '<?php echo __('edit_nurse'); ?>';
document.getElementById('nurseAction').value = 'edit_nurse';
document.getElementById('nurseId').value = nurse.id;
document.getElementById('nurseNameEn').value = nurse.name_en;
document.getElementById('nurseNameAr').value = nurse.name_ar;
document.getElementById('nurseEmail').value = nurse.email;
document.getElementById('nurseTel').value = nurse.tel;
document.getElementById('nurseDeptId').value = nurse.department_id || '';
var modal = new bootstrap.Modal(document.getElementById('addNurseModal'));
modal.show();
}
function showDeleteNurseModal(id) {
document.getElementById('deleteNurseId').value = id;
var modal = new bootstrap.Modal(document.getElementById('deleteNurseModal'));
modal.show();
}
</script>

View File

@ -4,7 +4,12 @@
// Fetch Departments
$departments = $db->query("SELECT * FROM departments")->fetchAll(PDO::FETCH_ASSOC);
// Fetch Doctors
$doctors = $db->query("SELECT * FROM doctors")->fetchAll(PDO::FETCH_ASSOC);
$doctors = $db->query("
SELECT e.*
FROM employees e
JOIN positions p ON e.position_id = p.id
WHERE UPPER(p.name_en) = 'DOCTOR'
")->fetchAll(PDO::FETCH_ASSOC);
// Fetch Patients (Limit 50 for initial load, preferably use AJAX for real search)
$patients = $db->query("SELECT * FROM patients ORDER BY id DESC LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
@ -253,4 +258,4 @@ function issueToken() {
}
});
}
</script>
</script>

View File

@ -35,10 +35,11 @@ $stmt->execute([$start_date, $end_date]);
$revenue_trend = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 6. Visits by Doctor (Top 5)
// Updated to join employees
$stmt = $db->prepare("
SELECT d.name_$lang as doctor_name, COUNT(v.id) as count
FROM visits v
JOIN doctors d ON v.doctor_id = d.id
JOIN employees d ON v.doctor_id = d.id
WHERE DATE(v.visit_date) BETWEEN ? AND ?
GROUP BY v.doctor_id
ORDER BY count DESC
@ -53,13 +54,14 @@ $stmt->execute([$start_date, $end_date]);
$patients_by_gender = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
// 8. Department Revenue Report
// Updated to join employees
$stmt = $db->prepare("
SELECT d.name_$lang as name,
COUNT(DISTINCT b.visit_id) as visit_count,
SUM(b.total_amount) as revenue
FROM bills b
JOIN visits v ON b.visit_id = v.id
JOIN doctors doc ON v.doctor_id = doc.id
JOIN employees doc ON v.doctor_id = doc.id
JOIN departments d ON doc.department_id = d.id
WHERE b.status = 'Paid' AND DATE(b.created_at) BETWEEN ? AND ?
GROUP BY d.id
@ -69,6 +71,7 @@ $stmt->execute([$start_date, $end_date]);
$dept_revenue = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 9. Doctor Revenue Report
// Updated to join employees
$stmt = $db->prepare("
SELECT doc.name_$lang as name,
d.name_$lang as department_name,
@ -76,7 +79,7 @@ $stmt = $db->prepare("
SUM(b.total_amount) as revenue
FROM bills b
JOIN visits v ON b.visit_id = v.id
JOIN doctors doc ON v.doctor_id = doc.id
JOIN employees doc ON v.doctor_id = doc.id
LEFT JOIN departments d ON doc.department_id = d.id
WHERE b.status = 'Paid' AND DATE(b.created_at) BETWEEN ? AND ?
GROUP BY doc.id

View File

@ -14,7 +14,10 @@ if ($search_patient) {
$params[] = "%$search_patient%";
}
if ($search_doctor) {
$where .= " AND (d.name_en LIKE ? OR d.name_ar LIKE ? OR n.name_en LIKE ? OR n.name_ar LIKE ?)";
$where .= " AND (
doc.name_en LIKE ? OR doc.name_ar LIKE ? OR
nur.name_en LIKE ? OR nur.name_ar LIKE ?
)";
$params[] = "%$search_doctor%";
$params[] = "%$search_doctor%";
$params[] = "%$search_doctor%";
@ -30,8 +33,8 @@ $countQuery = "
SELECT COUNT(*)
FROM visits v
JOIN patients p ON v.patient_id = p.id
LEFT JOIN doctors d ON v.doctor_id = d.id
LEFT JOIN nurses n ON v.nurse_id = n.id
LEFT JOIN employees doc ON v.doctor_id = doc.id
LEFT JOIN employees nur ON v.nurse_id = nur.id
$where";
$stmt = $db->prepare($countQuery);
$stmt->execute($params);
@ -42,12 +45,12 @@ $totalPages = ceil($totalVisits / $limit);
$query = "
SELECT v.*,
p.name as patient_name, p.dob as patient_dob, p.gender as patient_gender,
d.name_$lang as doctor_name,
n.name_$lang as nurse_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 doctors d ON v.doctor_id = d.id
LEFT JOIN nurses n ON v.nurse_id = n.id
LEFT JOIN employees doc ON v.doctor_id = doc.id
LEFT JOIN employees nur ON v.nurse_id = nur.id
$where
ORDER BY v.visit_date DESC
LIMIT $limit OFFSET $offset";

View File

@ -338,12 +338,22 @@ $translations = [
'delete_patient' => 'Delete Patient',
'confirm_delete' => 'Are you sure you want to delete',
'add_doctor' => 'Add Doctor',
'edit_doctor' => 'Edit Doctor',
'specialization_en' => 'Specialization (English)',
'specialization_ar' => 'Specialization (Arabic)',
'edit_doctor' => 'Edit Doctor',
'hr_management' => 'HR Management',
'attendance' => 'Attendance',
'leave_requests' => 'Leave Requests',
'add_employee' => 'Add Employee',
'edit_employee' => 'Edit Employee',
'delete_employee' => 'Delete Employee',
'select_position' => 'Select Position',
'no_employees_found' => 'No employees found',
'add_employee' => 'Add Employee',
'edit_employee' => 'Edit Employee',
'delete_employee' => 'Delete Employee',
'select_position' => 'Select Position',
'no_employees_found' => 'No employees found',
],
'ar' => [
'dashboard' => 'لوحة التحكم',
@ -689,5 +699,10 @@ $translations = [
'hr_management' => 'إدارة الموارد البشرية',
'attendance' => 'الحضور والانصراف',
'leave_requests' => 'طلبات الإجازة',
'add_employee' => 'إضافة موظف',
'edit_employee' => 'تعديل موظف',
'delete_employee' => 'حذف موظف',
'select_position' => 'اختر المسمى الوظيفي',
'no_employees_found' => 'لم يتم العثور على موظفين',
]
];

View File

@ -1,17 +0,0 @@
<?php
$section = 'nurses';
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$db = db();
$lang = $_SESSION['lang'];
require_once __DIR__ . '/includes/actions.php';
require_once __DIR__ . '/includes/common_data.php';
require_once __DIR__ . '/includes/layout/header.php';
require_once __DIR__ . '/includes/pages/nurses.php';
require_once __DIR__ . '/includes/layout/footer.php';
?>

View File

@ -1,8 +1,8 @@
<?php
require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/includes/auth.php';
check_auth();
$lang = $_SESSION['lang'] ?? 'en';
@ -14,7 +14,8 @@ $db = db();
// Fetch Doctor Name if filtered
$doctor_name = '';
if ($doctor_id) {
$stmt = $db->prepare("SELECT name_$lang FROM doctors WHERE id = ?");
// doctor_id is now employee_id
$stmt = $db->prepare("SELECT name_$lang FROM employees WHERE id = ?");
$stmt->execute([$doctor_id]);
$doctor_name = $stmt->fetchColumn();
}
@ -22,8 +23,8 @@ if ($doctor_id) {
// Fetch Holidays for the date
$holidays_on_date = [];
if ($doctor_id) {
// Specific doctor holiday check
$stmt = $db->prepare("SELECT note FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
// Specific doctor holiday check (using leave_requests)
$stmt = $db->prepare("SELECT reason FROM leave_requests WHERE employee_id = ? AND status = 'Approved' AND ? BETWEEN start_date AND end_date");
$stmt->execute([$doctor_id, $date]);
$holiday_note = $stmt->fetchColumn();
if ($holiday_note !== false) {
@ -33,12 +34,15 @@ if ($doctor_id) {
];
}
} else {
// All doctors holiday check
// All doctors holiday check (using leave_requests and filtering by position Doctor)
$stmt = $db->prepare("
SELECT d.name_$lang as doctor_name, h.note
FROM doctor_holidays h
JOIN doctors d ON h.doctor_id = d.id
WHERE ? BETWEEN h.start_date AND h.end_date
SELECT e.name_$lang as doctor_name, lr.reason as note
FROM leave_requests lr
JOIN employees e ON lr.employee_id = e.id
JOIN positions p ON e.position_id = p.id
WHERE lr.status = 'Approved'
AND UPPER(p.name_en) = 'DOCTOR'
AND ? BETWEEN lr.start_date AND lr.end_date
");
$stmt->execute([$date]);
$holidays_on_date = $stmt->fetchAll(PDO::FETCH_ASSOC);
@ -52,7 +56,7 @@ $query = "
d.name_$lang as doctor_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
JOIN doctors d ON a.doctor_id = d.id
LEFT JOIN employees d ON a.doctor_id = d.id
WHERE DATE(a.start_time) = ?
";
$params = [$date];
@ -137,7 +141,7 @@ $appointments = $stmt->fetchAll();
<?php if (!$doctor_id): ?>
<th width="20%"><?php echo __('doctor'); ?></th>
<?php endif; ?>
<th><?php echo __('reason'); ?> / <?php echo __('notes'); ?></th>
<th width="25%"><?php echo __('reason'); ?> / <?php echo __('notes'); ?></th>
<th width="10%"><?php echo __('status'); ?></th>
</tr>
</thead>
@ -152,19 +156,14 @@ $appointments = $stmt->fetchAll();
<?php endif; ?>
<td><?php echo htmlspecialchars($app['reason'] ?: '-'); ?></td>
<td>
<span class="badge bg-<?php
echo match($app['status']) {
'Completed' => 'success',
'Cancelled' => 'danger',
default => 'primary'
};
?> text-dark bg-opacity-10 border border-<?php
echo match($app['status']) {
'Completed' => 'success',
'Cancelled' => 'danger',
default => 'primary'
};
?>">
<?php
$statusClass = match($app['status']) {
'Completed' => 'success',
'Cancelled' => 'danger',
default => 'primary'
};
?>
<span class="badge bg-<?php echo $statusClass; ?> text-dark bg-opacity-10 border border-<?php echo $statusClass; ?>">
<?php echo $app['status']; ?>
</span>
</td>

View File

@ -28,17 +28,18 @@ try {
}
// Fetch Visit and Patient Details
// Updated to join employees instead of doctors
$stmt = $db->prepare("
SELECT
v.*,
p.name as patient_name,
p.phone as patient_phone,
p.civil_id,
d.name_en as doctor_name_en,
d.name_ar as doctor_name_ar
e.name_en as doctor_name_en,
e.name_ar as doctor_name_ar
FROM visits v
JOIN patients p ON v.patient_id = p.id
LEFT JOIN doctors d ON v.doctor_id = d.id
LEFT JOIN employees e ON v.doctor_id = e.id
WHERE v.id = ?
");
$stmt->execute([$bill['visit_id']]);

View File

@ -21,19 +21,21 @@ try {
// Fetch visit details
// Ensure all joined columns exist or use COALESCE/Check schema
// We select specific columns to avoid 'ambiguous column' errors or fetching too much
// Updated to join employees instead of doctors
$stmt = $db->prepare("
SELECT
v.*,
p.name as patient_name,
p.dob,
p.gender,
d.name_en as doctor_name_en,
d.name_ar as doctor_name_ar,
d.specialization_en,
d.specialization_ar
e.name_en as doctor_name_en,
e.name_ar as doctor_name_ar,
dept.name_en as specialization_en,
dept.name_ar as specialization_ar
FROM visits v
JOIN patients p ON v.patient_id = p.id
JOIN doctors d ON v.doctor_id = d.id
LEFT JOIN employees e ON v.doctor_id = e.id
LEFT JOIN departments dept ON e.department_id = dept.id
WHERE v.id = ?
");
$stmt->execute([$visit_id]);