Autosave: 20260317-082239
This commit is contained in:
parent
2575e8e91e
commit
9a2dea5596
@ -6,7 +6,7 @@ header('Content-Type: application/json');
|
||||
|
||||
$db = db();
|
||||
$lang = $_SESSION['lang'] ?? 'en';
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($method === 'GET') {
|
||||
$id = $_GET['id'] ?? null;
|
||||
@ -30,12 +30,14 @@ if ($method === 'GET') {
|
||||
$query = "
|
||||
SELECT
|
||||
a.id, a.start_time as start, a.end_time as end, a.reason, a.status,
|
||||
a.patient_id, a.doctor_id,
|
||||
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
|
||||
d.name_$lang as doctor_name,
|
||||
n.name_$lang as nurse_name
|
||||
FROM appointments a
|
||||
JOIN patients p ON a.patient_id = p.id
|
||||
JOIN doctors d ON a.doctor_id = d.id
|
||||
LEFT JOIN doctors d ON a.doctor_id = d.id
|
||||
LEFT JOIN nurses n ON a.nurse_id = n.id
|
||||
WHERE 1=1";
|
||||
|
||||
$params = [];
|
||||
@ -43,18 +45,30 @@ if ($method === 'GET') {
|
||||
if ($endStr) { $query .= " AND a.start_time <= ?"; $params[] = $endStr; }
|
||||
if ($doctor_id) { $query .= " AND a.doctor_id = ?"; $params[] = $doctor_id; }
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$appointments = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
try {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$appointments = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
error_log("Appointments Fetch Error: " . $e->getMessage());
|
||||
$appointments = [];
|
||||
}
|
||||
|
||||
foreach ($appointments as $a) {
|
||||
$color = '#0d6efd'; // blue
|
||||
if ($a['status'] === 'Completed') $color = '#198754'; // green
|
||||
if ($a['status'] === 'Cancelled') $color = '#dc3545'; // red
|
||||
if ($a['visit_type'] === 'Home') $color = '#fd7e14'; // orange for home visits
|
||||
|
||||
$providerName = $a['doctor_name'] ? $a['doctor_name'] : ($a['nurse_name'] . ' (' . __('nurse') . ')');
|
||||
$title = $a['patient_name'] . ' - ' . $providerName;
|
||||
if ($a['visit_type'] === 'Home') {
|
||||
$title = '[🏠] ' . $title;
|
||||
}
|
||||
|
||||
$events[] = [
|
||||
'id' => $a['id'],
|
||||
'title' => $a['patient_name'] . ' (' . $a['doctor_name'] . ')',
|
||||
'title' => $title,
|
||||
'start' => $a['start'],
|
||||
'end' => $a['end'],
|
||||
'color' => $color,
|
||||
@ -62,8 +76,12 @@ if ($method === 'GET') {
|
||||
'type' => 'appointment',
|
||||
'patient_id' => $a['patient_id'],
|
||||
'doctor_id' => $a['doctor_id'],
|
||||
'nurse_id' => $a['nurse_id'],
|
||||
'visit_type' => $a['visit_type'],
|
||||
'address' => $a['address'],
|
||||
'patient_name' => $a['patient_name'],
|
||||
'doctor_name' => $a['doctor_name'],
|
||||
'nurse_name' => $a['nurse_name'],
|
||||
'status' => $a['status'],
|
||||
'reason' => $a['reason']
|
||||
]
|
||||
@ -76,9 +94,17 @@ if ($method === 'GET') {
|
||||
if ($startStr) { $holidayQuery .= " AND holiday_date >= ?"; $holidayParams[] = date('Y-m-d', strtotime($startStr)); }
|
||||
if ($endStr) { $holidayQuery .= " AND holiday_date <= ?"; $holidayParams[] = date('Y-m-d', strtotime($endStr)); }
|
||||
|
||||
$stmt = $db->prepare($holidayQuery);
|
||||
$stmt->execute($holidayParams);
|
||||
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
try {
|
||||
$stmt = $db->prepare($holidayQuery);
|
||||
$stmt->execute($holidayParams);
|
||||
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$holidays = [];
|
||||
}
|
||||
|
||||
$s = get_system_settings();
|
||||
$global_start = $s['working_hours_start'] ?? '08:00';
|
||||
$global_end = $s['working_hours_end'] ?? '17:00';
|
||||
|
||||
foreach ($holidays as $h) {
|
||||
// Render a visible block event in the time grid
|
||||
@ -134,9 +160,13 @@ if ($method === 'GET') {
|
||||
$docHolidayParams[] = $doctor_id;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare($docHolidayQuery);
|
||||
$stmt->execute($docHolidayParams);
|
||||
$docHolidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
try {
|
||||
$stmt = $db->prepare($docHolidayQuery);
|
||||
$stmt->execute($docHolidayParams);
|
||||
$docHolidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$docHolidays = [];
|
||||
}
|
||||
|
||||
foreach ($docHolidays as $dh) {
|
||||
$title = $dh['doctor_name'] . ' - ' . ($dh['note'] ?: 'Holiday');
|
||||
@ -145,21 +175,12 @@ if ($method === 'GET') {
|
||||
$currentDate = strtotime($dh['start_date']);
|
||||
$endDate = strtotime($dh['end_date']);
|
||||
|
||||
// Output background events for each day individually to ensure they render in the time grid perfectly
|
||||
$isFilteredDoctor = ($doctor_id && $doctor_id == $dh['doctor_id']);
|
||||
|
||||
// Output background events for each day individually
|
||||
while ($currentDate <= $endDate) {
|
||||
$dateStr = date('Y-m-d', $currentDate);
|
||||
|
||||
// To block selection, FullCalendar relies on selectOverlap checking properties.
|
||||
// We'll set 'overlap: false' when filtered to a specific doctor.
|
||||
// Also we give it time 00:00:00 to 24:00:00 to fill the column.
|
||||
|
||||
$isFilteredDoctor = ($doctor_id && $doctor_id == $dh['doctor_id']);
|
||||
|
||||
// Render a visible block event in the time grid so it's super obvious
|
||||
$s = get_system_settings();
|
||||
$global_start = $s['working_hours_start'] ?? '08:00';
|
||||
$global_end = $s['working_hours_end'] ?? '17:00';
|
||||
|
||||
$events[] = [
|
||||
'id' => 'doc_hol_block_' . $dh['id'] . '_' . $dateStr,
|
||||
'title' => 'Holiday: ' . $dh['doctor_name'],
|
||||
@ -172,7 +193,6 @@ if ($method === 'GET') {
|
||||
'extendedProps' => ['type' => 'doctor_holiday_block', 'doctor_id' => $dh['doctor_id'], 'blocks_selection' => $isFilteredDoctor]
|
||||
];
|
||||
|
||||
// Keep the background shading
|
||||
$events[] = [
|
||||
'id' => 'doc_hol_bg_' . $dh['id'] . '_' . $dateStr,
|
||||
'start' => $dateStr . 'T00:00:00',
|
||||
@ -181,13 +201,12 @@ if ($method === 'GET') {
|
||||
'allDay' => false,
|
||||
'backgroundColor' => $isFilteredDoctor ? 'rgba(255, 193, 7, 0.5)' : 'rgba(255, 193, 7, 0.15)',
|
||||
'overlap' => !$isFilteredDoctor,
|
||||
'extendedProps' => ['type' => 'doctor_holiday', 'doctor_id' => $dh['doctor_id'], 'blocks_selection' => $isFilteredDoctor, 'blocks_selection' => $isFilteredDoctor]
|
||||
'extendedProps' => ['type' => 'doctor_holiday', 'doctor_id' => $dh['doctor_id'], 'blocks_selection' => $isFilteredDoctor]
|
||||
];
|
||||
|
||||
$currentDate = strtotime('+1 day', $currentDate);
|
||||
}
|
||||
|
||||
// Visible event strip across the top (allDay)
|
||||
$events[] = [
|
||||
'id' => 'doc_hol_' . $dh['id'],
|
||||
'title' => $title,
|
||||
@ -220,7 +239,6 @@ if ($method === 'GET') {
|
||||
}
|
||||
$businessHours = array_values($bhMap);
|
||||
} else {
|
||||
$s = get_system_settings();
|
||||
$st = $s['working_hours_start'] ?? '08:00';
|
||||
$et = $s['working_hours_end'] ?? '17:00';
|
||||
$businessHours = [
|
||||
@ -232,10 +250,6 @@ if ($method === 'GET') {
|
||||
];
|
||||
}
|
||||
|
||||
$s = get_system_settings();
|
||||
$global_start = $s['working_hours_start'] ?? '08:00';
|
||||
$global_end = $s['working_hours_end'] ?? '17:00';
|
||||
|
||||
echo json_encode([
|
||||
'events' => $events,
|
||||
'businessHours' => $businessHours,
|
||||
@ -250,64 +264,103 @@ if ($method === 'GET') {
|
||||
function checkDoctorHoliday($db, $doctor_id, $start_time) {
|
||||
if (!$doctor_id || !$start_time) return false;
|
||||
$date = date('Y-m-d', strtotime($start_time));
|
||||
$stmt = $db->prepare("SELECT COUNT(*) FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
|
||||
$stmt->execute([$doctor_id, $date]);
|
||||
return $stmt->fetchColumn() > 0;
|
||||
try {
|
||||
$stmt = $db->prepare("SELECT COUNT(*) FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
|
||||
$stmt->execute([$doctor_id, $date]);
|
||||
return $stmt->fetchColumn() > 0;
|
||||
} catch (PDOException $e) {
|
||||
error_log("Check Holiday Error: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
if (!$input) {
|
||||
$input = $_POST;
|
||||
}
|
||||
|
||||
if (empty($input)) {
|
||||
echo json_encode(['success' => false, 'error' => 'No input data received']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $input['action'] ?? '';
|
||||
|
||||
if (empty($action)) {
|
||||
echo json_encode(['success' => false, 'error' => 'No action specified']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'create') {
|
||||
$patient_id = $input['patient_id'] ?? '';
|
||||
$doctor_id = $input['doctor_id'] ?? '';
|
||||
$doctor_id = $input['doctor_id'] ?: null; // Nullable
|
||||
$nurse_id = $input['nurse_id'] ?: null; // Nullable
|
||||
$visit_type = $input['visit_type'] ?? 'Clinic';
|
||||
$address = $input['address'] ?? '';
|
||||
$start_time = $input['start_time'] ?? '';
|
||||
$reason = $input['reason'] ?? '';
|
||||
|
||||
if ($patient_id && $doctor_id && $start_time) {
|
||||
// Check for holiday conflict
|
||||
if (checkDoctorHoliday($db, $doctor_id, $start_time)) {
|
||||
if ($patient_id && ($doctor_id || $nurse_id) && $start_time) {
|
||||
// Check for holiday conflict if doctor assigned
|
||||
if ($doctor_id && checkDoctorHoliday($db, $doctor_id, $start_time)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Doctor is on holiday on this date.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO appointments (patient_id, doctor_id, start_time, end_time, reason) VALUES (?, ?, ?, DATE_ADD(?, INTERVAL 30 MINUTE), ?)");
|
||||
$stmt->execute([$patient_id, $doctor_id, $start_time, $start_time, $reason]);
|
||||
echo json_encode(['success' => true, 'id' => $db->lastInsertId()]);
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO appointments (patient_id, doctor_id, nurse_id, visit_type, address, start_time, end_time, reason) VALUES (?, ?, ?, ?, ?, ?, DATE_ADD(?, INTERVAL 30 MINUTE), ?)");
|
||||
$stmt->execute([$patient_id, $doctor_id, $nurse_id, $visit_type, $address, $start_time, $start_time, $reason]);
|
||||
echo json_encode(['success' => true, 'id' => $db->lastInsertId()]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'error' => 'DB Error: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing fields']);
|
||||
}
|
||||
} elseif ($action === 'update') {
|
||||
$id = $input['id'] ?? '';
|
||||
$patient_id = $input['patient_id'] ?? '';
|
||||
$doctor_id = $input['doctor_id'] ?? '';
|
||||
$doctor_id = $input['doctor_id'] ?: null;
|
||||
$nurse_id = $input['nurse_id'] ?: null;
|
||||
$visit_type = $input['visit_type'] ?? 'Clinic';
|
||||
$address = $input['address'] ?? '';
|
||||
$start_time = $input['start_time'] ?? '';
|
||||
$status = $input['status'] ?? 'Scheduled';
|
||||
$reason = $input['reason'] ?? '';
|
||||
|
||||
if ($id && $patient_id && $doctor_id && $start_time) {
|
||||
if ($id && $patient_id && ($doctor_id || $nurse_id) && $start_time) {
|
||||
// Check for holiday conflict
|
||||
if (checkDoctorHoliday($db, $doctor_id, $start_time)) {
|
||||
if ($doctor_id && checkDoctorHoliday($db, $doctor_id, $start_time)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Doctor is on holiday on this date.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("UPDATE appointments SET patient_id = ?, doctor_id = ?, start_time = ?, end_time = DATE_ADD(?, INTERVAL 30 MINUTE), status = ?, reason = ? WHERE id = ?");
|
||||
$stmt->execute([$patient_id, $doctor_id, $start_time, $start_time, $status, $reason, $id]);
|
||||
echo json_encode(['success' => true]);
|
||||
try {
|
||||
$stmt = $db->prepare("UPDATE appointments SET patient_id = ?, doctor_id = ?, nurse_id = ?, visit_type = ?, address = ?, start_time = ?, end_time = DATE_ADD(?, INTERVAL 30 MINUTE), status = ?, reason = ? WHERE id = ?");
|
||||
$stmt->execute([$patient_id, $doctor_id, $nurse_id, $visit_type, $address, $start_time, $start_time, $status, $reason, $id]);
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'error' => 'DB Error: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing fields']);
|
||||
}
|
||||
} elseif ($action === 'delete') {
|
||||
$id = $input['id'] ?? '';
|
||||
if ($id) {
|
||||
$stmt = $db->prepare("DELETE FROM appointments WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
echo json_encode(['success' => true]);
|
||||
try {
|
||||
$stmt = $db->prepare("DELETE FROM appointments WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'error' => 'DB Error: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing ID']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid action']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
@ -171,6 +171,24 @@ try {
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- GET ADS ---
|
||||
if ($action === 'get_ads') {
|
||||
$stmt = $db->query("SELECT * FROM queue_ads WHERE active = 1 ORDER BY created_at DESC");
|
||||
$ads = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Return both languages
|
||||
$data = array_map(function($ad) {
|
||||
return [
|
||||
'id' => $ad['id'],
|
||||
'text_en' => $ad['text_en'],
|
||||
'text_ar' => $ad['text_ar']
|
||||
];
|
||||
}, $ads);
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
exit;
|
||||
}
|
||||
|
||||
throw new Exception('Invalid action');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
6
check_appointments_schema.php
Normal file
6
check_appointments_schema.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
$stmt = $db->query("DESCRIBE appointments");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
print_r($columns);
|
||||
14
check_data.php
Normal file
14
check_data.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
$db = db();
|
||||
|
||||
try {
|
||||
$patient = $db->query("SELECT id, name FROM patients LIMIT 1")->fetch(PDO::FETCH_ASSOC);
|
||||
$doctor = $db->query("SELECT id, name_en FROM doctors LIMIT 1")->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo "Patient: " . ($patient ? "ID=" . $patient['id'] . ", Name=" . $patient['name'] : "None") . "\n";
|
||||
echo "Doctor: " . ($doctor ? "ID=" . $doctor['id'] . ", Name=" . $doctor['name_en'] : "None") . "\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
19
check_doctor_holidays_table.php
Normal file
19
check_doctor_holidays_table.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
$db = db();
|
||||
|
||||
try {
|
||||
$result = $db->query("SHOW COLUMNS FROM doctor_holidays");
|
||||
if ($result) {
|
||||
$columns = $result->fetchAll(PDO::FETCH_ASSOC);
|
||||
echo "Table 'doctor_holidays' exists with columns:\n";
|
||||
foreach ($columns as $col) {
|
||||
echo "- " . $col['Field'] . " (" . $col['Type'] . ")\n";
|
||||
}
|
||||
} else {
|
||||
echo "Table 'doctor_holidays' does not exist.\n";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
6
check_nurses_schema.php
Normal file
6
check_nurses_schema.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
$stmt = $db->query("DESCRIBE nurses");
|
||||
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
print_r($columns);
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE departments ADD COLUMN show_in_queue BOOLEAN DEFAULT 1;
|
||||
7
db/migrations/20260317_create_queue_ads.sql
Normal file
7
db/migrations/20260317_create_queue_ads.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS queue_ads (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
text_en TEXT NOT NULL,
|
||||
text_ar TEXT NOT NULL,
|
||||
active TINYINT(1) DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
11
db/migrations/20260317_enable_home_visits.sql
Normal file
11
db/migrations/20260317_enable_home_visits.sql
Normal file
@ -0,0 +1,11 @@
|
||||
-- Add columns to appointments table
|
||||
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS nurse_id INT NULL;
|
||||
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS visit_type ENUM('Clinic', 'Home') DEFAULT 'Clinic';
|
||||
ALTER TABLE appointments ADD COLUMN IF NOT EXISTS address TEXT NULL;
|
||||
ALTER TABLE appointments ADD CONSTRAINT fk_appointment_nurse FOREIGN KEY (nurse_id) REFERENCES nurses(id) ON DELETE SET NULL;
|
||||
|
||||
-- Add columns to visits table
|
||||
ALTER TABLE visits ADD COLUMN IF NOT EXISTS nurse_id INT NULL;
|
||||
ALTER TABLE visits ADD COLUMN IF NOT EXISTS visit_type ENUM('Clinic', 'Home') DEFAULT 'Clinic';
|
||||
ALTER TABLE visits ADD COLUMN IF NOT EXISTS address TEXT NULL;
|
||||
ALTER TABLE visits ADD CONSTRAINT fk_visit_nurse FOREIGN KEY (nurse_id) REFERENCES nurses(id) ON DELETE SET NULL;
|
||||
10
delete_test_appointment.php
Normal file
10
delete_test_appointment.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
$db = db();
|
||||
// Delete the test appointment created by test_create_appointment_curl.php (ID 14)
|
||||
// Use a safe check to only delete if it looks like a test
|
||||
$id = 14;
|
||||
$stmt = $db->prepare("DELETE FROM appointments WHERE id = ? AND reason = 'Test API'");
|
||||
$stmt->execute([$id]);
|
||||
echo "Deleted test appointment $id (if it matched).\n";
|
||||
|
||||
4
doctor_holidays.php
Normal file
4
doctor_holidays.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
require_once 'includes/layout/header.php';
|
||||
require_once 'includes/pages/doctor_holidays.php';
|
||||
require_once 'includes/layout/footer.php';
|
||||
10
home_visits.php
Normal file
10
home_visits.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/layout/header.php';
|
||||
|
||||
// Check for user role if needed (e.g. only doctors/admins/nurses)
|
||||
// For now, accessible to all logged in users
|
||||
|
||||
require_once __DIR__ . '/includes/pages/home_visits.php';
|
||||
|
||||
require_once __DIR__ . '/includes/layout/footer.php';
|
||||
?>
|
||||
@ -223,9 +223,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} elseif ($_POST['action'] === 'add_department') {
|
||||
$name_en = $_POST['name_en'] ?? '';
|
||||
$name_ar = $_POST['name_ar'] ?? '';
|
||||
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
|
||||
if ($name_en && $name_ar) {
|
||||
$stmt = $db->prepare("INSERT INTO departments (name_en, name_ar) VALUES (?, ?)");
|
||||
$stmt->execute([$name_en, $name_ar]);
|
||||
$stmt = $db->prepare("INSERT INTO departments (name_en, name_ar, show_in_queue) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$name_en, $name_ar, $show_in_queue]);
|
||||
$_SESSION['flash_message'] = __('add_department') . ' ' . __('successfully');
|
||||
$redirect = true;
|
||||
}
|
||||
@ -233,9 +234,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$id = $_POST['id'] ?? '';
|
||||
$name_en = $_POST['name_en'] ?? '';
|
||||
$name_ar = $_POST['name_ar'] ?? '';
|
||||
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
|
||||
if ($id && $name_en && $name_ar) {
|
||||
$stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ? WHERE id = ?");
|
||||
$stmt->execute([$name_en, $name_ar, $id]);
|
||||
$stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ?, show_in_queue = ? WHERE id = ?");
|
||||
$stmt->execute([$name_en, $name_ar, $show_in_queue, $id]);
|
||||
$_SESSION['flash_message'] = __('edit_department') . ' ' . __('successfully');
|
||||
$redirect = true;
|
||||
}
|
||||
@ -288,7 +290,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
} elseif ($_POST['action'] === 'record_visit') {
|
||||
$patient_id = $_POST['patient_id'] ?? '';
|
||||
$doctor_id = $_POST['doctor_id'] ?? '';
|
||||
$doctor_id = $_POST['doctor_id'] ?: null; // Nullable
|
||||
$nurse_id = $_POST['nurse_id'] ?: null; // Nullable
|
||||
$visit_type = $_POST['visit_type'] ?? 'Clinic';
|
||||
$appointment_id = $_POST['appointment_id'] ?: null;
|
||||
$weight = $_POST['weight'] ?? '';
|
||||
$bp = $_POST['blood_pressure'] ?? '';
|
||||
@ -298,15 +302,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$diagnosis = $_POST['diagnosis'] ?? '';
|
||||
$treatment = $_POST['treatment_plan'] ?? '';
|
||||
|
||||
if ($patient_id && $doctor_id) {
|
||||
if ($patient_id && ($doctor_id || $nurse_id)) {
|
||||
$db->beginTransaction();
|
||||
$stmt = $db->prepare("INSERT INTO visits (patient_id, doctor_id, appointment_id, weight, blood_pressure, heart_rate, temperature, symptoms, diagnosis, treatment_plan) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$patient_id, $doctor_id, $appointment_id, $weight, $bp, $hr, $temp, $symptoms, $diagnosis, $treatment]);
|
||||
|
||||
// Fetch address from appointment if not provided via some other means
|
||||
// For now, we rely on appointment_id if present to get the address
|
||||
$address = null;
|
||||
if ($appointment_id) {
|
||||
$stmtApt = $db->prepare("SELECT address FROM appointments WHERE id = ?");
|
||||
$stmtApt->execute([$appointment_id]);
|
||||
$apt = $stmtApt->fetch();
|
||||
$address = $apt['address'] ?? null;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO visits (patient_id, doctor_id, nurse_id, visit_type, address, appointment_id, weight, blood_pressure, heart_rate, temperature, symptoms, diagnosis, treatment_plan) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$patient_id, $doctor_id, $nurse_id, $visit_type, $address, $appointment_id, $weight, $bp, $hr, $temp, $symptoms, $diagnosis, $treatment]);
|
||||
$visit_id = $db->lastInsertId();
|
||||
$token_message = '';
|
||||
|
||||
// Token Generation
|
||||
if (isset($_POST['generate_token']) && $_POST['generate_token'] == '1') {
|
||||
// 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 = ?");
|
||||
$stmtDoc->execute([$doctor_id]);
|
||||
$docData = $stmtDoc->fetch();
|
||||
@ -341,6 +356,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmt = $db->prepare("UPDATE appointments SET status = 'Completed' WHERE id = ?");
|
||||
$stmt->execute([$appointment_id]);
|
||||
}
|
||||
|
||||
// Auto-create bill if requested (e.g. from Home Visits)
|
||||
if (isset($_POST['create_bill']) && $_POST['create_bill'] == '1') {
|
||||
$stmtIns = $db->prepare("SELECT insurance_company_id FROM patients WHERE id = ?");
|
||||
$stmtIns->execute([$patient_id]);
|
||||
$patient = $stmtIns->fetch();
|
||||
|
||||
$total = 0;
|
||||
$insurance_covered = 0;
|
||||
$patient_payable = 0;
|
||||
|
||||
$stmtBill = $db->prepare("INSERT INTO bills (patient_id, visit_id, total_amount, insurance_covered, patient_payable, status) VALUES (?, ?, ?, ?, ?, 'Pending')");
|
||||
$stmtBill->execute([$patient_id, $visit_id, $total, $insurance_covered, $patient_payable]);
|
||||
$bill_id = $db->lastInsertId();
|
||||
|
||||
$stmtItem = $db->prepare("INSERT INTO bill_items (bill_id, description, amount) VALUES (?, ?, ?)");
|
||||
$stmtItem->execute([$bill_id, 'Home Visit Service', 0]);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$_SESSION['flash_message'] = __('add_visit') . ' ' . __('successfully') . $token_message;
|
||||
$redirect = true;
|
||||
@ -991,24 +1025,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ($expiry) {
|
||||
if (is_numeric($expiry)) {
|
||||
$ts = strtotime($expiry);
|
||||
} else {
|
||||
$ts = strtotime($expiry);
|
||||
}
|
||||
|
||||
if ($ts) {
|
||||
$expiry = date('Y-m-d', $ts);
|
||||
} else {
|
||||
$expiry = null;
|
||||
}
|
||||
} else {
|
||||
$expiry = null;
|
||||
}
|
||||
*/
|
||||
|
||||
$stmt->execute([$name_en, $name_ar, $group_id, $price, $expiry, $supplier_id]);
|
||||
}
|
||||
}
|
||||
@ -1083,6 +1099,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$redirect = true;
|
||||
}
|
||||
}
|
||||
} elseif ($_POST['action'] === 'add_queue_ad') {
|
||||
$text_en = $_POST['text_en'] ?? '';
|
||||
$text_ar = $_POST['text_ar'] ?? '';
|
||||
$active = isset($_POST['active']) ? 1 : 0;
|
||||
|
||||
if ($text_en && $text_ar) {
|
||||
$stmt = $db->prepare("INSERT INTO queue_ads (text_en, text_ar, active) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$text_en, $text_ar, $active]);
|
||||
$_SESSION['flash_message'] = __('add_ad') . ' ' . __('successfully');
|
||||
$redirect = true;
|
||||
}
|
||||
} elseif ($_POST['action'] === 'edit_queue_ad') {
|
||||
$id = $_POST['id'] ?? '';
|
||||
$text_en = $_POST['text_en'] ?? '';
|
||||
$text_ar = $_POST['text_ar'] ?? '';
|
||||
$active = isset($_POST['active']) ? 1 : 0;
|
||||
|
||||
if ($id && $text_en && $text_ar) {
|
||||
$stmt = $db->prepare("UPDATE queue_ads SET text_en = ?, text_ar = ?, active = ? WHERE id = ?");
|
||||
$stmt->execute([$text_en, $text_ar, $active, $id]);
|
||||
$_SESSION['flash_message'] = __('edit_ad') . ' ' . __('successfully');
|
||||
$redirect = true;
|
||||
}
|
||||
} elseif ($_POST['action'] === 'delete_queue_ad') {
|
||||
$id = $_POST['id'] ?? '';
|
||||
if ($id) {
|
||||
$stmt = $db->prepare("DELETE FROM queue_ads WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$_SESSION['flash_message'] = __('delete') . ' ' . __('successfully');
|
||||
$redirect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -102,6 +102,7 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp
|
||||
<a href="patients.php" class="sidebar-link <?php echo $section === 'patients' ? 'active' : ''; ?>"><i class="bi bi-people me-2"></i> <?php echo __('patients'); ?></a>
|
||||
<a href="visits.php" class="sidebar-link <?php echo $section === 'visits' ? 'active' : ''; ?>"><i class="bi bi-clipboard2-pulse me-2"></i> <?php echo __('visits'); ?></a>
|
||||
<a href="appointments.php" class="sidebar-link <?php echo $section === "appointments" ? "active" : ""; ?>"><i class="bi bi-calendar-event me-2"></i> <?php echo __("appointments"); ?></a>
|
||||
<a href="home_visits.php" class="sidebar-link <?php echo $section === 'home_visits' ? 'active' : ''; ?>"><i class="bi bi-house-heart me-2"></i> <?php echo __('home_visits'); ?></a>
|
||||
<a href="queue.php" class="sidebar-link <?php echo $section === 'queue' ? 'active' : ''; ?>"><i class="bi bi-list-ol me-2"></i> <?php echo __('queue_management'); ?></a>
|
||||
|
||||
<a href="#labSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ['laboratory_tests', 'test_groups', 'laboratory_inquiries']) ? 'active' : ''; ?> d-flex justify-content-between align-items-center">
|
||||
@ -145,13 +146,14 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp
|
||||
<a href="billing.php" class="sidebar-link <?php echo $section === 'billing' ? 'active' : ''; ?>"><i class="bi bi-receipt me-2"></i> <?php echo __('billing'); ?></a>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<a href="#settingsSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments']) ? 'active' : ''; ?> d-flex justify-content-between align-items-center">
|
||||
<a href="#settingsSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments', 'queue_ads']) ? 'active' : ''; ?> d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-gear me-2"></i> <?php echo __('settings'); ?></span>
|
||||
<i class="bi bi-chevron-down small"></i>
|
||||
</a>
|
||||
<div class="collapse <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments']) ? 'show' : ''; ?>" id="settingsSubmenu">
|
||||
<div class="collapse <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments', 'queue_ads']) ? 'show' : ''; ?>" id="settingsSubmenu">
|
||||
<div class="sidebar-submenu">
|
||||
<a href="settings.php" class="sidebar-link py-2 <?php echo $section === 'company_profile' ? 'active' : ''; ?>"><i class="bi bi-building me-2"></i> <?php echo __('company_profile'); ?></a>
|
||||
<a href="employees.php" class="sidebar-link py-2 <?php echo $section === 'employees' ? 'active' : ''; ?>"><i class="bi bi-person-workspace me-2"></i> <?php echo __('employees'); ?></a>
|
||||
@ -159,6 +161,7 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp
|
||||
<a href="departments.php" class="sidebar-link py-2 <?php echo $section === 'departments' ? 'active' : ''; ?>"><i class="bi bi-diagram-3 me-2"></i> <?php echo __('departments'); ?></a>
|
||||
<a href="hospital_services.php" class="sidebar-link py-2 <?php echo $section === 'services' ? 'active' : ''; ?>"><i class="bi bi-activity me-2"></i> <?php echo __('services'); ?></a>
|
||||
<a href="cities.php" class="sidebar-link py-2 <?php echo $section === 'cities' ? 'active' : ''; ?>"><i class="bi bi-building me-2"></i> <?php echo __('cities'); ?></a>
|
||||
<a href="queue_ads.php" class="sidebar-link py-2 <?php echo $section === 'queue_ads' ? 'active' : ''; ?>"><i class="bi bi-megaphone me-2"></i> <?php echo __('queue_ads'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@ -41,6 +41,10 @@
|
||||
<div class="badge bg-danger me-2" style="width: 15px; height: 15px; border-radius: 50%;"> </div>
|
||||
<small class="text-muted">Cancelled</small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-3 mb-2">
|
||||
<div class="badge me-2" style="width: 15px; height: 15px; border-radius: 50%; background-color: #fd7e14;"> </div>
|
||||
<small class="text-muted">Home Visit</small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="badge bg-warning me-2" style="width: 15px; height: 15px; border-radius: 50%;"> </div>
|
||||
<small class="text-muted">Holiday</small>
|
||||
@ -71,18 +75,53 @@
|
||||
<label class="form-label small text-muted"><?php echo __('patient'); ?></label>
|
||||
<select id="apt_patient_id" class="form-select select2-modal-apt">
|
||||
<?php foreach ($all_patients as $p): ?>
|
||||
<option value="<?php echo $p['id']; ?>"><?php echo htmlspecialchars($p['name']); ?></option>
|
||||
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo htmlspecialchars($p['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted">Visit Type</label>
|
||||
<select id="apt_visit_type" class="form-select" onchange="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="apt_provider_type" class="form-select" onchange="toggleProviderField()">
|
||||
<option value="Doctor">Doctor</option>
|
||||
<option value="Nurse">Nurse</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-none" id="div_address">
|
||||
<label class="form-label small text-muted">Address (For Home Visit)</label>
|
||||
<textarea id="apt_address" class="form-control" rows="2" placeholder="Enter patient address..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="div_doctor">
|
||||
<label class="form-label small text-muted"><?php echo __('doctor'); ?></label>
|
||||
<select id="apt_doctor_id" class="form-select select2-modal-apt">
|
||||
<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="div_nurse">
|
||||
<label class="form-label small text-muted"><?php echo __('nurse'); ?></label>
|
||||
<select id="apt_nurse_id" class="form-select select2-modal-apt">
|
||||
<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>
|
||||
@ -134,19 +173,68 @@ function initAptSelect2() {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAddressField() {
|
||||
var type = document.getElementById('apt_visit_type').value;
|
||||
var div = document.getElementById('div_address');
|
||||
if (type === 'Home') {
|
||||
div.classList.remove('d-none');
|
||||
// Auto-fill address if empty and patient selected
|
||||
var addressField = document.getElementById('apt_address');
|
||||
if (!addressField.value) {
|
||||
var patientSelect = document.getElementById('apt_patient_id');
|
||||
var selectedOption = patientSelect.options[patientSelect.selectedIndex];
|
||||
if (selectedOption && selectedOption.dataset.address) {
|
||||
addressField.value = selectedOption.dataset.address;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
div.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleProviderField() {
|
||||
var type = document.getElementById('apt_provider_type').value;
|
||||
var divDoc = document.getElementById('div_doctor');
|
||||
var divNurse = document.getElementById('div_nurse');
|
||||
|
||||
if (type === 'Nurse') {
|
||||
divDoc.classList.add('d-none');
|
||||
divNurse.classList.remove('d-none');
|
||||
// Clear doctor selection? Maybe not necessary, backend handles nulls based on logic
|
||||
} else {
|
||||
divDoc.classList.remove('d-none');
|
||||
divNurse.classList.add('d-none');
|
||||
}
|
||||
// Re-validate holiday check since provider changed
|
||||
validateHolidayFrontend();
|
||||
}
|
||||
|
||||
function showCreateModal(startTime = null) {
|
||||
document.getElementById('modalTitle').innerText = '<?php echo __('book_appointment'); ?>';
|
||||
document.getElementById('apt_id').value = '';
|
||||
document.getElementById('apt_reason').value = '';
|
||||
document.getElementById('apt_status').value = 'Scheduled';
|
||||
|
||||
// Defaults
|
||||
document.getElementById('apt_visit_type').value = 'Clinic';
|
||||
document.getElementById('apt_provider_type').value = 'Doctor';
|
||||
document.getElementById('apt_address').value = '';
|
||||
$('#apt_nurse_id').val('').trigger('change');
|
||||
|
||||
toggleAddressField();
|
||||
toggleProviderField();
|
||||
|
||||
document.getElementById('btnDeleteApt').style.display = 'none';
|
||||
|
||||
if (startTime) {
|
||||
if (startTime && startTime instanceof Date) {
|
||||
var offset = startTime.getTimezoneOffset() * 60000;
|
||||
var localISOTime = (new Date(startTime.getTime() - offset)).toISOString().slice(0, 16);
|
||||
document.getElementById('apt_start_time').value = localISOTime;
|
||||
} else {
|
||||
document.getElementById('apt_start_time').value = new Date().toISOString().slice(0, 16);
|
||||
var now = new Date();
|
||||
var offset = now.getTimezoneOffset() * 60000;
|
||||
var localISOTime = (new Date(now.getTime() - offset)).toISOString().slice(0, 16);
|
||||
document.getElementById('apt_start_time').value = localISOTime;
|
||||
}
|
||||
|
||||
if (document.getElementById('doctorFilter').value) {
|
||||
@ -161,11 +249,16 @@ function showCreateModal(startTime = null) {
|
||||
|
||||
function saveAppointment() {
|
||||
var id = document.getElementById('apt_id').value;
|
||||
var providerType = document.getElementById('apt_provider_type').value;
|
||||
|
||||
var data = {
|
||||
action: id ? 'update' : 'create',
|
||||
id: id,
|
||||
patient_id: document.getElementById('apt_patient_id').value,
|
||||
doctor_id: document.getElementById('apt_doctor_id').value,
|
||||
doctor_id: providerType === 'Doctor' ? document.getElementById('apt_doctor_id').value : null,
|
||||
nurse_id: providerType === 'Nurse' ? document.getElementById('apt_nurse_id').value : null,
|
||||
visit_type: document.getElementById('apt_visit_type').value,
|
||||
address: document.getElementById('apt_address').value,
|
||||
start_time: document.getElementById('apt_start_time').value,
|
||||
status: document.getElementById('apt_status').value,
|
||||
reason: document.getElementById('apt_reason').value
|
||||
@ -176,7 +269,12 @@ function saveAppointment() {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
var modalEl = document.getElementById('appointmentDetailsModal');
|
||||
@ -186,6 +284,10 @@ function saveAppointment() {
|
||||
} else {
|
||||
alert('Error: ' + (result.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fetch error:', error);
|
||||
alert('An error occurred while saving the appointment. Please check console for details.');
|
||||
});
|
||||
}
|
||||
|
||||
@ -198,7 +300,12 @@ function deleteAppointment() {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'delete', id: id })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
var modalEl = document.getElementById('appointmentDetailsModal');
|
||||
@ -208,6 +315,10 @@ function deleteAppointment() {
|
||||
} else {
|
||||
alert('Error: ' + (result.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fetch error:', error);
|
||||
alert('An error occurred while deleting the appointment. Please check console for details.');
|
||||
});
|
||||
}
|
||||
|
||||
@ -241,7 +352,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
businessHours: true,
|
||||
events: function(fetchInfo, successCallback, failureCallback) {
|
||||
fetch('api/appointments.php?start=' + fetchInfo.startStr + '&end=' + fetchInfo.endStr + '&doctor_id=' + doctorFilter.value)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// We DO NOT setOption('businessHours') here to prevent FullCalendar from re-rendering and clearing events.
|
||||
successCallback(data.events);
|
||||
@ -270,7 +386,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('modalTitle').innerText = 'Edit Appointment';
|
||||
document.getElementById('apt_id').value = info.event.id;
|
||||
$('#apt_patient_id').val(props.patient_id).trigger('change');
|
||||
$('#apt_doctor_id').val(props.doctor_id).trigger('change');
|
||||
|
||||
// Set fields
|
||||
var visitType = props.visit_type || 'Clinic';
|
||||
document.getElementById('apt_visit_type').value = visitType;
|
||||
document.getElementById('apt_address').value = props.address || '';
|
||||
toggleAddressField();
|
||||
|
||||
var providerType = props.nurse_id ? 'Nurse' : 'Doctor';
|
||||
document.getElementById('apt_provider_type').value = providerType;
|
||||
toggleProviderField();
|
||||
|
||||
if (providerType === 'Doctor') {
|
||||
$('#apt_doctor_id').val(props.doctor_id).trigger('change');
|
||||
} else {
|
||||
$('#apt_nurse_id').val(props.nurse_id).trigger('change');
|
||||
}
|
||||
|
||||
var start = info.event.start;
|
||||
var offset = start.getTimezoneOffset() * 60000;
|
||||
@ -300,10 +431,32 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
validateHolidayFrontend();
|
||||
});
|
||||
|
||||
// When patient changes, auto-fill address if empty and Home Visit is selected
|
||||
$('#apt_patient_id').on('change', function() {
|
||||
if (document.getElementById('apt_visit_type').value === 'Home') {
|
||||
var addressField = document.getElementById('apt_address');
|
||||
if (!addressField.value) {
|
||||
var selectedOption = this.options[this.selectedIndex];
|
||||
if (selectedOption && selectedOption.dataset.address) {
|
||||
addressField.value = selectedOption.dataset.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#apt_doctor_id').on('change', validateHolidayFrontend);
|
||||
$('#apt_start_time').on('change', validateHolidayFrontend);
|
||||
$('#apt_provider_type').on('change', validateHolidayFrontend); // Re-validate when provider changes
|
||||
|
||||
function validateHolidayFrontend() {
|
||||
var providerType = $('#apt_provider_type').val();
|
||||
if (providerType === 'Nurse') {
|
||||
// Nurses don't have holiday checks yet
|
||||
$('#holidayWarning').remove();
|
||||
document.getElementById('btnSaveApt').disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var docId = $('#apt_doctor_id').val();
|
||||
var startTimeStr = $('#apt_start_time').val();
|
||||
var btnSave = document.getElementById('btnSaveApt');
|
||||
|
||||
@ -32,7 +32,7 @@ if (isset($_GET['ajax_search'])) {
|
||||
if (empty($departments)):
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-5 text-muted">
|
||||
<td colspan="5" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-diagram-3 display-4 d-block mb-3"></i>
|
||||
<?php echo __('no_departments_found'); ?>
|
||||
</td>
|
||||
@ -43,6 +43,13 @@ if (isset($_GET['ajax_search'])) {
|
||||
<td class="px-4 text-secondary"><?php echo $dept['id']; ?></td>
|
||||
<td class="fw-semibold text-dark"><?php echo htmlspecialchars($dept['name_en']); ?></td>
|
||||
<td class="text-secondary"><?php echo htmlspecialchars($dept['name_ar']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($dept['show_in_queue']): ?>
|
||||
<span class="badge bg-success-subtle text-success rounded-pill px-3"><?php echo __('active'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary-subtle text-secondary rounded-pill px-3"><?php echo __('inactive'); ?></span>
|
||||
<?php endif; ?>
|
||||
</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"
|
||||
@ -150,13 +157,14 @@ if (isset($_GET['ajax_search'])) {
|
||||
<th class="px-4 py-3">#</th>
|
||||
<th class="py-3"><?php echo __('name_en'); ?></th>
|
||||
<th class="py-3"><?php echo __('name_ar'); ?></th>
|
||||
<th class="py-3 text-center"><?php echo __('show_in_queue'); ?></th>
|
||||
<th class="py-3 text-end px-4"><?php echo __('actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="departmentsTableBody">
|
||||
<?php if (empty($departments)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-5 text-muted">
|
||||
<td colspan="5" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-diagram-3 display-4 d-block mb-3"></i>
|
||||
<?php echo __('no_departments_found'); ?>
|
||||
</td>
|
||||
@ -167,6 +175,13 @@ if (isset($_GET['ajax_search'])) {
|
||||
<td class="px-4 text-secondary"><?php echo $dept['id']; ?></td>
|
||||
<td class="fw-semibold text-dark"><?php echo htmlspecialchars($dept['name_en']); ?></td>
|
||||
<td class="text-secondary"><?php echo htmlspecialchars($dept['name_ar']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($dept['show_in_queue']): ?>
|
||||
<span class="badge bg-success-subtle text-success rounded-pill px-3"><?php echo __('active'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary-subtle text-secondary rounded-pill px-3"><?php echo __('inactive'); ?></span>
|
||||
<?php endif; ?>
|
||||
</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"
|
||||
@ -260,6 +275,10 @@ if (isset($_GET['ajax_search'])) {
|
||||
<label class="form-label"><?php echo __('name_ar'); ?> <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="name_ar" id="deptNameAr" required>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" name="show_in_queue" id="deptShowInQueue" value="1" checked>
|
||||
<label class="form-check-label" for="deptShowInQueue"><?php echo __('show_in_queue'); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button>
|
||||
@ -365,6 +384,7 @@ function resetDepartmentModal() {
|
||||
|
||||
document.getElementById('deptNameEn').value = '';
|
||||
document.getElementById('deptNameAr').value = '';
|
||||
document.getElementById('deptShowInQueue').checked = true;
|
||||
}
|
||||
|
||||
function showEditDepartmentModal(dept) {
|
||||
@ -374,6 +394,7 @@ function showEditDepartmentModal(dept) {
|
||||
|
||||
document.getElementById('deptNameEn').value = dept.name_en;
|
||||
document.getElementById('deptNameAr').value = dept.name_ar;
|
||||
document.getElementById('deptShowInQueue').checked = (dept.show_in_queue == 1);
|
||||
|
||||
var modal = new bootstrap.Modal(document.getElementById('addDepartmentModal'));
|
||||
modal.show();
|
||||
|
||||
179
includes/pages/doctor_holidays.php
Normal file
179
includes/pages/doctor_holidays.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?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>
|
||||
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="">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('Are you sure you want to delete this holiday?')) {
|
||||
document.getElementById('deleteId').value = id;
|
||||
document.getElementById('deleteForm').submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
217
includes/pages/home_visits.php
Normal file
217
includes/pages/home_visits.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
$search_status = $_GET['status'] ?? '';
|
||||
$search_date = $_GET['date'] ?? date('Y-m-d');
|
||||
|
||||
$where = "WHERE a.visit_type = 'Home'";
|
||||
$params = [];
|
||||
|
||||
if ($search_status) {
|
||||
$where .= " AND a.status = ?";
|
||||
$params[] = $search_status;
|
||||
}
|
||||
if ($search_date) {
|
||||
$where .= " AND DATE(a.start_time) = ?";
|
||||
$params[] = $search_date;
|
||||
}
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
a.*,
|
||||
p.name as patient_name, p.phone as patient_phone,
|
||||
d.name_$lang as doctor_name,
|
||||
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
|
||||
$where
|
||||
ORDER BY a.start_time ASC
|
||||
";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$appointments = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="fw-bold text-secondary"><i class="bi bi-house-heart me-2"></i><?php echo __('home_visits'); ?></h3>
|
||||
<a href="appointments.php" class="btn btn-outline-primary">
|
||||
<i class="bi bi-calendar-check me-1"></i> <?php echo __('appointments'); ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<form class="row g-3" method="GET">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small text-muted"><?php echo __('date'); ?></label>
|
||||
<input type="date" name="date" class="form-control" value="<?php echo htmlspecialchars($search_date); ?>" onchange="this.form.submit()">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small text-muted"><?php echo __('status'); ?></label>
|
||||
<select name="status" class="form-select" onchange="this.form.submit()">
|
||||
<option value=""><?php echo __('all'); ?></option>
|
||||
<option value="Scheduled" <?php echo $search_status === 'Scheduled' ? 'selected' : ''; ?>>Scheduled</option>
|
||||
<option value="Completed" <?php echo $search_status === 'Completed' ? 'selected' : ''; ?>>Completed</option>
|
||||
<option value="Cancelled" <?php echo $search_status === 'Cancelled' ? 'selected' : ''; ?>>Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<?php if (empty($appointments)): ?>
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="bi bi-house-slash display-4 text-muted mb-3 d-block"></i>
|
||||
<p class="text-muted"><?php echo __('no_appointments_found'); ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($appointments as $app): ?>
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<span class="badge <?php echo $app['status'] === 'Completed' ? 'bg-success' : ($app['status'] === 'Cancelled' ? 'bg-danger' : 'bg-primary'); ?>">
|
||||
<?php echo $app['status']; ?>
|
||||
</span>
|
||||
<small class="text-muted fw-bold">
|
||||
<?php echo date('H:i', strtotime($app['start_time'])); ?>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title mb-1"><?php echo htmlspecialchars($app['patient_name']); ?></h5>
|
||||
<p class="text-muted small mb-3">
|
||||
<i class="bi bi-telephone me-1"></i> <?php echo htmlspecialchars($app['patient_phone']); ?>
|
||||
</p>
|
||||
|
||||
<div class="mb-3 p-2 bg-light rounded small">
|
||||
<i class="bi bi-geo-alt text-danger me-1"></i>
|
||||
<?php echo nl2br(htmlspecialchars($app['address'] ?? 'No address provided')); ?>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="avatar me-2 bg-secondary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;">
|
||||
<i class="bi bi-person-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<small class="d-block text-muted"><?php echo __('provider'); ?></small>
|
||||
<span class="fw-bold text-dark">
|
||||
<?php
|
||||
if ($app['doctor_id']) {
|
||||
echo htmlspecialchars($app['doctor_name']) . ' (' . __('doctor') . ')';
|
||||
} elseif ($app['nurse_id']) {
|
||||
echo htmlspecialchars($app['nurse_name']) . ' (' . __('nurse') . ')';
|
||||
} else {
|
||||
echo '<span class="text-danger">Unassigned</span>';
|
||||
}
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($app['reason']): ?>
|
||||
<p class="small text-muted mb-0">
|
||||
<strong>Reason:</strong> <?php echo htmlspecialchars($app['reason']); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-top-0 d-flex justify-content-end gap-2">
|
||||
<!-- Actions -->
|
||||
<?php if ($app['status'] === 'Scheduled'): ?>
|
||||
<button class="btn btn-sm btn-outline-success" onclick="completeHomeVisit(<?php echo $app['id']; ?>, <?php echo $app['patient_id']; ?>, <?php echo $app['doctor_id'] ?: 'null'; ?>, <?php echo $app['nurse_id'] ?: 'null'; ?>)">
|
||||
<i class="bi bi-check2-circle"></i> Complete
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Complete Home Visit Modal -->
|
||||
<div class="modal fade" id="completeHomeVisitModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Complete Home Visit</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="completeVisitForm">
|
||||
<input type="hidden" id="chv_appointment_id" name="appointment_id">
|
||||
<input type="hidden" id="chv_patient_id" name="patient_id">
|
||||
<input type="hidden" id="chv_doctor_id" name="doctor_id">
|
||||
<input type="hidden" id="chv_nurse_id" name="nurse_id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notes / Treatment</label>
|
||||
<textarea class="form-control" id="chv_treatment_plan" name="treatment_plan" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="chv_create_bill" checked>
|
||||
<label class="form-check-label" for="chv_create_bill">
|
||||
Create Bill?
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" onclick="submitHomeVisitCompletion()">Save & Complete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function completeHomeVisit(aptId, patientId, doctorId, nurseId) {
|
||||
document.getElementById('chv_appointment_id').value = aptId;
|
||||
document.getElementById('chv_patient_id').value = patientId;
|
||||
document.getElementById('chv_doctor_id').value = doctorId || '';
|
||||
document.getElementById('chv_nurse_id').value = nurseId || '';
|
||||
|
||||
var modal = new bootstrap.Modal(document.getElementById('completeHomeVisitModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function submitHomeVisitCompletion() {
|
||||
var aptId = document.getElementById('chv_appointment_id').value;
|
||||
var patientId = document.getElementById('chv_patient_id').value;
|
||||
var doctorId = document.getElementById('chv_doctor_id').value;
|
||||
var nurseId = document.getElementById('chv_nurse_id').value;
|
||||
var treatment = document.getElementById('chv_treatment_plan').value;
|
||||
var createBill = document.getElementById('chv_create_bill').checked ? '1' : '0';
|
||||
|
||||
var form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = 'index.php?page=home_visits';
|
||||
|
||||
var inputs = [
|
||||
{name: 'action', value: 'record_visit'},
|
||||
{name: 'patient_id', value: patientId},
|
||||
{name: 'doctor_id', value: doctorId},
|
||||
{name: 'nurse_id', value: nurseId},
|
||||
{name: 'appointment_id', value: aptId},
|
||||
{name: 'visit_type', value: 'Home'},
|
||||
{name: 'treatment_plan', value: treatment},
|
||||
{name: 'create_bill', value: createBill}
|
||||
];
|
||||
|
||||
inputs.forEach(function(i) {
|
||||
if (i.value) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = i.name;
|
||||
input.value = i.value;
|
||||
form.appendChild(input);
|
||||
}
|
||||
});
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
</script>
|
||||
200
includes/pages/queue_ads.php
Normal file
200
includes/pages/queue_ads.php
Normal file
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
$limit = 10;
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// Count Total
|
||||
$countQuery = "SELECT COUNT(*) FROM queue_ads";
|
||||
$stmt = $db->query($countQuery);
|
||||
$totalAds = $stmt->fetchColumn();
|
||||
$totalPages = ceil($totalAds / $limit);
|
||||
|
||||
// Fetch Data
|
||||
$query = "SELECT * FROM queue_ads ORDER BY created_at DESC LIMIT $limit OFFSET $offset";
|
||||
$stmt = $db->query($query);
|
||||
$ads = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="fw-bold text-secondary"><?php echo __('queue_ads'); ?></h3>
|
||||
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addAdModal" onclick="resetAdModal()">
|
||||
<i class="bi bi-plus-lg me-1"></i> <?php echo __('add_ad'); ?>
|
||||
</button>
|
||||
</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">#</th>
|
||||
<th class="py-3"><?php echo __('ad_text_en'); ?></th>
|
||||
<th class="py-3"><?php echo __('ad_text_ar'); ?></th>
|
||||
<th class="py-3"><?php echo __('status'); ?></th>
|
||||
<th class="py-3 text-end px-4"><?php echo __('actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($ads)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-megaphone display-4 d-block mb-3"></i>
|
||||
<?php echo __('no_ads_found'); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($ads as $ad): ?>
|
||||
<tr>
|
||||
<td class="px-4 text-secondary"><?php echo $ad['id']; ?></td>
|
||||
<td class="fw-semibold text-dark text-truncate" style="max-width: 200px;" title="<?php echo htmlspecialchars($ad['text_en']); ?>"><?php echo htmlspecialchars($ad['text_en']); ?></td>
|
||||
<td class="text-secondary text-truncate" style="max-width: 200px;" title="<?php echo htmlspecialchars($ad['text_ar']); ?>"><?php echo htmlspecialchars($ad['text_ar']); ?></td>
|
||||
<td>
|
||||
<?php if ($ad['active']): ?>
|
||||
<span class="badge bg-success bg-opacity-10 text-success px-3 py-2 rounded-pill"><?php echo __('active'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-danger bg-opacity-10 text-danger px-3 py-2 rounded-pill"><?php echo __('inactive'); ?></span>
|
||||
<?php endif; ?>
|
||||
</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="showEditAdModal(<?php echo htmlspecialchars(json_encode($ad, 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="showDeleteAdModal(<?php echo $ad['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 -->
|
||||
<?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, $totalAds); ?> <?php echo __('of'); ?> <?php echo $totalAds; ?>
|
||||
</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="?page=<?php echo $page - 1; ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
||||
<li class="page-item <?php echo $page == $i ? 'active' : ''; ?>">
|
||||
<a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
|
||||
</li>
|
||||
<?php endfor; ?>
|
||||
<li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
|
||||
<a class="page-link" href="?page=<?php echo $page + 1; ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit Ad Modal -->
|
||||
<div class="modal fade" id="addAdModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title" id="adModalTitle"><?php echo __('add_ad'); ?></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="adAction" value="add_queue_ad">
|
||||
<input type="hidden" name="id" id="adId">
|
||||
<div class="modal-body p-4">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?php echo __('ad_text_en'); ?> <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" name="text_en" id="adTextEn" required rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?php echo __('ad_text_ar'); ?> <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" name="text_ar" id="adTextAr" required rows="3"></textarea>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="active" id="adActive" checked value="1">
|
||||
<label class="form-check-label" for="adActive"><?php echo __('active'); ?></label>
|
||||
</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 Ad Modal -->
|
||||
<div class="modal fade" id="deleteAdModal" 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_ad'); ?></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_queue_ad">
|
||||
<input type="hidden" name="id" id="deleteAdId">
|
||||
<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>
|
||||
function resetAdModal() {
|
||||
document.getElementById('adModalTitle').textContent = '<?php echo __('add_ad'); ?>';
|
||||
document.getElementById('adAction').value = 'add_queue_ad';
|
||||
document.getElementById('adId').value = '';
|
||||
|
||||
document.getElementById('adTextEn').value = '';
|
||||
document.getElementById('adTextAr').value = '';
|
||||
document.getElementById('adActive').checked = true;
|
||||
}
|
||||
|
||||
function showEditAdModal(ad) {
|
||||
document.getElementById('adModalTitle').textContent = '<?php echo __('edit_ad'); ?>';
|
||||
document.getElementById('adAction').value = 'edit_queue_ad';
|
||||
document.getElementById('adId').value = ad.id;
|
||||
|
||||
document.getElementById('adTextEn').value = ad.text_en;
|
||||
document.getElementById('adTextAr').value = ad.text_ar;
|
||||
document.getElementById('adActive').checked = (ad.active == 1);
|
||||
|
||||
var modal = new bootstrap.Modal(document.getElementById('addAdModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function showDeleteAdModal(id) {
|
||||
document.getElementById('deleteAdId').value = id;
|
||||
var modal = new bootstrap.Modal(document.getElementById('deleteAdModal'));
|
||||
modal.show();
|
||||
}
|
||||
</script>
|
||||
@ -14,7 +14,9 @@ if ($search_patient) {
|
||||
$params[] = "%$search_patient%";
|
||||
}
|
||||
if ($search_doctor) {
|
||||
$where .= " AND (d.name_en LIKE ? OR d.name_ar LIKE ?)";
|
||||
$where .= " AND (d.name_en LIKE ? OR d.name_ar LIKE ? OR n.name_en LIKE ? OR n.name_ar LIKE ?)";
|
||||
$params[] = "%$search_doctor%";
|
||||
$params[] = "%$search_doctor%";
|
||||
$params[] = "%$search_doctor%";
|
||||
$params[] = "%$search_doctor%";
|
||||
}
|
||||
@ -28,7 +30,8 @@ $countQuery = "
|
||||
SELECT COUNT(*)
|
||||
FROM visits v
|
||||
JOIN patients p ON v.patient_id = p.id
|
||||
JOIN doctors d ON v.doctor_id = d.id
|
||||
LEFT JOIN doctors d ON v.doctor_id = d.id
|
||||
LEFT JOIN nurses n ON v.nurse_id = n.id
|
||||
$where";
|
||||
$stmt = $db->prepare($countQuery);
|
||||
$stmt->execute($params);
|
||||
@ -37,10 +40,14 @@ $totalPages = ceil($totalVisits / $limit);
|
||||
|
||||
// Fetch Data
|
||||
$query = "
|
||||
SELECT v.*, p.name as patient_name, p.dob as patient_dob, p.gender as patient_gender, d.name_$lang as doctor_name
|
||||
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
|
||||
FROM visits v
|
||||
JOIN patients p ON v.patient_id = p.id
|
||||
JOIN doctors d ON v.doctor_id = d.id
|
||||
LEFT JOIN doctors d ON v.doctor_id = d.id
|
||||
LEFT JOIN nurses n ON v.nurse_id = n.id
|
||||
$where
|
||||
ORDER BY v.visit_date DESC
|
||||
LIMIT $limit OFFSET $offset";
|
||||
@ -83,10 +90,28 @@ if (isset($_GET['ajax_search'])) {
|
||||
<?php else: ?>
|
||||
<?php foreach ($visits as $v): ?>
|
||||
<tr>
|
||||
<td class="px-4 text-muted small"><?php echo $v['id']; ?></td>
|
||||
<td class="px-4 text-muted small">
|
||||
<?php echo $v['id']; ?>
|
||||
<?php if (($v['visit_type'] ?? 'Clinic') === 'Home'): ?>
|
||||
<br><span class="badge bg-warning text-dark"><i class="bi bi-house"></i> Home</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-secondary"><?php echo date('Y-m-d H:i', strtotime($v['visit_date'])); ?></td>
|
||||
<td class="fw-semibold text-dark"><?php echo htmlspecialchars($v['patient_name'] ?? ''); ?></td>
|
||||
<td class="text-secondary"><?php echo htmlspecialchars($v['doctor_name'] ?? ''); ?></td>
|
||||
<td class="fw-semibold text-dark">
|
||||
<?php echo htmlspecialchars($v['patient_name'] ?? ''); ?>
|
||||
<?php if (!empty($v['address'])): ?>
|
||||
<br><small class="text-muted"><i class="bi bi-geo-alt"></i> <?php echo htmlspecialchars($v['address']); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-secondary">
|
||||
<?php
|
||||
if (!empty($v['doctor_name'])) {
|
||||
echo htmlspecialchars($v['doctor_name']);
|
||||
} elseif (!empty($v['nurse_name'])) {
|
||||
echo htmlspecialchars($v['nurse_name']) . ' <small class="text-muted">(' . __('nurse') . ')</small>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$diagnosis_plain = strip_tags($v['diagnosis'] ?? '');
|
||||
@ -294,10 +319,28 @@ if (isset($_GET['ajax_search'])) {
|
||||
<?php else: ?>
|
||||
<?php foreach ($visits as $v): ?>
|
||||
<tr>
|
||||
<td class="px-4 text-muted small"><?php echo $v['id']; ?></td>
|
||||
<td class="px-4 text-muted small">
|
||||
<?php echo $v['id']; ?>
|
||||
<?php if (($v['visit_type'] ?? 'Clinic') === 'Home'): ?>
|
||||
<br><span class="badge bg-warning text-dark"><i class="bi bi-house"></i> Home</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-secondary"><?php echo date('Y-m-d H:i', strtotime($v['visit_date'])); ?></td>
|
||||
<td class="fw-semibold text-dark"><?php echo htmlspecialchars($v['patient_name'] ?? ''); ?></td>
|
||||
<td class="text-secondary"><?php echo htmlspecialchars($v['doctor_name'] ?? ''); ?></td>
|
||||
<td class="fw-semibold text-dark">
|
||||
<?php echo htmlspecialchars($v['patient_name'] ?? ''); ?>
|
||||
<?php if (!empty($v['address'])): ?>
|
||||
<br><small class="text-muted"><i class="bi bi-geo-alt"></i> <?php echo htmlspecialchars($v['address']); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-secondary">
|
||||
<?php
|
||||
if (!empty($v['doctor_name'])) {
|
||||
echo htmlspecialchars($v['doctor_name']);
|
||||
} elseif (!empty($v['nurse_name'])) {
|
||||
echo htmlspecialchars($v['nurse_name']) . ' <small class="text-muted">(' . __('nurse') . ')</small>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$diagnosis_plain = strip_tags($v['diagnosis'] ?? '');
|
||||
|
||||
48
lang.php
48
lang.php
@ -275,6 +275,11 @@ $translations = [
|
||||
'are_you_sure_delete_service' => 'Are you sure you want to delete this service?',
|
||||
'appointments_list' => 'Appointments List',
|
||||
'doctor_holidays' => 'Doctor Holidays',
|
||||
'add_holiday' => 'Add Holiday',
|
||||
'edit_holiday' => 'Edit Holiday',
|
||||
'start_date' => 'Start Date',
|
||||
'end_date' => 'End Date',
|
||||
'note' => 'Note',
|
||||
'printed_on' => 'Printed on',
|
||||
'no_appointments' => 'No appointments found for this date',
|
||||
'back' => 'Back',
|
||||
@ -296,6 +301,25 @@ $translations = [
|
||||
'any_doctor' => 'Any Doctor',
|
||||
'showing_last_50_patients' => 'Showing last 50 patients',
|
||||
'queue_display' => 'Queue Display',
|
||||
'queue_ads' => 'Queue Ads',
|
||||
'add_ad' => 'Add Ad',
|
||||
'edit_ad' => 'Edit Ad',
|
||||
'ad_text_en' => 'Ad Text (English)',
|
||||
'ad_text_ar' => 'Ad Text (Arabic)',
|
||||
'no_ads_found' => 'No ads found',
|
||||
'delete_ad' => 'Delete Ad',
|
||||
'show_in_queue' => 'Show in Queue',
|
||||
'home_visits' => 'Home Visits',
|
||||
'provider' => 'Provider',
|
||||
'nurse' => 'Nurse',
|
||||
'visit_type' => 'Visit Type',
|
||||
'clinic' => 'Clinic',
|
||||
'home' => 'Home',
|
||||
'no_appointments_found' => 'No appointments found',
|
||||
'provider_type' => 'Provider Type',
|
||||
'select_provider_type' => 'Select Provider Type',
|
||||
'select_nurse' => 'Select Nurse',
|
||||
'holiday' => 'Holiday',
|
||||
],
|
||||
'ar' => [
|
||||
'attachment' => 'المرفق',
|
||||
@ -574,6 +598,11 @@ $translations = [
|
||||
'are_you_sure_delete_service' => 'هل أنت متأكد أنك تريد حذف هذه الخدمة؟',
|
||||
'appointments_list' => 'قائمة المواعيد',
|
||||
'doctor_holidays' => 'إجازات الأطباء',
|
||||
'add_holiday' => 'إضافة إجازة',
|
||||
'edit_holiday' => 'تعديل إجازة',
|
||||
'start_date' => 'تاريخ البدء',
|
||||
'end_date' => 'تاريخ الانتهاء',
|
||||
'note' => 'ملاحظة',
|
||||
'printed_on' => 'طبع في',
|
||||
'no_appointments' => 'لا توجد مواعيد لهذا التاريخ',
|
||||
'back' => 'رجوع',
|
||||
@ -595,5 +624,24 @@ $translations = [
|
||||
'any_doctor' => 'أي طبيب',
|
||||
'showing_last_50_patients' => 'عرض آخر 50 مريض',
|
||||
'queue_display' => 'شاشة الانتظار',
|
||||
'queue_ads' => 'إعلانات الانتظار',
|
||||
'add_ad' => 'إضافة إعلان',
|
||||
'edit_ad' => 'تعديل إعلان',
|
||||
'ad_text_en' => 'نص الإعلان (إنجليزي)',
|
||||
'ad_text_ar' => 'نص الإعلان (عربي)',
|
||||
'no_ads_found' => 'لا توجد إعلانات',
|
||||
'delete_ad' => 'حذف إعلان',
|
||||
'show_in_queue' => 'العرض في شاشة الانتظار',
|
||||
'home_visits' => 'الزيارات المنزلية',
|
||||
'provider' => 'مقدم الخدمة',
|
||||
'nurse' => 'الممرضة',
|
||||
'visit_type' => 'نوع الزيارة',
|
||||
'clinic' => 'العيادة',
|
||||
'home' => 'المنزل',
|
||||
'no_appointments_found' => 'لم يتم العثور على مواعيد',
|
||||
'provider_type' => 'نوع مقدم الخدمة',
|
||||
'select_provider_type' => 'اختر نوع مقدم الخدمة',
|
||||
'select_nurse' => 'اختر الممرضة',
|
||||
'holiday' => 'إجازة',
|
||||
]
|
||||
];
|
||||
|
||||
13
queue_ads.php
Normal file
13
queue_ads.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$section = 'queue_ads';
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
|
||||
$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/queue_ads.php';
|
||||
require_once __DIR__ . '/includes/layout/footer.php';
|
||||
@ -6,9 +6,13 @@ $lang = 'en';
|
||||
// Fetch initial data to render skeletal HTML
|
||||
try {
|
||||
$db = db();
|
||||
$departments = $db->query("SELECT * FROM departments")->fetchAll(PDO::FETCH_ASSOC);
|
||||
// Use raw query instead of fetching all to handle potential empty tables gracefully if needed,
|
||||
// though here we just get all departments.
|
||||
$departments = $db->query("SELECT * FROM departments WHERE show_in_queue = 1")->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Exception $e) {
|
||||
die("Database error");
|
||||
// If DB fails, we can't show much, but let's not crash the whole page if possible.
|
||||
// die("Database error");
|
||||
$departments = [];
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
@ -25,6 +29,7 @@ try {
|
||||
background-color: #f0f2f5;
|
||||
font-family: 'Tajawal', sans-serif;
|
||||
overflow: hidden; /* Hide scrollbars for TV feel */
|
||||
padding-bottom: 60px; /* Space for marquee */
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
||||
@ -131,6 +136,31 @@ try {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
/* Marquee Footer Styles */
|
||||
.footer-marquee {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: #002D62;
|
||||
color: white;
|
||||
padding: 10px 0;
|
||||
font-size: 1.5rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 -2px 5px rgba(0,0,0,0.2);
|
||||
border-top: 3px solid #fbbc04; /* Accent color */
|
||||
}
|
||||
.marquee-content {
|
||||
display: inline-block;
|
||||
padding-left: 100%;
|
||||
animation: marquee 25s linear infinite;
|
||||
}
|
||||
@keyframes marquee {
|
||||
0% { transform: translate(0, 0); }
|
||||
100% { transform: translate(-100%, 0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -146,6 +176,13 @@ try {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer Marquee -->
|
||||
<div class="footer-marquee">
|
||||
<div class="marquee-content" id="marqueeContent">
|
||||
<span class="mx-3">Welcome to our hospital / أهلاً بكم في مستشفانا</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function updateClock() {
|
||||
@ -254,10 +291,32 @@ try {
|
||||
});
|
||||
}
|
||||
|
||||
function fetchAds() {
|
||||
fetch('api/queue.php?action=get_ads')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.data && data.data.length > 0) {
|
||||
const adsContent = data.data.map(ad =>
|
||||
`<span class="mx-5">${ad.text_en} - ${ad.text_ar}</span>`
|
||||
).join('<i class="bi bi-star-fill mx-3 text-warning"></i>');
|
||||
|
||||
document.getElementById('marqueeContent').innerHTML = adsContent;
|
||||
} else {
|
||||
// Fallback if no ads
|
||||
document.getElementById('marqueeContent').innerHTML = '<span class="mx-3">Welcome to our hospital / أهلاً بكم في مستشفانا</span>';
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Ads fetch error:', err));
|
||||
}
|
||||
|
||||
// Initial load
|
||||
fetchQueueStatus();
|
||||
// Poll every 5 seconds
|
||||
fetchAds();
|
||||
|
||||
// Poll every 5 seconds for queue
|
||||
setInterval(fetchQueueStatus, 5000);
|
||||
// Poll every 60 seconds for ads
|
||||
setInterval(fetchAds, 60000);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
5
test_api_appointments.php
Normal file
5
test_api_appointments.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_GET['start'] = '2026-03-15';
|
||||
$_GET['end'] = '2026-03-22';
|
||||
require_once 'api/appointments.php';
|
||||
28
test_create_appointment_curl.php
Normal file
28
test_create_appointment_curl.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
$data = [
|
||||
'action' => 'create',
|
||||
'patient_id' => 2,
|
||||
'doctor_id' => 1,
|
||||
'nurse_id' => null,
|
||||
'visit_type' => 'Clinic',
|
||||
'address' => '',
|
||||
'start_time' => '2026-03-25T10:00',
|
||||
'reason' => 'Test API'
|
||||
];
|
||||
|
||||
$ch = curl_init('http://localhost/api/appointments.php');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($response === false) {
|
||||
echo "Curl Error: " . curl_error($ch);
|
||||
} else {
|
||||
echo "HTTP Code: $http_code\n";
|
||||
echo "Response: $response\n";
|
||||
}
|
||||
curl_close($ch);
|
||||
Loading…
x
Reference in New Issue
Block a user