diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..674ef3e --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,124 @@ +/* assets/css/custom.css */ + +body { + font-family: 'Poppins', sans-serif; + background-color: #F8F9FA; + color: #212529; +} + +.sidebar { + background-color: #2c3e50; + height: 100vh; + position: sticky; + top: 0; +} + +.sidebar .nav-link { + color: rgba(255, 255, 255, 0.7); + border-radius: 0.5rem; + margin-bottom: 0.5rem; +} + +.sidebar .nav-link:hover { + color: #ffffff; + background-color: rgba(255, 255, 255, 0.1); +} + +.sidebar .nav-link.active { + color: #ffffff; + background-color: #4A90E2; +} + +.sidebar .nav-link .bi { + font-size: 1.2rem; +} + +.sidebar hr { + border-color: rgba(255, 255, 255, 0.2); +} + +.main-content { + background-color: #F8F9FA; +} + +.btn-primary-custom { + background-color: #4A90E2; + border-color: #4A90E2; + color: #ffffff; + padding: 0.75rem 1.25rem; + border-radius: 0.5rem; + font-weight: 600; + transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; +} + +.btn-primary-custom:hover { + background-color: #357ABD; + border-color: #357ABD; + color: #ffffff; +} + +.card { + border: none; + border-radius: 0.5rem; + box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important; +} + +.card .card-header { + background-color: #fff; + border-bottom: 1px solid #e3e6f0; + padding: 1rem 1.25rem; +} + +.card .card-body { + padding: 1.5rem; +} + +.border-left-primary { + border-left: 0.25rem solid #4A90E2 !important; +} + +.text-primary { + color: #4A90E2 !important; +} + +.border-left-success { + border-left: 0.25rem solid #1cc88a !important; +} + +.text-success { + color: #1cc88a !important; +} + +.border-left-info { + border-left: 0.25rem solid #36b9cc !important; +} + +.text-info { + color: #36b9cc !important; +} + +.text-gray-300 { + color: #dddfeb !important; +} + +.text-gray-800 { + color: #5a5c69 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.text-xs { + font-size: .7rem; +} + +.form-control, .form-select { + border-radius: 0.5rem; + padding: 0.75rem 1rem; +} + +.form-control:focus, .form-select:focus { + box-shadow: 0 0 0 0.25rem rgba(74, 144, 226, 0.25); + border-color: #4A90E2; +} diff --git a/auth.php b/auth.php new file mode 100644 index 0000000..8739b2d --- /dev/null +++ b/auth.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/billing.php b/billing.php new file mode 100644 index 0000000..fea00f0 --- /dev/null +++ b/billing.php @@ -0,0 +1,144 @@ +prepare("UPDATE patients SET payment_status = 'paid' WHERE id = ?"); + $stmt->execute([$patient_id_to_update]); + // Redirect to avoid form resubmission + header("Location: billing.php"); + exit; + } catch (PDOException $e) { + // Log error + } + } +} + +// Fetch unpaid patients +try { + $pdo = db(); + $stmt = $pdo->prepare("SELECT p.*, u.username as doctor_name FROM patients p JOIN users u ON p.doctor_id = u.id WHERE p.status = 'Completed' AND p.payment_status = 'unpaid' ORDER BY p.updated_at DESC"); + $stmt->execute(); + $unpaid_patients = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $unpaid_patients = []; + // Log error +} + +?> + + + + + + Billing Management + + + + + + + +
+ + + + +
+
+
+

Billing

+

Manage outstanding payments.

+
+ +
+
+
Pending Payments
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Patient NameDoctorService RenderedCostAction
Dr. $ +
+ + + +
+ Invoice +
No pending payments.
+
+
+
+
+
+
+ + + + diff --git a/db/config.php b/db/config.php index b92662e..80b585f 100644 --- a/db/config.php +++ b/db/config.php @@ -1,17 +1,36 @@ PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; + static $pdo; + if ($pdo) { + return $pdo; + } + + $host = getenv('DB_HOST') ?: '127.0.0.1'; + $port = getenv('DB_PORT') ?: '3306'; + $dbname = getenv('DB_NAME') ?: 'app_35705'; + $user = getenv('DB_USER') ?: 'app_35705'; + $pass = getenv('DB_PASS') ?: 'cdc156d8-b1ec-4426-86fb-b1e546c1a442'; + $charset = 'utf8mb4'; + + $dsn = "mysql:host=$host;port=$port;dbname=$dbname;charset=$charset"; + $options = [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, + ]; + + try { + $pdo = new PDO($dsn, $user, $pass, $options); + return $pdo; + } catch (PDOException $e) { + // In a real app, you'd log this error and show a generic message + throw new PDOException($e->getMessage(), (int)$e->getCode()); + } } +?> diff --git a/doctor_dashboard.php b/doctor_dashboard.php new file mode 100644 index 0000000..84f1158 --- /dev/null +++ b/doctor_dashboard.php @@ -0,0 +1,248 @@ +prepare("SELECT p.* FROM patients p JOIN users u ON p.doctor_id = u.id WHERE u.id = ? AND DATE(p.created_at) = CURDATE() ORDER BY p.created_at DESC"); + $stmt->execute([$doctor_id]); + $patients = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $patients = []; + // In a real app, you'd want to log this error +} + +// Fetch doctor's details from users table +try { + $pdo = db(); + $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); + $stmt->execute([$doctor_id]); + $doctor = $stmt->fetch(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $doctor = null; +} + +?> + + + + + + Doctor's Dashboard + + + + + + + +
+ + + + +
+
+
+

Doctor's Dashboard

+

Welcome, Dr. !

+
+ + +
+
+
Today's Appointments
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Patient IDNameStatusAction
+ + + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
No patients assigned for today.
+
+
+
+
+
+
+ + + + + +Start Consultation'; + case 'In Progress': + return ''; // Form is shown in a separate row + case 'Completed': + return 'Completed'; + default: + return ''; + } +} +?> \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..bf4a762 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,6 @@ - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- - - +// This is the main entry point of the application. +// It will redirect the user to the login page. +header("Location: login.php"); +exit; +?> \ No newline at end of file diff --git a/invoice.php b/invoice.php new file mode 100644 index 0000000..1b7832a --- /dev/null +++ b/invoice.php @@ -0,0 +1,115 @@ +prepare(" + SELECT p.*, u.username as doctor_name + FROM patients p + JOIN users u ON p.doctor_id = u.id + WHERE p.id = ? AND p.status = 'Completed' + ORDER BY p.updated_at DESC + LIMIT 1 + "); + $stmt->execute([$patient_id]); + $visit = $stmt->fetch(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $visit = null; + // Log error +} + +if (!$visit) { + die("No completed visit found for this patient or an error occurred."); +} + +?> + + + + + + Invoice - <?php echo htmlspecialchars($visit['patient_id']); ?> + + + + + + +
+
+
ClinicFlow
+

Invoice

+
+ +
+
+
Billed To:
+

+

+

+
+
+

Invoice #: INV-

+

Date:

+

Status:

+
+
+ +
+
Consultation Details:
+

Consulting Doctor: Dr.

+
+ + + + + + + + + + + + + + +
Service RenderedCost
$
+ +
+

Total: $

+
+ + + Back to Billing + +
+ + + diff --git a/login.php b/login.php new file mode 100644 index 0000000..0511510 --- /dev/null +++ b/login.php @@ -0,0 +1,120 @@ +prepare("SELECT * FROM `users` WHERE `username` = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + $_SESSION['role'] = $user['role']; + + // Redirect based on role + if ($user['role'] === 'doctor') { + header("Location: doctor_dashboard.php"); + } else { + header("Location: reception.php"); + } + exit; + } else { + $error = 'Invalid username or password.'; + } + } catch (PDOException $e) { + $error = 'Database error. Please try again later.'; + } + } +} +?> + + + + + + Login - Clinic Management + + + + + +
+
+ + +
+
+ + diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..2d2c92f --- /dev/null +++ b/logout.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/patient_profile.php b/patient_profile.php new file mode 100644 index 0000000..a801c78 --- /dev/null +++ b/patient_profile.php @@ -0,0 +1,162 @@ +prepare("SELECT * FROM patients WHERE id = ?"); +$stmt->execute([$patient_id]); +$patient = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$patient) { + die("Patient not found."); +} + +// Now, fetch all visits for this patient using their patient_id +$history_stmt = db()->prepare( + "SELECT p.*, d.username as doctor_name + FROM patients p + LEFT JOIN users d ON p.doctor_id = d.id + WHERE p.patient_id = ? + ORDER BY p.created_at DESC" +); +$history_stmt->execute([$patient['patient_id']]); +$visit_history = $history_stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + + + + + + Patient Profile - <?= htmlspecialchars($patient['patient_name']) ?> + + + + + + + +
+ + + + +
+
+

Patient Profile

+ + Back to Dashboard + +
+ + +
+
+
+ + +
+
+
+
+
+

Phone:

+
+
+

Address:

+
+
+
+
+ + +
+
+
+ + Visit History +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Visit DateAssigned DoctorStatusConsultation Notes
No visit history found.
+ + +
+
+
+
+ +
+
+ + + + diff --git a/patient_register.php b/patient_register.php new file mode 100644 index 0000000..7ee3c6e --- /dev/null +++ b/patient_register.php @@ -0,0 +1,152 @@ +query("SELECT id, username FROM users WHERE role = 'doctor' ORDER BY username"); + $doctors = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $error = "Database error: " . $e->getMessage(); +} + +if ($_SERVER["REQUEST_METHOD"] == "POST") { + $patient_name = trim($_POST['patient_name']); + $phone_number = trim($_POST['phone_number']); + $address = trim($_POST['address']); + $doctor_id = $_POST['doctor_id']; + + if (empty($patient_name) || empty($doctor_id)) { + $error = "Patient name and assigned doctor are required."; + } else { + try { + // Generate a unique patient ID + $prefix = 'PT'; + $stmt = $pdo->query("SELECT MAX(id) FROM patients"); + $last_id = $stmt->fetchColumn(); + $next_id = ($last_id) ? $last_id + 1 : 1; + $patient_id = $prefix . str_pad($next_id, 6, '0', STR_PAD_LEFT); + + // Set initial status + $status = 'Pending'; + $total_fee = 20.00; // Example fee + + $sql = "INSERT INTO patients (patient_id, patient_name, phone_number, address, doctor_id, status, total_fee) VALUES (?, ?, ?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + + if ($stmt->execute([$patient_id, $patient_name, $phone_number, $address, $doctor_id, $status, $total_fee])) { + $_SESSION['message'] = "Patient registered successfully! Patient ID: $patient_id"; + header("Location: reception.php"); + exit(); + } else { + $error = "Failed to register patient."; + } + } catch (PDOException $e) { + $error = "Database error: " . $e->getMessage(); + } + } +} +?> + + + + + + Register Patient - Hospital Management + + + + + + + + + +
+ + + + +
+
+

Register New Patient

+
+ + +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+ + + + diff --git a/reception.php b/reception.php new file mode 100644 index 0000000..13b4474 --- /dev/null +++ b/reception.php @@ -0,0 +1,201 @@ +query("SELECT count(id) FROM patients WHERE DATE(created_at) = CURDATE()")->fetchColumn(); +$total_patients = $pdo->query("SELECT count(id) FROM patients")->fetchColumn(); +$total_revenue = $pdo->query("SELECT SUM(total_fee) FROM patients WHERE status = 'Completed'")->fetchColumn(); + +?> + + + + + + Reception Dashboard - Hospital Management + + + + + + + + + +
+ + + + +
+
+

Receptionist Dashboard

+ + + Register New Patient + +
+ + +
+
+
+
+
+
+
+ Patients Registered (Today)
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ Total Patients
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Total Revenue +
+
$
+
+
+ +
+
+
+
+
+
+ + +
+
+
Find a Patient
+
+
+
+ + +
+
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + + prepare($sql); + $stmt->execute($params); + + if ($stmt->rowCount() > 0) { + while ($row = $stmt->fetch()) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + } else { + echo ''; + } + ?> + +
Patient IDPatient NamePhone NumberAssigned DoctorStatusDate
" . htmlspecialchars($row['patient_name']) . "" . htmlspecialchars($row['phone_number']) . "" . htmlspecialchars($row['doctor_name'] ?? 'N/A') . "" . htmlspecialchars($row['status']) . "" . date("Y-m-d H:i", strtotime($row['created_at'])) . "
No patients found.
+
+
+
+ +
+
+ + + + diff --git a/update_patient_status.php b/update_patient_status.php new file mode 100644 index 0000000..a74b189 --- /dev/null +++ b/update_patient_status.php @@ -0,0 +1,75 @@ + false, 'message' => 'Invalid request']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $patient_id = $_POST['patient_id'] ?? null; + $status = $_POST['status'] ?? null; + $notes = $_POST['notes'] ?? null; + $service_rendered = $_POST['service_rendered'] ?? null; + $cost = $_POST['cost'] ?? null; + + if ($patient_id && $status) { + try { + $pdo = db(); + $sql = "UPDATE patients SET status = ?"; + $params = [$status]; + + if ($notes !== null) { + $sql .= ", notes = ?"; + $params[] = $notes; + } + + if ($service_rendered !== null) { + $sql .= ", service_rendered = ?"; + $params[] = $service_rendered; + } + + if ($cost !== null) { + $sql .= ", cost = ?"; + $params[] = $cost; + } + + // When completing, set payment_status to unpaid + if ($status === 'Completed') { + $sql .= ", payment_status = 'unpaid'"; + } + + $sql .= " WHERE id = ?"; + $params[] = $patient_id; + + $stmt = $pdo->prepare($sql); + + if ($stmt->execute($params)) { + $response['success'] = true; + $response['message'] = 'Patient status updated successfully.'; + $response['status_class'] = get_status_badge_class($status); + } else { + $response['message'] = 'Failed to update patient status.'; + } + } catch (PDOException $e) { + $response['message'] = 'Database error: ' . $e->getMessage(); + } + } else { + $response['message'] = 'Missing patient ID or status.'; + } +} + +echo json_encode($response); + +function get_status_badge_class($status) { + switch ($status) { + case 'Pending': + return 'warning'; + case 'In Progress': + return 'info'; + case 'Completed': + return 'success'; + default: + return 'secondary'; + } +} +?> \ No newline at end of file