Autosave: 20260317-082239

This commit is contained in:
Flatlogic Bot 2026-03-17 08:22:39 +00:00
parent 2575e8e91e
commit 9a2dea5596
25 changed files with 1286 additions and 111 deletions

View File

@ -6,7 +6,7 @@ header('Content-Type: application/json');
$db = db(); $db = db();
$lang = $_SESSION['lang'] ?? 'en'; $lang = $_SESSION['lang'] ?? 'en';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') { if ($method === 'GET') {
$id = $_GET['id'] ?? null; $id = $_GET['id'] ?? null;
@ -30,12 +30,14 @@ if ($method === 'GET') {
$query = " $query = "
SELECT SELECT
a.id, a.start_time as start, a.end_time as end, a.reason, a.status, 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, 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 FROM appointments a
JOIN patients p ON a.patient_id = p.id 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"; WHERE 1=1";
$params = []; $params = [];
@ -43,18 +45,30 @@ if ($method === 'GET') {
if ($endStr) { $query .= " AND a.start_time <= ?"; $params[] = $endStr; } if ($endStr) { $query .= " AND a.start_time <= ?"; $params[] = $endStr; }
if ($doctor_id) { $query .= " AND a.doctor_id = ?"; $params[] = $doctor_id; } if ($doctor_id) { $query .= " AND a.doctor_id = ?"; $params[] = $doctor_id; }
$stmt = $db->prepare($query); try {
$stmt->execute($params); $stmt = $db->prepare($query);
$appointments = $stmt->fetchAll(PDO::FETCH_ASSOC); $stmt->execute($params);
$appointments = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Appointments Fetch Error: " . $e->getMessage());
$appointments = [];
}
foreach ($appointments as $a) { foreach ($appointments as $a) {
$color = '#0d6efd'; // blue $color = '#0d6efd'; // blue
if ($a['status'] === 'Completed') $color = '#198754'; // green if ($a['status'] === 'Completed') $color = '#198754'; // green
if ($a['status'] === 'Cancelled') $color = '#dc3545'; // red 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[] = [ $events[] = [
'id' => $a['id'], 'id' => $a['id'],
'title' => $a['patient_name'] . ' (' . $a['doctor_name'] . ')', 'title' => $title,
'start' => $a['start'], 'start' => $a['start'],
'end' => $a['end'], 'end' => $a['end'],
'color' => $color, 'color' => $color,
@ -62,8 +76,12 @@ if ($method === 'GET') {
'type' => 'appointment', 'type' => 'appointment',
'patient_id' => $a['patient_id'], 'patient_id' => $a['patient_id'],
'doctor_id' => $a['doctor_id'], 'doctor_id' => $a['doctor_id'],
'nurse_id' => $a['nurse_id'],
'visit_type' => $a['visit_type'],
'address' => $a['address'],
'patient_name' => $a['patient_name'], 'patient_name' => $a['patient_name'],
'doctor_name' => $a['doctor_name'], 'doctor_name' => $a['doctor_name'],
'nurse_name' => $a['nurse_name'],
'status' => $a['status'], 'status' => $a['status'],
'reason' => $a['reason'] 'reason' => $a['reason']
] ]
@ -76,9 +94,17 @@ if ($method === 'GET') {
if ($startStr) { $holidayQuery .= " AND holiday_date >= ?"; $holidayParams[] = date('Y-m-d', strtotime($startStr)); } 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)); } if ($endStr) { $holidayQuery .= " AND holiday_date <= ?"; $holidayParams[] = date('Y-m-d', strtotime($endStr)); }
$stmt = $db->prepare($holidayQuery); try {
$stmt->execute($holidayParams); $stmt = $db->prepare($holidayQuery);
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC); $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) { foreach ($holidays as $h) {
// Render a visible block event in the time grid // Render a visible block event in the time grid
@ -134,9 +160,13 @@ if ($method === 'GET') {
$docHolidayParams[] = $doctor_id; $docHolidayParams[] = $doctor_id;
} }
$stmt = $db->prepare($docHolidayQuery); try {
$stmt->execute($docHolidayParams); $stmt = $db->prepare($docHolidayQuery);
$docHolidays = $stmt->fetchAll(PDO::FETCH_ASSOC); $stmt->execute($docHolidayParams);
$docHolidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$docHolidays = [];
}
foreach ($docHolidays as $dh) { foreach ($docHolidays as $dh) {
$title = $dh['doctor_name'] . ' - ' . ($dh['note'] ?: 'Holiday'); $title = $dh['doctor_name'] . ' - ' . ($dh['note'] ?: 'Holiday');
@ -145,21 +175,12 @@ if ($method === 'GET') {
$currentDate = strtotime($dh['start_date']); $currentDate = strtotime($dh['start_date']);
$endDate = strtotime($dh['end_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) { while ($currentDate <= $endDate) {
$dateStr = date('Y-m-d', $currentDate); $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[] = [ $events[] = [
'id' => 'doc_hol_block_' . $dh['id'] . '_' . $dateStr, 'id' => 'doc_hol_block_' . $dh['id'] . '_' . $dateStr,
'title' => 'Holiday: ' . $dh['doctor_name'], '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] 'extendedProps' => ['type' => 'doctor_holiday_block', 'doctor_id' => $dh['doctor_id'], 'blocks_selection' => $isFilteredDoctor]
]; ];
// Keep the background shading
$events[] = [ $events[] = [
'id' => 'doc_hol_bg_' . $dh['id'] . '_' . $dateStr, 'id' => 'doc_hol_bg_' . $dh['id'] . '_' . $dateStr,
'start' => $dateStr . 'T00:00:00', 'start' => $dateStr . 'T00:00:00',
@ -181,13 +201,12 @@ if ($method === 'GET') {
'allDay' => false, 'allDay' => false,
'backgroundColor' => $isFilteredDoctor ? 'rgba(255, 193, 7, 0.5)' : 'rgba(255, 193, 7, 0.15)', 'backgroundColor' => $isFilteredDoctor ? 'rgba(255, 193, 7, 0.5)' : 'rgba(255, 193, 7, 0.15)',
'overlap' => !$isFilteredDoctor, '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); $currentDate = strtotime('+1 day', $currentDate);
} }
// Visible event strip across the top (allDay)
$events[] = [ $events[] = [
'id' => 'doc_hol_' . $dh['id'], 'id' => 'doc_hol_' . $dh['id'],
'title' => $title, 'title' => $title,
@ -220,7 +239,6 @@ if ($method === 'GET') {
} }
$businessHours = array_values($bhMap); $businessHours = array_values($bhMap);
} else { } else {
$s = get_system_settings();
$st = $s['working_hours_start'] ?? '08:00'; $st = $s['working_hours_start'] ?? '08:00';
$et = $s['working_hours_end'] ?? '17:00'; $et = $s['working_hours_end'] ?? '17:00';
$businessHours = [ $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([ echo json_encode([
'events' => $events, 'events' => $events,
'businessHours' => $businessHours, 'businessHours' => $businessHours,
@ -250,64 +264,103 @@ if ($method === 'GET') {
function checkDoctorHoliday($db, $doctor_id, $start_time) { function checkDoctorHoliday($db, $doctor_id, $start_time) {
if (!$doctor_id || !$start_time) return false; if (!$doctor_id || !$start_time) return false;
$date = date('Y-m-d', strtotime($start_time)); $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"); try {
$stmt->execute([$doctor_id, $date]); $stmt = $db->prepare("SELECT COUNT(*) FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
return $stmt->fetchColumn() > 0; $stmt->execute([$doctor_id, $date]);
return $stmt->fetchColumn() > 0;
} catch (PDOException $e) {
error_log("Check Holiday Error: " . $e->getMessage());
return false;
}
} }
if ($method === 'POST') { 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'] ?? ''; $action = $input['action'] ?? '';
if (empty($action)) {
echo json_encode(['success' => false, 'error' => 'No action specified']);
exit;
}
if ($action === 'create') { if ($action === 'create') {
$patient_id = $input['patient_id'] ?? ''; $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'] ?? ''; $start_time = $input['start_time'] ?? '';
$reason = $input['reason'] ?? ''; $reason = $input['reason'] ?? '';
if ($patient_id && $doctor_id && $start_time) { if ($patient_id && ($doctor_id || $nurse_id) && $start_time) {
// Check for holiday conflict // Check for holiday conflict if doctor assigned
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.']); echo json_encode(['success' => false, 'error' => 'Doctor is on holiday on this date.']);
exit; exit;
} }
$stmt = $db->prepare("INSERT INTO appointments (patient_id, doctor_id, start_time, end_time, reason) VALUES (?, ?, ?, DATE_ADD(?, INTERVAL 30 MINUTE), ?)"); try {
$stmt->execute([$patient_id, $doctor_id, $start_time, $start_time, $reason]); $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), ?)");
echo json_encode(['success' => true, 'id' => $db->lastInsertId()]); $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 { } else {
echo json_encode(['success' => false, 'error' => 'Missing fields']); echo json_encode(['success' => false, 'error' => 'Missing fields']);
} }
} elseif ($action === 'update') { } elseif ($action === 'update') {
$id = $input['id'] ?? ''; $id = $input['id'] ?? '';
$patient_id = $input['patient_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'] ?? ''; $start_time = $input['start_time'] ?? '';
$status = $input['status'] ?? 'Scheduled'; $status = $input['status'] ?? 'Scheduled';
$reason = $input['reason'] ?? ''; $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 // 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.']); echo json_encode(['success' => false, 'error' => 'Doctor is on holiday on this date.']);
exit; exit;
} }
$stmt = $db->prepare("UPDATE appointments SET patient_id = ?, doctor_id = ?, start_time = ?, end_time = DATE_ADD(?, INTERVAL 30 MINUTE), status = ?, reason = ? WHERE id = ?"); try {
$stmt->execute([$patient_id, $doctor_id, $start_time, $start_time, $status, $reason, $id]); $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 = ?");
echo json_encode(['success' => true]); $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 { } else {
echo json_encode(['success' => false, 'error' => 'Missing fields']); echo json_encode(['success' => false, 'error' => 'Missing fields']);
} }
} elseif ($action === 'delete') { } elseif ($action === 'delete') {
$id = $input['id'] ?? ''; $id = $input['id'] ?? '';
if ($id) { if ($id) {
$stmt = $db->prepare("DELETE FROM appointments WHERE id = ?"); try {
$stmt->execute([$id]); $stmt = $db->prepare("DELETE FROM appointments WHERE id = ?");
echo json_encode(['success' => true]); $stmt->execute([$id]);
echo json_encode(['success' => true]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'error' => 'DB Error: ' . $e->getMessage()]);
}
} else { } else {
echo json_encode(['success' => false, 'error' => 'Missing ID']); echo json_encode(['success' => false, 'error' => 'Missing ID']);
} }
} else {
echo json_encode(['success' => false, 'error' => 'Invalid action']);
} }
exit; exit;
} }

View File

@ -171,6 +171,24 @@ try {
exit; 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'); throw new Exception('Invalid action');
} catch (Exception $e) { } catch (Exception $e) {

View 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
View 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";
}

View 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
View 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);

View File

@ -0,0 +1 @@
ALTER TABLE departments ADD COLUMN show_in_queue BOOLEAN DEFAULT 1;

View 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
);

View 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;

View 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
View 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
View 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';
?>

View File

@ -223,9 +223,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} elseif ($_POST['action'] === 'add_department') { } elseif ($_POST['action'] === 'add_department') {
$name_en = $_POST['name_en'] ?? ''; $name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? ''; $name_ar = $_POST['name_ar'] ?? '';
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
if ($name_en && $name_ar) { if ($name_en && $name_ar) {
$stmt = $db->prepare("INSERT INTO departments (name_en, name_ar) VALUES (?, ?)"); $stmt = $db->prepare("INSERT INTO departments (name_en, name_ar, show_in_queue) VALUES (?, ?, ?)");
$stmt->execute([$name_en, $name_ar]); $stmt->execute([$name_en, $name_ar, $show_in_queue]);
$_SESSION['flash_message'] = __('add_department') . ' ' . __('successfully'); $_SESSION['flash_message'] = __('add_department') . ' ' . __('successfully');
$redirect = true; $redirect = true;
} }
@ -233,9 +234,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'] ?? ''; $id = $_POST['id'] ?? '';
$name_en = $_POST['name_en'] ?? ''; $name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? ''; $name_ar = $_POST['name_ar'] ?? '';
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
if ($id && $name_en && $name_ar) { if ($id && $name_en && $name_ar) {
$stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ? WHERE id = ?"); $stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ?, show_in_queue = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $id]); $stmt->execute([$name_en, $name_ar, $show_in_queue, $id]);
$_SESSION['flash_message'] = __('edit_department') . ' ' . __('successfully'); $_SESSION['flash_message'] = __('edit_department') . ' ' . __('successfully');
$redirect = true; $redirect = true;
} }
@ -288,7 +290,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} elseif ($_POST['action'] === 'record_visit') { } elseif ($_POST['action'] === 'record_visit') {
$patient_id = $_POST['patient_id'] ?? ''; $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; $appointment_id = $_POST['appointment_id'] ?: null;
$weight = $_POST['weight'] ?? ''; $weight = $_POST['weight'] ?? '';
$bp = $_POST['blood_pressure'] ?? ''; $bp = $_POST['blood_pressure'] ?? '';
@ -298,15 +302,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$diagnosis = $_POST['diagnosis'] ?? ''; $diagnosis = $_POST['diagnosis'] ?? '';
$treatment = $_POST['treatment_plan'] ?? ''; $treatment = $_POST['treatment_plan'] ?? '';
if ($patient_id && $doctor_id) { if ($patient_id && ($doctor_id || $nurse_id)) {
$db->beginTransaction(); $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(); $visit_id = $db->lastInsertId();
$token_message = ''; $token_message = '';
// Token Generation // Token Generation (Only for Doctor visits in Clinic usually)
if (isset($_POST['generate_token']) && $_POST['generate_token'] == '1') { if (isset($_POST['generate_token']) && $_POST['generate_token'] == '1' && $doctor_id) {
$stmtDoc = $db->prepare("SELECT department_id FROM doctors WHERE id = ?"); $stmtDoc = $db->prepare("SELECT department_id FROM doctors WHERE id = ?");
$stmtDoc->execute([$doctor_id]); $stmtDoc->execute([$doctor_id]);
$docData = $stmtDoc->fetch(); $docData = $stmtDoc->fetch();
@ -341,6 +356,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $db->prepare("UPDATE appointments SET status = 'Completed' WHERE id = ?"); $stmt = $db->prepare("UPDATE appointments SET status = 'Completed' WHERE id = ?");
$stmt->execute([$appointment_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(); $db->commit();
$_SESSION['flash_message'] = __('add_visit') . ' ' . __('successfully') . $token_message; $_SESSION['flash_message'] = __('add_visit') . ' ' . __('successfully') . $token_message;
$redirect = true; $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]); $stmt->execute([$name_en, $name_ar, $group_id, $price, $expiry, $supplier_id]);
} }
} }
@ -1083,6 +1099,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$redirect = true; $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;
}
} }
} }

View File

@ -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="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="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="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="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"> <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="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="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="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="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> <span><i class="bi bi-gear me-2"></i> <?php echo __('settings'); ?></span>
<i class="bi bi-chevron-down small"></i> <i class="bi bi-chevron-down small"></i>
</a> </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"> <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="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> <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="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="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="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>
</div> </div>
</nav> </nav>

View File

@ -41,6 +41,10 @@
<div class="badge bg-danger me-2" style="width: 15px; height: 15px; border-radius: 50%;">&nbsp;</div> <div class="badge bg-danger me-2" style="width: 15px; height: 15px; border-radius: 50%;">&nbsp;</div>
<small class="text-muted">Cancelled</small> <small class="text-muted">Cancelled</small>
</div> </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;">&nbsp;</div>
<small class="text-muted">Home Visit</small>
</div>
<div class="d-flex align-items-center mb-2"> <div class="d-flex align-items-center mb-2">
<div class="badge bg-warning me-2" style="width: 15px; height: 15px; border-radius: 50%;">&nbsp;</div> <div class="badge bg-warning me-2" style="width: 15px; height: 15px; border-radius: 50%;">&nbsp;</div>
<small class="text-muted">Holiday</small> <small class="text-muted">Holiday</small>
@ -71,18 +75,53 @@
<label class="form-label small text-muted"><?php echo __('patient'); ?></label> <label class="form-label small text-muted"><?php echo __('patient'); ?></label>
<select id="apt_patient_id" class="form-select select2-modal-apt"> <select id="apt_patient_id" class="form-select select2-modal-apt">
<?php foreach ($all_patients as $p): ?> <?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; ?> <?php endforeach; ?>
</select> </select>
</div> </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> <label class="form-label small text-muted"><?php echo __('doctor'); ?></label>
<select id="apt_doctor_id" class="form-select select2-modal-apt"> <select id="apt_doctor_id" class="form-select select2-modal-apt">
<option value="">Select Doctor</option>
<?php foreach ($all_doctors as $d): ?> <?php foreach ($all_doctors as $d): ?>
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option> <option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </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="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label small text-muted"><?php echo __('date'); ?> & <?php echo __('time'); ?></label> <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) { function showCreateModal(startTime = null) {
document.getElementById('modalTitle').innerText = '<?php echo __('book_appointment'); ?>'; document.getElementById('modalTitle').innerText = '<?php echo __('book_appointment'); ?>';
document.getElementById('apt_id').value = ''; document.getElementById('apt_id').value = '';
document.getElementById('apt_reason').value = ''; document.getElementById('apt_reason').value = '';
document.getElementById('apt_status').value = 'Scheduled'; 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'; document.getElementById('btnDeleteApt').style.display = 'none';
if (startTime) { if (startTime && startTime instanceof Date) {
var offset = startTime.getTimezoneOffset() * 60000; var offset = startTime.getTimezoneOffset() * 60000;
var localISOTime = (new Date(startTime.getTime() - offset)).toISOString().slice(0, 16); var localISOTime = (new Date(startTime.getTime() - offset)).toISOString().slice(0, 16);
document.getElementById('apt_start_time').value = localISOTime; document.getElementById('apt_start_time').value = localISOTime;
} else { } 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) { if (document.getElementById('doctorFilter').value) {
@ -161,11 +249,16 @@ function showCreateModal(startTime = null) {
function saveAppointment() { function saveAppointment() {
var id = document.getElementById('apt_id').value; var id = document.getElementById('apt_id').value;
var providerType = document.getElementById('apt_provider_type').value;
var data = { var data = {
action: id ? 'update' : 'create', action: id ? 'update' : 'create',
id: id, id: id,
patient_id: document.getElementById('apt_patient_id').value, 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, start_time: document.getElementById('apt_start_time').value,
status: document.getElementById('apt_status').value, status: document.getElementById('apt_status').value,
reason: document.getElementById('apt_reason').value reason: document.getElementById('apt_reason').value
@ -176,7 +269,12 @@ function saveAppointment() {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data) 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 => { .then(result => {
if (result.success) { if (result.success) {
var modalEl = document.getElementById('appointmentDetailsModal'); var modalEl = document.getElementById('appointmentDetailsModal');
@ -186,6 +284,10 @@ function saveAppointment() {
} else { } else {
alert('Error: ' + (result.error || 'Unknown error')); 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' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete', id: id }) 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 => { .then(result => {
if (result.success) { if (result.success) {
var modalEl = document.getElementById('appointmentDetailsModal'); var modalEl = document.getElementById('appointmentDetailsModal');
@ -208,6 +315,10 @@ function deleteAppointment() {
} else { } else {
alert('Error: ' + (result.error || 'Unknown error')); 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, businessHours: true,
events: function(fetchInfo, successCallback, failureCallback) { events: function(fetchInfo, successCallback, failureCallback) {
fetch('api/appointments.php?start=' + fetchInfo.startStr + '&end=' + fetchInfo.endStr + '&doctor_id=' + doctorFilter.value) 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 => { .then(data => {
// We DO NOT setOption('businessHours') here to prevent FullCalendar from re-rendering and clearing events. // We DO NOT setOption('businessHours') here to prevent FullCalendar from re-rendering and clearing events.
successCallback(data.events); successCallback(data.events);
@ -270,7 +386,22 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('modalTitle').innerText = 'Edit Appointment'; document.getElementById('modalTitle').innerText = 'Edit Appointment';
document.getElementById('apt_id').value = info.event.id; document.getElementById('apt_id').value = info.event.id;
$('#apt_patient_id').val(props.patient_id).trigger('change'); $('#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 start = info.event.start;
var offset = start.getTimezoneOffset() * 60000; var offset = start.getTimezoneOffset() * 60000;
@ -300,10 +431,32 @@ document.addEventListener('DOMContentLoaded', function() {
validateHolidayFrontend(); 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_doctor_id').on('change', validateHolidayFrontend);
$('#apt_start_time').on('change', validateHolidayFrontend); $('#apt_start_time').on('change', validateHolidayFrontend);
$('#apt_provider_type').on('change', validateHolidayFrontend); // Re-validate when provider changes
function validateHolidayFrontend() { 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 docId = $('#apt_doctor_id').val();
var startTimeStr = $('#apt_start_time').val(); var startTimeStr = $('#apt_start_time').val();
var btnSave = document.getElementById('btnSaveApt'); var btnSave = document.getElementById('btnSaveApt');

View File

@ -32,7 +32,7 @@ if (isset($_GET['ajax_search'])) {
if (empty($departments)): if (empty($departments)):
?> ?>
<tr> <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> <i class="bi bi-diagram-3 display-4 d-block mb-3"></i>
<?php echo __('no_departments_found'); ?> <?php echo __('no_departments_found'); ?>
</td> </td>
@ -43,6 +43,13 @@ if (isset($_GET['ajax_search'])) {
<td class="px-4 text-secondary"><?php echo $dept['id']; ?></td> <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="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-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"> <td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white"> <div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end" <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="px-4 py-3">#</th>
<th class="py-3"><?php echo __('name_en'); ?></th> <th class="py-3"><?php echo __('name_en'); ?></th>
<th class="py-3"><?php echo __('name_ar'); ?></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> <th class="py-3 text-end px-4"><?php echo __('actions'); ?></th>
</tr> </tr>
</thead> </thead>
<tbody id="departmentsTableBody"> <tbody id="departmentsTableBody">
<?php if (empty($departments)): ?> <?php if (empty($departments)): ?>
<tr> <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> <i class="bi bi-diagram-3 display-4 d-block mb-3"></i>
<?php echo __('no_departments_found'); ?> <?php echo __('no_departments_found'); ?>
</td> </td>
@ -167,6 +175,13 @@ if (isset($_GET['ajax_search'])) {
<td class="px-4 text-secondary"><?php echo $dept['id']; ?></td> <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="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-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"> <td class="text-end px-4">
<div class="btn-group shadow-sm border rounded bg-white"> <div class="btn-group shadow-sm border rounded bg-white">
<button class="btn btn-link text-primary py-1 px-2 border-end" <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> <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> <input type="text" class="form-control" name="name_ar" id="deptNameAr" required>
</div> </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>
<div class="modal-footer bg-light"> <div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button> <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('deptNameEn').value = '';
document.getElementById('deptNameAr').value = ''; document.getElementById('deptNameAr').value = '';
document.getElementById('deptShowInQueue').checked = true;
} }
function showEditDepartmentModal(dept) { function showEditDepartmentModal(dept) {
@ -374,6 +394,7 @@ function showEditDepartmentModal(dept) {
document.getElementById('deptNameEn').value = dept.name_en; document.getElementById('deptNameEn').value = dept.name_en;
document.getElementById('deptNameAr').value = dept.name_ar; document.getElementById('deptNameAr').value = dept.name_ar;
document.getElementById('deptShowInQueue').checked = (dept.show_in_queue == 1);
var modal = new bootstrap.Modal(document.getElementById('addDepartmentModal')); var modal = new bootstrap.Modal(document.getElementById('addDepartmentModal'));
modal.show(); modal.show();

View 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>

View 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>

View 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">&laquo;</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">&raquo;</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>

View File

@ -14,7 +14,9 @@ if ($search_patient) {
$params[] = "%$search_patient%"; $params[] = "%$search_patient%";
} }
if ($search_doctor) { 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%";
$params[] = "%$search_doctor%"; $params[] = "%$search_doctor%";
} }
@ -28,7 +30,8 @@ $countQuery = "
SELECT COUNT(*) SELECT COUNT(*)
FROM visits v FROM visits v
JOIN patients p ON v.patient_id = p.id 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"; $where";
$stmt = $db->prepare($countQuery); $stmt = $db->prepare($countQuery);
$stmt->execute($params); $stmt->execute($params);
@ -37,10 +40,14 @@ $totalPages = ceil($totalVisits / $limit);
// Fetch Data // Fetch Data
$query = " $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 FROM visits v
JOIN patients p ON v.patient_id = p.id 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 $where
ORDER BY v.visit_date DESC ORDER BY v.visit_date DESC
LIMIT $limit OFFSET $offset"; LIMIT $limit OFFSET $offset";
@ -83,10 +90,28 @@ if (isset($_GET['ajax_search'])) {
<?php else: ?> <?php else: ?>
<?php foreach ($visits as $v): ?> <?php foreach ($visits as $v): ?>
<tr> <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="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="fw-semibold text-dark">
<td class="text-secondary"><?php echo htmlspecialchars($v['doctor_name'] ?? ''); ?></td> <?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> <td>
<?php <?php
$diagnosis_plain = strip_tags($v['diagnosis'] ?? ''); $diagnosis_plain = strip_tags($v['diagnosis'] ?? '');
@ -294,10 +319,28 @@ if (isset($_GET['ajax_search'])) {
<?php else: ?> <?php else: ?>
<?php foreach ($visits as $v): ?> <?php foreach ($visits as $v): ?>
<tr> <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="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="fw-semibold text-dark">
<td class="text-secondary"><?php echo htmlspecialchars($v['doctor_name'] ?? ''); ?></td> <?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> <td>
<?php <?php
$diagnosis_plain = strip_tags($v['diagnosis'] ?? ''); $diagnosis_plain = strip_tags($v['diagnosis'] ?? '');

View File

@ -275,6 +275,11 @@ $translations = [
'are_you_sure_delete_service' => 'Are you sure you want to delete this service?', 'are_you_sure_delete_service' => 'Are you sure you want to delete this service?',
'appointments_list' => 'Appointments List', 'appointments_list' => 'Appointments List',
'doctor_holidays' => 'Doctor Holidays', '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', 'printed_on' => 'Printed on',
'no_appointments' => 'No appointments found for this date', 'no_appointments' => 'No appointments found for this date',
'back' => 'Back', 'back' => 'Back',
@ -296,6 +301,25 @@ $translations = [
'any_doctor' => 'Any Doctor', 'any_doctor' => 'Any Doctor',
'showing_last_50_patients' => 'Showing last 50 patients', 'showing_last_50_patients' => 'Showing last 50 patients',
'queue_display' => 'Queue Display', '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' => [ 'ar' => [
'attachment' => 'المرفق', 'attachment' => 'المرفق',
@ -574,6 +598,11 @@ $translations = [
'are_you_sure_delete_service' => 'هل أنت متأكد أنك تريد حذف هذه الخدمة؟', 'are_you_sure_delete_service' => 'هل أنت متأكد أنك تريد حذف هذه الخدمة؟',
'appointments_list' => 'قائمة المواعيد', 'appointments_list' => 'قائمة المواعيد',
'doctor_holidays' => 'إجازات الأطباء', 'doctor_holidays' => 'إجازات الأطباء',
'add_holiday' => 'إضافة إجازة',
'edit_holiday' => 'تعديل إجازة',
'start_date' => 'تاريخ البدء',
'end_date' => 'تاريخ الانتهاء',
'note' => 'ملاحظة',
'printed_on' => 'طبع في', 'printed_on' => 'طبع في',
'no_appointments' => 'لا توجد مواعيد لهذا التاريخ', 'no_appointments' => 'لا توجد مواعيد لهذا التاريخ',
'back' => 'رجوع', 'back' => 'رجوع',
@ -595,5 +624,24 @@ $translations = [
'any_doctor' => 'أي طبيب', 'any_doctor' => 'أي طبيب',
'showing_last_50_patients' => 'عرض آخر 50 مريض', 'showing_last_50_patients' => 'عرض آخر 50 مريض',
'queue_display' => 'شاشة الانتظار', '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
View 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';

View File

@ -6,9 +6,13 @@ $lang = 'en';
// Fetch initial data to render skeletal HTML // Fetch initial data to render skeletal HTML
try { try {
$db = db(); $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) { } 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> <!DOCTYPE html>
@ -25,6 +29,7 @@ try {
background-color: #f0f2f5; background-color: #f0f2f5;
font-family: 'Tajawal', sans-serif; font-family: 'Tajawal', sans-serif;
overflow: hidden; /* Hide scrollbars for TV feel */ overflow: hidden; /* Hide scrollbars for TV feel */
padding-bottom: 60px; /* Space for marquee */
} }
.header { .header {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
@ -131,6 +136,31 @@ try {
font-weight: bold; font-weight: bold;
color: white; 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> </style>
</head> </head>
<body> <body>
@ -146,6 +176,13 @@ try {
</div> </div>
</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> <script>
function updateClock() { 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 // Initial load
fetchQueueStatus(); fetchQueueStatus();
// Poll every 5 seconds fetchAds();
// Poll every 5 seconds for queue
setInterval(fetchQueueStatus, 5000); setInterval(fetchQueueStatus, 5000);
// Poll every 60 seconds for ads
setInterval(fetchAds, 60000);
</script> </script>

View 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';

View 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);