diff --git a/api/patients.php b/api/patients.php
index 6674cc6..e7e5379 100644
--- a/api/patients.php
+++ b/api/patients.php
@@ -9,20 +9,36 @@ try {
switch ($action) {
case 'search':
$q = $_GET['q'] ?? '';
- // Allow empty search to return nothing or some default?
- // Select2 usually sends a query.
+
if (empty($q)) {
- echo json_encode([]);
+ echo json_encode(['results' => []]);
exit;
}
- // Search by name or phone
- $sql = "SELECT id, name_en as name, phone FROM patients WHERE name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ? LIMIT 20";
+ // Search by name, phone or id (patient number)
+ $sql = "SELECT id, name, phone, civil_id FROM patients WHERE name LIKE ? OR phone LIKE ? OR civil_id LIKE ? OR id = ? LIMIT 20";
$stmt = $pdo->prepare($sql);
$term = "%$q%";
- $stmt->execute([$term, $term, $term]);
+ $id_term = intval($q); // for exact match on patient number
+ $stmt->execute([$term, $term, $term, $id_term]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
- echo json_encode($results);
+
+ // Format results for select2
+ $formatted_results = array_map(function($p) {
+ $patient_number = sprintf('%06d', $p['id']);
+ $display_text = $patient_number . ' - ' . $p['name'];
+ if (!empty($p['phone'])) {
+ $display_text .= ' - ' . $p['phone'];
+ }
+ return [
+ 'id' => $p['id'],
+ 'text' => $display_text,
+ 'name' => $p['name'],
+ 'phone' => $p['phone']
+ ];
+ }, $results);
+
+ echo json_encode(['results' => $formatted_results]);
break;
default:
@@ -32,4 +48,4 @@ try {
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
-}
\ No newline at end of file
+}
diff --git a/api/queue.php b/api/queue.php
index 9b350a0..d96334a 100644
--- a/api/queue.php
+++ b/api/queue.php
@@ -74,7 +74,7 @@ try {
}
$sql = "
- SELECT q.*,
+ SELECT q.*, td.name_$lang as target_department_name,
p.name as patient_name,
d.name_$lang as doctor_name,
d.name_en as doctor_name_en,
@@ -86,6 +86,7 @@ try {
FROM patient_queue q
JOIN patients p ON q.patient_id = p.id
JOIN departments dept ON q.department_id = dept.id
+ LEFT JOIN departments td ON q.target_department_id = td.id
LEFT JOIN employees d ON q.doctor_id = d.id
$where
ORDER BY
@@ -138,6 +139,57 @@ try {
exit;
}
+
+ // --- TRANSFER TOKEN ---
+ if ($action === 'transfer') {
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+ throw new Exception('Invalid request method');
+ }
+
+ $queue_id = $_POST['queue_id'] ?? null;
+ $new_department_id = $_POST['department_id'] ?? null;
+ $new_doctor_id = $_POST['doctor_id'] ?? null;
+
+ if (!$queue_id || !$new_department_id) {
+ throw new Exception('Queue ID and Target Department are required');
+ }
+
+ // Get current queue token
+ $stmt = $db->prepare("SELECT patient_id FROM patient_queue WHERE id = ?");
+ $stmt->execute([$queue_id]);
+ $current = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if (!$current) {
+ throw new Exception('Queue token not found');
+ }
+
+ // Complete the old token
+ $stmt = $db->prepare("UPDATE patient_queue SET status = 'completed', updated_at = NOW() WHERE id = ?");
+ $stmt->execute([$queue_id]);
+
+ // Create new token
+ $today = date('Y-m-d');
+ $stmt = $db->prepare("
+ SELECT MAX(token_number)
+ FROM patient_queue
+ WHERE department_id = ?
+ AND DATE(created_at) = ?
+ ");
+ $stmt->execute([$new_department_id, $today]);
+ $max_token = $stmt->fetchColumn();
+ $next_token = ($max_token) ? $max_token + 1 : 1;
+
+ $stmt = $db->prepare("
+ INSERT INTO patient_queue (patient_id, department_id, doctor_id, token_number, status, created_at)
+ VALUES (?, ?, ?, ?, 'waiting', NOW())
+ ");
+ $stmt->execute([$current['patient_id'], $new_department_id, $new_doctor_id ?: null, $next_token]);
+ $new_queue_id = $db->lastInsertId();
+
+ echo json_encode(['success' => true, 'message' => 'Token transferred', 'token_number' => $next_token, 'new_queue_id' => $new_queue_id]);
+ exit;
+ }
+
// --- SUMMARY ---
if ($action === 'summary') {
$today = date('Y-m-d');
@@ -160,6 +212,7 @@ try {
SUM(CASE WHEN q.status = 'completed' THEN 1 ELSE 0 END) as completed
FROM patient_queue q
JOIN departments dept ON q.department_id = dept.id
+ LEFT JOIN departments td ON q.target_department_id = td.id
$where
GROUP BY dept.id
";
diff --git a/db/migrations/20260328_add_smart_queue_routing.sql b/db/migrations/20260328_add_smart_queue_routing.sql
new file mode 100644
index 0000000..1df7cbd
--- /dev/null
+++ b/db/migrations/20260328_add_smart_queue_routing.sql
@@ -0,0 +1,3 @@
+ALTER TABLE departments ADD COLUMN requires_vitals TINYINT(1) DEFAULT 0;
+ALTER TABLE departments ADD COLUMN is_vitals_room TINYINT(1) DEFAULT 0;
+ALTER TABLE patient_queue ADD COLUMN target_department_id INT(11) DEFAULT NULL;
diff --git a/fix_db.php b/fix_db.php
deleted file mode 100644
index 232c191..0000000
--- a/fix_db.php
+++ /dev/null
@@ -1,77 +0,0 @@
-Applying Database Fixes...";
-
-try {
- // 1. Add columns if missing
- try {
- $db->exec("ALTER TABLE appointments ADD COLUMN start_time DATETIME NULL");
- echo "Added start_time column.
";
- } catch (PDOException $e) {
- echo "start_time check: " . $e->getMessage() . "
";
- }
-
- try {
- $db->exec("ALTER TABLE appointments ADD COLUMN end_time DATETIME NULL");
- echo "Added end_time column.
";
- } catch (PDOException $e) {
- echo "end_time check: " . $e->getMessage() . "
";
- }
-
- // 2. Migrate data
- // Check if appointment_date exists before trying to use it
- try {
- $stmt = $db->query("SHOW COLUMNS FROM appointments LIKE 'appointment_date'");
- if ($stmt->fetch()) {
- $db->exec("UPDATE appointments SET start_time = appointment_date WHERE start_time IS NULL");
- echo "Migrated data from appointment_date to start_time.
";
- } else {
- echo "Column 'appointment_date' does not exist, skipping data migration.
";
- }
- } catch (PDOException $e) {
- echo "Data migration check failed: " . $e->getMessage() . "
";
- }
-
- // Populate end_time based on start_time
- try {
- $db->exec("UPDATE appointments SET end_time = DATE_ADD(start_time, INTERVAL 30 MINUTE) WHERE end_time IS NULL AND start_time IS NOT NULL");
- echo "Populated end_time based on start_time.
";
- } catch (PDOException $e) {
- echo "Could not populate end_time: " . $e->getMessage() . "
";
- }
-
- // 3. Set NOT NULL constraint
- try {
- // Only if start_time has values (or table is empty)
- $db->exec("ALTER TABLE appointments MODIFY COLUMN start_time DATETIME NOT NULL");
- echo "Set start_time to NOT NULL.
";
- } catch (PDOException $e) {
- echo "Could not set start_time to NOT NULL (maybe some rows are NULL?): " . $e->getMessage() . "
";
- }
-
- try {
- $db->exec("ALTER TABLE appointments MODIFY COLUMN end_time DATETIME NOT NULL");
- echo "Set end_time to NOT NULL.
";
- } catch (PDOException $e) {
- echo "Could not set end_time to NOT NULL: " . $e->getMessage() . "
";
- }
-
- // 4. Ensure other columns exist (like nurse_id, visit_type, etc. from previous migrations)
- try {
- $db->exec("ALTER TABLE appointments ADD COLUMN nurse_id INT NULL");
- } catch (Exception $e) {}
- try {
- $db->exec("ALTER TABLE appointments ADD COLUMN visit_type ENUM('Clinic', 'Home') DEFAULT 'Clinic'");
- } catch (Exception $e) {}
- try {
- $db->exec("ALTER TABLE appointments ADD COLUMN address TEXT NULL");
- } catch (Exception $e) {}
-
- echo "