Rezept hinzugefügt auswahl der MEdikamente

This commit is contained in:
Cay 2026-01-08 18:57:44 +00:00
parent 798ba92b8a
commit d41b0d22d1
13 changed files with 1019 additions and 771 deletions

View File

@ -1,13 +1,23 @@
const db = require("../db");
const { createUser, getAllUsers} = require("../services/admin.service");
const { createUser, getAllUsers } = require("../services/admin.service");
const bcrypt = require("bcrypt");
async function listUsers(req, res) {
const { q } = req.query;
try {
const users = await getAllUsers(db);
let users;
if (q) {
users = await getAllUsers(db, q);
} else {
users = await getAllUsers(db);
}
res.render("admin_users", {
users,
currentUser: req.session.user
currentUser: req.session.user,
query: { q },
});
} catch (err) {
console.error(err);
@ -18,7 +28,7 @@ async function listUsers(req, res) {
function showCreateUser(req, res) {
res.render("admin_create_user", {
error: null,
user: req.session.user
user: req.session.user,
});
}
@ -30,20 +40,20 @@ async function postCreateUser(req, res) {
password,
role,
fachrichtung,
arztnummer
arztnummer,
} = req.body;
first_name = first_name?.trim();
last_name = last_name?.trim();
username = username?.trim();
first_name = first_name?.trim();
last_name = last_name?.trim();
username = username?.trim();
fachrichtung = fachrichtung?.trim();
arztnummer = arztnummer?.trim();
arztnummer = arztnummer?.trim();
// 🔴 Grundvalidierung
if (!first_name || !last_name || !username || !password || !role) {
return res.render("admin_create_user", {
error: "Alle Pflichtfelder müssen ausgefüllt sein",
user: req.session.user
user: req.session.user,
});
}
@ -52,7 +62,7 @@ async function postCreateUser(req, res) {
if (!fachrichtung || !arztnummer) {
return res.render("admin_create_user", {
error: "Für Ärzte sind Fachrichtung und Arztnummer Pflicht",
user: req.session.user
user: req.session.user,
});
}
} else {
@ -75,20 +85,18 @@ async function postCreateUser(req, res) {
req.session.flash = {
type: "success",
message: "Benutzer erfolgreich angelegt"
message: "Benutzer erfolgreich angelegt",
};
res.redirect("/admin/users");
} catch (error) {
res.render("admin_create_user", {
error,
user: req.session.user
user: req.session.user,
});
}
}
async function changeUserRole(req, res) {
const userId = req.params.id;
const { role } = req.body;
@ -98,19 +106,21 @@ async function changeUserRole(req, res) {
return res.redirect("/admin/users");
}
db.query(
"UPDATE users SET role = ? WHERE id = ?",
[role, userId],
err => {
if (err) {
console.error(err);
req.session.flash = { type: "danger", message: "Fehler beim Ändern der Rolle" };
} else {
req.session.flash = { type: "success", message: "Rolle erfolgreich geändert" };
}
res.redirect("/admin/users");
db.query("UPDATE users SET role = ? WHERE id = ?", [role, userId], (err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Fehler beim Ändern der Rolle",
};
} else {
req.session.flash = {
type: "success",
message: "Rolle erfolgreich geändert",
};
}
);
res.redirect("/admin/users");
});
}
async function resetUserPassword(req, res) {
@ -123,27 +133,74 @@ async function resetUserPassword(req, res) {
}
const hash = await bcrypt.hash(password, 10);
db.query(
"UPDATE users SET password = ? WHERE id = ?",
[hash, userId],
err => {
(err) => {
if (err) {
console.error(err);
req.session.flash = { type: "danger", message: "Fehler beim Zurücksetzen" };
req.session.flash = {
type: "danger",
message: "Fehler beim Zurücksetzen",
};
} else {
req.session.flash = { type: "success", message: "Passwort zurückgesetzt" };
req.session.flash = {
type: "success",
message: "Passwort zurückgesetzt",
};
}
res.redirect("/admin/users");
}
);
}
function activateUser(req, res) {
const userId = req.params.id;
db.query("UPDATE users SET active = 1 WHERE id = ?", [userId], (err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Benutzer konnte nicht aktiviert werden",
};
} else {
req.session.flash = {
type: "success",
message: "Benutzer wurde aktiviert",
};
}
res.redirect("/admin/users");
});
}
function deactivateUser(req, res) {
const userId = req.params.id;
db.query("UPDATE users SET active = 0 WHERE id = ?", [userId], (err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Benutzer konnte nicht deaktiviert werden",
};
} else {
req.session.flash = {
type: "success",
message: "Benutzer wurde deaktiviert",
};
}
res.redirect("/admin/users");
});
}
module.exports = {
listUsers,
showCreateUser,
postCreateUser,
changeUserRole,
resetUserPassword
resetUserPassword,
activateUser,
deactivateUser,
};

View File

@ -10,7 +10,7 @@ function createPatient(req, res) {
db.query(
"INSERT INTO patients (firstname, lastname, birthdate, active) VALUES (?, ?, ?, 1)",
[firstname, lastname, birthdate],
err => {
(err) => {
if (err) {
console.error(err);
return res.send("Datenbankfehler");
@ -26,15 +26,28 @@ function listPatients(req, res) {
let sql = "SELECT * FROM patients WHERE 1=1";
const params = [];
if (firstname) { sql += " AND firstname LIKE ?"; params.push(`%${firstname}%`); }
if (lastname) { sql += " AND lastname LIKE ?"; params.push(`%${lastname}%`); }
if (birthdate) { sql += " AND birthdate = ?"; params.push(birthdate); }
if (firstname) {
sql += " AND firstname LIKE ?";
params.push(`%${firstname}%`);
}
if (lastname) {
sql += " AND lastname LIKE ?";
params.push(`%${lastname}%`);
}
if (birthdate) {
sql += " AND birthdate = ?";
params.push(birthdate);
}
sql += " ORDER BY lastname, firstname";
db.query(sql, params, (err, patients) => {
if (err) return res.send("Datenbankfehler");
res.render("patients", { patients, query: req.query, user: req.session.user});
res.render("patients", {
patients,
query: req.query,
user: req.session.user,
});
});
}
@ -43,12 +56,13 @@ function showEditPatient(req, res) {
"SELECT * FROM patients WHERE id = ?",
[req.params.id],
(err, results) => {
if (err || results.length === 0) return res.send("Patient nicht gefunden");
if (err || results.length === 0)
return res.send("Patient nicht gefunden");
res.render("patient_edit", {
patient: results[0],
error: null,
user: req.session.user,
returnTo: req.query.returnTo || null
returnTo: req.query.returnTo || null,
});
}
);
@ -71,13 +85,13 @@ function updatePatient(req, res) {
postal_code,
city,
country,
notes
notes,
} = req.body;
if (!firstname || !lastname || !birthdate) {
req.session.flash = {
type: "warning",
message: "Vorname, Nachname und Geburtsdatum sind Pflichtfelder"
message: "Vorname, Nachname und Geburtsdatum sind Pflichtfelder",
};
return res.redirect("back");
}
@ -112,9 +126,9 @@ function updatePatient(req, res) {
city || null,
country || null,
notes || null,
id
id,
],
err => {
(err) => {
if (err) {
console.error(err);
return res.send("Fehler beim Speichern");
@ -174,14 +188,15 @@ function showPatientMedications(req, res) {
if (err) return res.send("Medikamente konnten nicht geladen werden");
db.query(currentSql, [patientId], (err, currentMeds) => {
if (err) return res.send("Aktuelle Medikation konnte nicht geladen werden");
if (err)
return res.send("Aktuelle Medikation konnte nicht geladen werden");
res.render("patient_medications", {
patient: patients[0],
meds,
currentMeds,
user: req.session.user,
returnTo
returnTo,
});
});
});
@ -199,7 +214,7 @@ function moveToWaitingRoom(req, res) {
WHERE id = ?
`,
[id],
err => {
(err) => {
if (err) return res.send("Fehler beim Verschieben ins Wartezimmer");
res.redirect("/patients");
}
@ -214,7 +229,7 @@ function showWaitingRoom(req, res) {
res.render("waiting_room", {
patients,
user: req.session.user
user: req.session.user,
});
}
);
@ -236,15 +251,17 @@ function showPatientOverview(req, res) {
ORDER BY created_at DESC
`;
// 🔤 Services dynamisch nach Sprache laden
const servicesSql = (nameField) => `
const medicationVariantsSql = `
SELECT
id,
${nameField} AS name,
price
FROM services
WHERE active = 1
ORDER BY ${nameField}
mv.id AS variant_id,
m.name AS medication_name,
mf.name AS form_name,
mv.dosage,
mv.package
FROM medication_variants mv
JOIN medications m ON mv.medication_id = m.id
JOIN medication_forms mf ON mv.form_id = mf.id
ORDER BY m.name, mf.name, mv.dosage
`;
db.query(patientSql, [patientId], (err, patients) => {
@ -254,12 +271,22 @@ function showPatientOverview(req, res) {
const patient = patients[0];
// 🇪🇸 / 🇩🇪 Sprache bestimmen
// 🇪🇸 / 🇩🇪 Sprache für Leistungen
const serviceNameField =
patient.country === "ES"
? "COALESCE(NULLIF(name_es, ''), name_de)"
: "name_de";
const servicesSql = `
SELECT
id,
${serviceNameField} AS name,
price
FROM services
WHERE active = 1
ORDER BY ${serviceNameField}
`;
const todayServicesSql = `
SELECT
ps.id,
@ -279,18 +306,23 @@ function showPatientOverview(req, res) {
db.query(notesSql, [patientId], (err, notes) => {
if (err) return res.send("Fehler Notizen");
db.query(servicesSql(serviceNameField), (err, services) => {
db.query(servicesSql, (err, services) => {
if (err) return res.send("Fehler Leistungen");
db.query(todayServicesSql, [patientId], (err, todayServices) => {
if (err) return res.send("Fehler heutige Leistungen");
res.render("patient_overview", {
patient,
notes,
services,
todayServices,
user: req.session.user
db.query(medicationVariantsSql, (err, medicationVariants) => {
if (err) return res.send("Fehler Medikamente");
res.render("patient_overview", {
patient,
notes,
services,
todayServices,
medicationVariants,
user: req.session.user,
});
});
});
});
@ -298,6 +330,47 @@ function showPatientOverview(req, res) {
});
}
function assignMedicationToPatient(req, res) {
const patientId = req.params.id;
const { medication_variant_id, dosage_instruction, start_date, end_date } =
req.body;
if (!medication_variant_id) {
req.session.flash = {
type: "warning",
message: "Bitte ein Medikament auswählen",
};
return res.redirect(`/patients/${patientId}/overview`);
}
db.query(
`
INSERT INTO patient_medications
(patient_id, medication_variant_id, dosage_instruction, start_date, end_date)
VALUES (?, ?, ?, ?, ?)
`,
[
patientId,
medication_variant_id,
dosage_instruction || null,
start_date || new Date(),
end_date || null,
],
(err) => {
if (err) {
console.error(err);
return res.send("Fehler beim Verordnen");
}
req.session.flash = {
type: "success",
message: "Medikament erfolgreich verordnet",
};
res.redirect(`/patients/${patientId}/overview`);
}
);
}
function addPatientNote(req, res) {
const patientId = req.params.id;
@ -310,7 +383,7 @@ function addPatientNote(req, res) {
db.query(
"INSERT INTO patient_notes (patient_id, note) VALUES (?, ?)",
[patientId, note],
err => {
(err) => {
if (err) return res.send("Fehler beim Speichern der Notiz");
res.redirect(`/patients/${patientId}/overview`);
}
@ -323,7 +396,7 @@ function callFromWaitingRoom(req, res) {
db.query(
"UPDATE patients SET waiting_room = 0 WHERE id = ?",
[patientId],
err => {
(err) => {
if (err) return res.send("Fehler beim Entfernen aus dem Wartezimmer");
res.redirect(`/patients/${patientId}/overview`);
}
@ -336,7 +409,7 @@ function dischargePatient(req, res) {
db.query(
"UPDATE patients SET discharged = 1 WHERE id = ?",
[patientId],
err => {
(err) => {
if (err) return res.send("Fehler beim Entlassen des Patienten");
res.redirect("/waiting-room");
}
@ -375,7 +448,7 @@ function showMedicationPlan(req, res) {
res.render("patient_plan", {
patient: patients[0],
meds
meds,
});
});
});
@ -392,19 +465,19 @@ function movePatientToWaitingRoom(req, res) {
WHERE id = ?
`,
[patientId],
err => {
(err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Fehler beim Zurücksetzen ins Wartezimmer"
message: "Fehler beim Zurücksetzen ins Wartezimmer",
};
return res.redirect(`/patients/${patientId}/overview`);
}
req.session.flash = {
type: "success",
message: "Patient wurde ins Wartezimmer gesetzt"
message: "Patient wurde ins Wartezimmer gesetzt",
};
res.redirect("/waiting-room");
@ -415,55 +488,107 @@ function movePatientToWaitingRoom(req, res) {
function deactivatePatient(req, res) {
const id = req.params.id;
db.query(
"UPDATE patients SET active = 0 WHERE id = ?",
[id],
err => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Patient konnte nicht gesperrt werden"
};
return res.redirect("/patients");
}
db.query("UPDATE patients SET active = 0 WHERE id = ?", [id], (err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "success",
message: "Patient wurde gesperrt"
type: "danger",
message: "Patient konnte nicht gesperrt werden",
};
res.redirect("/patients");
return res.redirect("/patients");
}
);
req.session.flash = {
type: "success",
message: "Patient wurde gesperrt",
};
res.redirect("/patients");
});
}
function activatePatient(req, res) {
const id = req.params.id;
db.query(
"UPDATE patients SET active = 1 WHERE id = ?",
[id],
err => {
if (err) {
console.error(err);
req.session.flash = {
type: "danger",
message: "Patient konnte nicht entsperrt werden"
};
return res.redirect("/patients");
}
db.query("UPDATE patients SET active = 1 WHERE id = ?", [id], (err) => {
if (err) {
console.error(err);
req.session.flash = {
type: "success",
message: "Patient wurde entsperrt"
type: "danger",
message: "Patient konnte nicht entsperrt werden",
};
res.redirect("/patients");
return res.redirect("/patients");
}
);
req.session.flash = {
type: "success",
message: "Patient wurde entsperrt",
};
res.redirect("/patients");
});
}
async function showPatientOverviewDashborad(req, res) {
const patientId = req.params.id;
try {
// 👤 Patient
const [[patient]] = await db
.promise()
.query("SELECT * FROM patients WHERE id = ?", [patientId]);
if (!patient) {
return res.redirect("/patients");
}
// 💊 AKTUELLE MEDIKAMENTE (end_date IS NULL)
const [medications] = await db.promise().query(
`
SELECT
m.name AS medication_name,
mv.dosage AS variant_dosage,
pm.dosage_instruction,
pm.start_date
FROM patient_medications pm
JOIN medication_variants mv
ON pm.medication_variant_id = mv.id
JOIN medications m
ON mv.medication_id = m.id
WHERE pm.patient_id = ?
AND pm.end_date IS NULL
ORDER BY pm.start_date DESC
`,
[patientId]
);
// 🧾 RECHNUNGEN
const [invoices] = await db.promise().query(
`
SELECT
id,
invoice_date,
total_amount,
file_path,
status
FROM invoices
WHERE patient_id = ?
ORDER BY invoice_date DESC
`,
[patientId]
);
res.render("patient_overview_dashboard", {
patient,
medications,
invoices,
user: req.session.user,
});
} catch (err) {
console.error(err);
res.send("Datenbankfehler");
}
}
module.exports = {
listPatients,
@ -481,5 +606,7 @@ module.exports = {
showMedicationPlan,
movePatientToWaitingRoom,
deactivatePatient,
activatePatient
activatePatient,
showPatientOverviewDashborad,
assignMedicationToPatient,
};

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,9 @@ const {
showCreateUser,
postCreateUser,
changeUserRole,
resetUserPassword
resetUserPassword,
activateUser,
deactivateUser,
} = require("../controllers/admin.controller");
const { requireAdmin } = require("../middleware/auth.middleware");
@ -17,6 +19,7 @@ router.post("/create-user", requireAdmin, postCreateUser);
router.post("/users/change-role/:id", requireAdmin, changeUserRole);
router.post("/users/reset-password/:id", requireAdmin, resetUserPassword);
router.post("/users/activate/:id", requireAdmin, activateUser);
router.post("/users/deactivate/:id", requireAdmin, deactivateUser);
module.exports = router;

View File

@ -1,10 +1,7 @@
const express = require("express");
const router = express.Router();
const {
requireLogin,
requireAdmin
} = require("../middleware/auth.middleware");
const { requireLogin, requireAdmin } = require("../middleware/auth.middleware");
const {
listPatients,
@ -20,7 +17,9 @@ const {
dischargePatient,
showMedicationPlan,
deactivatePatient,
activatePatient
activatePatient,
showPatientOverviewDashborad,
assignMedicationToPatient,
} = require("../controllers/patient.controller");
router.get("/", requireLogin, listPatients);
@ -35,8 +34,9 @@ router.post("/:id/notes", requireLogin, addPatientNote);
router.post("/waiting-room/call/:id", requireAdmin, callFromWaitingRoom);
router.post("/:id/discharge", requireLogin, dischargePatient);
router.get("/:id/plan", requireLogin, showMedicationPlan);
router.post("/deactivate/:id", requireLogin, deactivatePatient);
router.post("/activate/:id", requireLogin, activatePatient);
router.post("/deactivate/:id", requireLogin, deactivatePatient);
router.post("/activate/:id", requireLogin, activatePatient);
router.get("/:id", requireLogin, showPatientOverviewDashborad);
router.post("/:id/medications/assign", requireLogin, assignMedicationToPatient);
module.exports = router;

View File

@ -17,16 +17,8 @@ async function createUser(
`INSERT INTO users
(first_name, last_name, username, password, role, fachrichtung, arztnummer, active)
VALUES (?, ?, ?, ?, ?, ?, ?, 1)`,
[
first_name,
last_name,
username,
hash,
role,
fachrichtung,
arztnummer
],
err => {
[first_name, last_name, username, hash, role, fachrichtung, arztnummer],
(err) => {
if (err) {
if (err.code === "ER_DUP_ENTRY") {
return reject("Benutzername existiert bereits");
@ -39,28 +31,33 @@ async function createUser(
});
}
function getAllUsers(db) {
return new Promise((resolve, reject) => {
db.query(
`SELECT
id,
first_name,
last_name,
username,
role,
active,
lock_until
FROM users
ORDER BY last_name, first_name`,
(err, users) => {
if (err) return reject(err);
resolve(users);
}
);
});
async function getAllUsers(db, search = null) {
let sql = `
SELECT *
FROM users
WHERE 1=1
`;
const params = [];
if (search) {
sql += `
AND (
first_name LIKE ?
OR last_name LIKE ?
OR username LIKE ?
)
`;
const q = `%${search}%`;
params.push(q, q, q);
}
sql += " ORDER BY last_name, first_name";
const [rows] = await db.promise().query(sql, params);
return rows;
}
module.exports = {
createUser,
getAllUsers
getAllUsers,
};

View File

@ -44,6 +44,30 @@
+ Neuen Benutzer anlegen
</a>
</div>
<div class="row mb-3 align-items-end">
<div class="col-md-6">
<form method="GET" action="/admin/users" class="d-flex gap-2">
<input
type="text"
name="q"
class="form-control"
placeholder="🔍 Benutzer suchen (Name oder Username)"
value="<%= query?.q || '' %>">
<button class="btn btn-outline-primary">
Suchen
</button>
<% if (query?.q) { %>
<a href="/admin/users" class="btn btn-outline-secondary">
Reset
</a>
<% } %>
</form>
</div>
</div>
<table class="table table-bordered table-hover align-middle">
<thead class="table-dark">
<tr>

View File

@ -38,60 +38,6 @@ function formatDate(d) {
<% if (user && user.role === 'arzt') { %>
<div class="card shadow mb-4">
<div class="card-body">
<h5 class="mb-3"> Medikament hinzufügen</h5>
<form method="POST"
action="/patients/<%= patient.id %>/medications?returnTo=<%= returnTo || '' %>">
<div class="mb-3">
<label class="form-label">Medikament</label>
<select name="medication_variant_id"
class="form-select"
required>
<option value="">Bitte wählen</option>
<% meds.forEach(m => { %>
<option value="<%= m.id %>">
<%= m.medication %>
<%= m.form %>
<%= m.dosage %>
<%= m.package %>
</option>
<% }) %>
</select>
</div>
<div class="mb-3">
<label class="form-label">Dosieranweisung</label>
<input type="text"
name="dosage_instruction"
class="form-control"
placeholder="z. B. 101 nach dem Essen">
</div>
<div class="row mb-3">
<div class="col">
<label class="form-label">Startdatum</label>
<input type="date"
name="start_date"
class="form-control">
</div>
<div class="col">
<label class="form-label">Enddatum</label>
<input type="date"
name="end_date"
class="form-control">
</div>
</div>
<button class="btn btn-primary">
💾 Medikament hinzufügen
</button>
</form>
</div>
</div>
<% } else { %>

View File

@ -1,282 +1,274 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Patientenübersicht <%= patient.firstname %> <%= patient.lastname %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<head>
<meta charset="UTF-8" />
<title>
Patientenübersicht <%= patient.firstname %> <%= patient.lastname %>
</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/css/bootstrap.min.css" />
<script src="/js/service-search.js"></script>
</head>
</head>
<body class="bg-light">
<body class="bg-light">
<!-- NAVBAR -->
<nav class="navbar navbar-dark bg-dark position-relative px-3">
<div
class="position-absolute top-50 start-50 translate-middle d-flex align-items-center gap-2 text-white"
>
<span style="font-size: 1.4rem">👨‍⚕️</span>
<span class="fw-semibold fs-5">
Patient <%= patient.firstname %> <%= patient.lastname %>
</span>
</div>
<nav class="navbar navbar-dark bg-dark px-3">
<span class="navbar-brand">
👨‍⚕️ Patient <%= patient.firstname %> <%= patient.lastname %>
</span>
<a href="/waiting-room" class="btn btn-outline-light btn-sm">
🪑 Zurück
</a>
<form method="POST"
action="/patients/<%= patient.id %>/waiting-room"
class="d-inline"
onsubmit="return confirm('Patient ins Wartezimmer zurücksetzen?')">
<button class="btn btn-warning">
🪑 Ins Wartezimmer
</button>
</form>
</nav>
<div class="container mt-4">
<%- include("partials/flash") %>
<!-- =========================
PATIENTENDATEN
========================== -->
<div class="card shadow mb-4">
<div class="card-body">
<h4>Patientendaten</h4>
<table class="table table-sm">
<tr>
<th>Vorname</th>
<td><%= patient.firstname %></td>
</tr>
<tr>
<th>Nachname</th>
<td><%= patient.lastname %></td>
</tr>
<tr>
<th>Geburtsdatum</th>
<td>
<%= patient.birthdate
? new Date(patient.birthdate).toLocaleDateString("de-DE")
: "-" %>
</td>
</tr>
<tr>
<th>Geschlecht</th>
<td>
<% if (patient.gender === 'm') { %>Männlich
<% } else if (patient.gender === 'w') { %>Weiblich
<% } else if (patient.gender === 'd') { %>Divers
<% } else { %><% } %>
</td>
</tr>
<tr>
<th>E-Mail</th>
<td><%= patient.email || "-" %></td>
</tr>
<tr>
<th>Telefon</th>
<td><%= patient.phone || "-" %></td>
</tr>
<tr>
<th>Adresse</th>
<td>
<%= patient.street || "" %> <%= patient.house_number || "" %><br>
<%= patient.postal_code || "" %> <%= patient.city || "" %><br>
<%= patient.country || "" %>
</td>
</tr>
</table>
</div>
</div>
<!-- =========================
AKTIONEN
========================== -->
<div class="d-flex flex-wrap gap-2 align-items-center mb-4">
<a href="/patients/<%= patient.id %>/medications?returnTo=overview"
class="btn btn-primary">
💊 Medikation
</a>
<a href="/patients/<%= patient.id %>/plan"
class="btn btn-outline-secondary">
📄 Medikationsplan
</a>
<a href="/patients/edit/<%= patient.id %>?returnTo=overview"
class="btn btn-outline-info">
✏️ Patient bearbeiten
</a>
<form method="POST"
action="/patients/<%= patient.id %>/discharge"
class="d-inline"
onsubmit="return confirm('Patient wirklich entlassen?')">
<button class="btn btn-danger">
🟥 Entlassen
</button>
<div class="ms-auto">
<form
method="POST"
action="/patients/<%= patient.id %>/waiting-room"
onsubmit="return confirm('Patient ins Wartezimmer zurücksetzen?')"
>
<button class="btn btn-warning btn-sm">🪑 Ins Wartezimmer</button>
</form>
</div>
</nav>
</div>
<div class="container mt-4">
<%- include("partials/flash") %>
<!-- =========================
UNTERER BEREICH
========================== -->
<div class="row">
<!-- PATIENTENDATEN -->
<div class="card shadow mb-4">
<div class="card-body">
<h4>Patientendaten</h4>
<table class="table table-sm">
<tr>
<th>Vorname</th>
<td><%= patient.firstname %></td>
</tr>
<tr>
<th>Nachname</th>
<td><%= patient.lastname %></td>
</tr>
<tr>
<th>Geburtsdatum</th>
<td>
<%= patient.birthdate ? new
Date(patient.birthdate).toLocaleDateString("de-DE") : "-" %>
</td>
</tr>
<tr>
<th>E-Mail</th>
<td><%= patient.email || "-" %></td>
</tr>
<tr>
<th>Telefon</th>
<td><%= patient.phone || "-" %></td>
</tr>
</table>
</div>
</div>
<!-- =========================
LINKS: NOTIZEN
========================== -->
<div class="col-md-7">
<!-- AKTIONEN -->
<div class="d-flex gap-2 mb-4">
<a
href="/patients/<%= patient.id %>/medications?returnTo=overview"
class="btn btn-primary"
>
💊 Medikation verwalten
</a>
<div class="card shadow">
<div class="card-body">
<a
href="/patients/edit/<%= patient.id %>?returnTo=overview"
class="btn btn-outline-info"
>
✏️ Patient bearbeiten
</a>
<h5>Notizen</h5>
<form
method="POST"
action="/patients/<%= patient.id %>/discharge"
class="d-inline"
onsubmit="return confirm('Patient wirklich entlassen?')"
>
<button class="btn btn-danger">🟥 Entlassen</button>
</form>
</div>
<form method="POST"
action="/patients/<%= patient.id %>/notes"
class="mb-3">
<!-- UNTERER BEREICH -->
<div
class="row g-3"
style="
height: calc(100vh - 520px);
min-height: 320px;
padding-bottom: 3rem;
overflow: hidden;
"
>
<!-- 📝 NOTIZEN -->
<div class="col-lg-5 col-md-12 h-100">
<div class="card shadow h-100">
<div class="card-body d-flex flex-column h-100">
<h5>📝 Notizen</h5>
<textarea class="form-control mb-2"
name="note"
rows="3"
placeholder="Neue Notiz hinzufügen..."></textarea>
<form
method="POST"
action="/patients/<%= patient.id %>/notes"
style="flex-shrink: 0"
>
<textarea
class="form-control mb-2"
name="note"
rows="3"
style="resize: none"
placeholder="Neue Notiz hinzufügen…"
></textarea>
<button class="btn btn-sm btn-primary">
Notiz speichern
</button>
</form>
<button class="btn btn-sm btn-primary">
Notiz speichern
</button>
</form>
<hr>
<% if (!notes || notes.length === 0) { %>
<p class="text-muted">Keine Notizen vorhanden</p>
<% } else { %>
<% notes.forEach(n => { %>
<div class="mb-3 p-2 border rounded bg-light">
<div class="small text-muted mb-1">
<%= new Date(n.created_at).toLocaleString("de-DE") %>
</div>
<div><%= n.note %></div>
</div>
<% }) %>
<% } %>
<hr class="my-2" style="flex-shrink: 0" />
<div
style="
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
padding-bottom: 2rem;
"
>
<% if (!notes || notes.length === 0) { %>
<p class="text-muted">Keine Notizen vorhanden</p>
<% } else { %> <% notes.forEach(n => { %>
<div class="mb-3 p-2 border rounded bg-light">
<div class="small text-muted">
<%= new Date(n.created_at).toLocaleString("de-DE") %>
</div>
<div><%= n.note %></div>
</div>
<% }) %> <% } %>
</div>
</div>
</div>
</div>
<!-- =========================
RECHTS: HEUTIGE LEISTUNGEN
========================== -->
<div class="col-md-5">
<!-- 💊 MEDIKAMENT -->
<div class="col-lg-3 col-md-6 h-100">
<div class="card shadow h-100">
<div class="card-body">
<h5>💊 Rezept erstellen</h5>
<div class="card shadow">
<div class="card-body">
<form
method="POST"
action="/patients/<%= patient.id %>/medications/assign"
>
<select
name="medication_variant_id"
class="form-select mb-2"
required
>
<option value="">Bitte auswählen…</option>
<% medicationVariants.forEach(mv => { %>
<option value="<%= mv.variant_id %>">
<%= mv.medication_name %> <%= mv.form_name %> <%=
mv.dosage %>
</option>
<% }) %>
</select>
<h5>Heutige Leistungen</h5>
<input
type="text"
name="dosage_instruction"
class="form-control mb-2"
placeholder="z. B. 101"
/>
<!-- Leistung hinzufügen -->
<form method="POST"
action="/patients/<%= patient.id %>/services"
class="mb-3">
<input
type="date"
name="start_date"
class="form-control mb-2"
value="<%= new Date().toISOString().split('T')[0] %>"
/>
<div class="mb-2">
<label class="form-label">Leistung suchen</label>
<input type="text"
id="serviceSearch"
class="form-control mb-2"
placeholder="Leistung suchen…">
</div>
<input type="date" name="end_date" class="form-control mb-3" />
<div class="mb-2">
<label class="form-label">Leistung</label>
<select name="service_id"
id="serviceSelect"
class="form-select"
size="8"
required>
<% services.forEach(s => { %>
<option value="<%= s.id %>">
<%= s.name %>
<%= Number(s.price || 0).toFixed(2) %> €
</option>
<% }) %>
</select>
</div>
<div class="mb-2">
<label class="form-label">Menge</label>
<input type="number"
name="quantity"
class="form-control"
value="1"
min="1"
required>
</div>
<button class="btn btn-sm btn-success">
Leistung hinzufügen
</button>
</form>
<hr>
<!-- Heutige Leistungen anzeigen-->
<% if (!todayServices || todayServices.length === 0) { %>
<p class="text-muted">
Noch keine Leistungen für heute erfasst.
</p>
<% } else { %>
<% todayServices.forEach(ls => { %>
<div class="border rounded p-2 mb-2 bg-light">
<strong><%= ls.name %></strong><br>
Menge: <%= ls.quantity %><br>
Preis: <%= Number(ls.price).toFixed(2) %> €<br>
<small class="text-muted">
Arzt: <%= ls.doctor || "—" %>
</small>
<div class="mt-2 d-flex gap-2">
<!-- Preis ändern -->
<form method="POST"
action="/patient-services/update/<%= ls.id %>">
<input type="number"
step="0.01"
name="price"
value="<%= ls.price %>"
class="form-control form-control-sm mb-1"
required>
<button class="btn btn-sm btn-outline-warning">
💰 Preis ändern
</button>
</form>
<!-- Löschen -->
<form method="POST"
action="/patient-services/delete/<%= ls.id %>"
onsubmit="return confirm('Leistung wirklich löschen?')">
<button class="btn btn-sm btn-danger">
🗑️ Löschen
</button>
</form>
</div>
</div>
<% }) %>
<% } %>
</div>
<button class="btn btn-sm btn-success w-100">
Verordnen
</button>
</form>
</div>
</div>
</div>
<!-- 🧾 HEUTIGE LEISTUNGEN -->
<div class="col-lg-4 col-md-6 h-100">
<div class="card shadow h-100">
<div class="card-body d-flex flex-column h-100">
<h5>🧾 Heutige Leistungen</h5>
<form
method="POST"
action="/patients/<%= patient.id %>/services"
style="flex-shrink: 0"
>
<input
type="text"
id="serviceSearch"
class="form-control mb-2"
placeholder="Leistung suchen…"
/>
<select
name="service_id"
id="serviceSelect"
class="form-select mb-2"
size="5"
required
>
<% services.forEach(s => { %>
<option value="<%= s.id %>">
<%= s.name %> <%= Number(s.price || 0).toFixed(2) %> €
</option>
<% }) %>
</select>
<input
type="number"
name="quantity"
class="form-control mb-2"
value="1"
min="1"
/>
<button class="btn btn-sm btn-success w-100">
Leistung hinzufügen
</button>
</form>
<hr class="my-2" style="flex-shrink: 0" />
<div
style="
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
padding-bottom: 2rem;
"
>
<% if (!todayServices || todayServices.length === 0) { %>
<p class="text-muted">Noch keine Leistungen für heute.</p>
<% } else { %> <% todayServices.forEach(ls => { %>
<div class="border rounded p-2 mb-2 bg-light">
<strong><%= ls.name %></strong><br />
Menge: <%= ls.quantity %><br />
Preis: <%= Number(ls.price).toFixed(2) %> €
</div>
<% }) %> <% } %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</body>
</html>

View File

@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<title>Patientenübersicht</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/css/bootstrap.min.css" />
</head>
<body class="bg-light">
<!-- NAVBAR -->
<nav class="navbar navbar-dark bg-dark position-relative px-3">
<div
class="position-absolute top-50 start-50 translate-middle d-flex align-items-center gap-2 text-white"
>
<span style="font-size: 1.4rem">👥</span>
<span class="fw-semibold fs-5">Patientenübersicht</span>
</div>
<div class="ms-auto">
<a href="/dashboard" class="btn btn-outline-primary btn-sm">
⬅️ Dashboard
</a>
</div>
</nav>
<div class="container mt-4">
<!-- PATIENT INFO -->
<div class="card shadow mb-4">
<div class="card-body">
<h4>👤 <%= patient.firstname %> <%= patient.lastname %></h4>
<p class="text-muted mb-3">
Geboren am <%= new
Date(patient.birthdate).toLocaleDateString("de-DE") %>
</p>
<ul class="list-group">
<li class="list-group-item">
<strong>E-Mail:</strong> <%= patient.email || "-" %>
</li>
<li class="list-group-item">
<strong>Telefon:</strong> <%= patient.phone || "-" %>
</li>
<li class="list-group-item">
<strong>Adresse:</strong>
<%= patient.street || "" %> <%= patient.house_number || "" %>, <%=
patient.postal_code || "" %> <%= patient.city || "" %>
</li>
</ul>
</div>
</div>
<!-- =========================
MEDIKAMENTE & RECHNUNGEN
========================== -->
<div
class="row g-3"
style="
height: calc(100vh - 420px);
min-height: 300px;
padding-bottom: 3rem;
overflow: hidden;
"
>
<!-- 💊 MEDIKAMENTE -->
<div class="col-lg-6 h-100">
<div class="card shadow h-100">
<div class="card-body d-flex flex-column h-100">
<h5>💊 Aktuelle Medikamente</h5>
<div
style="
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
padding-bottom: 1.5rem;
"
>
<% if (medications.length === 0) { %>
<p class="text-muted">Keine aktiven Medikamente</p>
<% } else { %>
<table class="table table-sm table-bordered mt-2">
<thead class="table-light">
<tr>
<th>Medikament</th>
<th>Variante</th>
<th>Anweisung</th>
</tr>
</thead>
<tbody>
<% medications.forEach(m => { %>
<tr>
<td><%= m.medication_name %></td>
<td><%= m.variant_dosage %></td>
<td><%= m.dosage_instruction || "-" %></td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
</div>
</div>
</div>
</div>
<!-- 🧾 RECHNUNGEN -->
<div class="col-lg-6 h-100">
<div class="card shadow h-100">
<div class="card-body d-flex flex-column h-100">
<h5>🧾 Rechnungen</h5>
<div
style="
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
padding-bottom: 1.5rem;
"
>
<% if (invoices.length === 0) { %>
<p class="text-muted">Keine Rechnungen vorhanden</p>
<% } else { %>
<table class="table table-sm table-bordered mt-2">
<thead class="table-light">
<tr>
<th>Datum</th>
<th>Betrag</th>
<th>PDF</th>
</tr>
</thead>
<tbody>
<% invoices.forEach(i => { %>
<tr>
<td>
<%= new Date(i.invoice_date).toLocaleDateString("de-DE")
%>
</td>
<td><%= Number(i.total_amount).toFixed(2) %> €</td>
<td>
<% if (i.file_path) { %>
<a
href="<%= i.file_path %>"
target="_blank"
class="btn btn-sm btn-outline-primary"
>
📄 Öffnen
</a>
<% } else { %> - <% } %>
</td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,74 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Medikationsplan <%= patient.firstname %> <%= patient.lastname %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body class="bg-light">
<nav class="navbar navbar-dark bg-dark px-3">
<span class="navbar-brand">
📄 Medikationsplan <%= patient.firstname %> <%= patient.lastname %>
</span>
<a href="/patients/<%= patient.id %>/overview"
class="btn btn-outline-light btn-sm">
Zurück
</a>
</nav>
<div class="container mt-4">
<%- include("partials/flash") %>
<div class="card shadow">
<div class="card-body">
<table class="table table-bordered table-sm">
<thead class="table-light">
<tr>
<th>Medikament</th>
<th>Form</th>
<th>Dosierung</th>
<th>Packung</th>
<th>Anweisung</th>
<th>Zeitraum</th>
</tr>
</thead>
<tbody>
<% if (meds.length === 0) { %>
<tr>
<td colspan="6" class="text-center text-muted">
Keine aktuelle Medikation
</td>
</tr>
<% } %>
<% meds.forEach(m => { %>
<tr>
<td><%= m.medication %></td>
<td><%= m.form %></td>
<td><%= m.dosage %></td>
<td><%= m.package %></td>
<td><%= m.dosage_instruction || "-" %></td>
<td>
<%= m.start_date
? new Date(m.start_date).toLocaleDateString("de-DE")
: "-" %>
<%= m.end_date
? new Date(m.end_date).toLocaleDateString("de-DE")
: "laufend" %>
</td>
</tr>
<% }) %>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,257 +1,270 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<head>
<meta charset="UTF-8" />
<title>Patientenübersicht</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body class="bg-light">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/css/bootstrap.min.css" />
</head>
<body class="bg-light">
<nav class="navbar navbar-dark bg-dark position-relative px-3">
<!-- 🟢 ZENTRIERTER TITEL -->
<div
class="position-absolute top-50 start-50 translate-middle d-flex align-items-center gap-2 text-white"
>
<span style="font-size: 1.4rem">👥</span>
<span class="fw-semibold fs-5">Patientenübersicht</span>
</div>
<!-- 🟢 ZENTRIERTER TITEL -->
<div class="position-absolute top-50 start-50 translate-middle
d-flex align-items-center gap-2 text-white">
<span style="font-size: 1.4rem;">👥</span>
<span class="fw-semibold fs-5">Patientenübersicht</span>
</div>
<!-- 🔵 RECHTS: DASHBOARD -->
<div class="ms-auto">
<a href="/dashboard" class="btn btn-outline-primary btn-sm">
⬅️ Dashboard
</a>
</div>
<!-- 🔵 RECHTS: DASHBOARD -->
<div class="ms-auto">
<a href="/dashboard" class="btn btn-outline-primary btn-sm">
⬅️ Dashboard
</a>
</div>
</nav>
<div class="container-fluid mt-4">
<%- include("partials/flash") %>
<div class="container-fluid mt-4">
<%- include("partials/flash") %>
<!-- Aktionen oben -->
<div class="d-flex gap-2 mb-3">
<a href="/patients/create" class="btn btn-success">
+ Neuer Patient
</a>
</div>
<!-- Aktionen oben -->
<div class="d-flex gap-2 mb-3">
<a href="/patients/create" class="btn btn-success"> + Neuer Patient </a>
</div>
<div class="card shadow">
<div class="card shadow">
<div class="card-body">
<!-- Suchformular -->
<form method="GET" action="/patients" class="row g-2 mb-4">
<div class="col-md-3">
<input type="text"
name="firstname"
class="form-control"
placeholder="Vorname"
value="<%= query?.firstname || '' %>">
</div>
<div class="col-md-3">
<input type="text"
name="lastname"
class="form-control"
placeholder="Nachname"
value="<%= query?.lastname || '' %>">
</div>
<div class="col-md-3">
<input type="date"
name="birthdate"
class="form-control"
value="<%= query?.birthdate || '' %>">
</div>
<div class="col-md-3 d-flex gap-2">
<button class="btn btn-primary w-100">
Suchen
</button>
<a href="/patients" class="btn btn-secondary w-100">
Zurücksetzen
</a>
</div>
</form>
<!-- Tabelle -->
<div class="table-responsive">
<table class="table table-bordered table-hover align-middle table-sm">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>N.I.E. / DNI</th>
<th>Geschlecht</th>
<th>Geburtstag</th>
<th>E-Mail</th>
<th>Telefon</th>
<th>Adresse</th>
<th>Land</th>
<th>Status</th>
<th>Notizen</th>
<th>Erstellt</th>
<th>Geändert</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<% if (patients.length === 0) { %>
<tr>
<td colspan="13" class="text-center text-muted">
Keine Patienten gefunden
</td>
</tr>
<% } %>
<% patients.forEach(p => { %>
<tr>
<td><%= p.id %></td>
<td><strong><%= p.firstname %> <%= p.lastname %></strong></td>
<td><%= p.dni || "-" %></td>
<td>
<% if (p.gender === 'm') { %>m
<% } else if (p.gender === 'w') { %>w
<% } else if (p.gender === 'd') { %>d
<% } else { %>-<% } %>
</td>
<td><%= new Date(p.birthdate).toLocaleDateString("de-DE") %></td>
<td><%= p.email || "-" %></td>
<td><%= p.phone || "-" %></td>
<td>
<%= p.street || "" %> <%= p.house_number || "" %><br>
<%= p.postal_code || "" %> <%= p.city || "" %>
</td>
<td><%= p.country || "-" %></td>
<td>
<% if (p.active) { %>
<span class="badge bg-success">Aktiv</span>
<% } else { %>
<span class="badge bg-secondary">Inaktiv</span>
<% } %>
</td>
<td style="max-width: 200px;">
<%= p.notes ? p.notes.substring(0, 80) : "-" %>
</td>
<td><%= new Date(p.created_at).toLocaleString("de-DE") %></td>
<td><%= new Date(p.updated_at).toLocaleString("de-DE") %></td>
<!-- AKTIONEN -->
<td class="text-nowrap">
<div class="dropdown">
<button class="btn btn-sm btn-outline-secondary"
data-bs-toggle="dropdown">
Auswahl ▾
</button>
<ul class="dropdown-menu dropdown-menu-end position-fixed">
<!-- ✏️ BEARBEITEN -->
<li>
<a class="dropdown-item"
href="/patients/edit/<%= p.id %>">
✏️ Bearbeiten
</a>
</li>
<li><hr class="dropdown-divider"></li>
<!-- 🪑 WARTEZIMMER -->
<% if (p.waiting_room) { %>
<li>
<span class="dropdown-item text-muted">
🪑 Wartet bereits
</span>
</li>
<% } else { %>
<li>
<form method="POST"
action="/patients/waiting-room/<%= p.id %>">
<button class="dropdown-item">
🪑 Ins Wartezimmer
</button>
</form>
</li>
<% } %>
<li><hr class="dropdown-divider"></li>
<!-- 💊 MEDIKAMENTE -->
<li>
<a class="dropdown-item"
href="/patients/<%= p.id %>/medications">
💊 Medikamente
</a>
</li>
<li><hr class="dropdown-divider"></li>
<!-- 🔒 STATUS -->
<li>
<% if (p.active) { %>
<form method="POST"
action="/patients/deactivate/<%= p.id %>">
<button class="dropdown-item text-warning">
🔒 Sperren
</button>
</form>
<% } else { %>
<form method="POST"
action="/patients/activate/<%= p.id %>">
<button class="dropdown-item text-success">
🔓 Entsperren
</button>
</form>
<% } %>
</li>
<li><hr class="dropdown-divider"></li>
<!-- 📎 DATEI-UPLOAD -->
<li class="px-3 py-2">
<form method="POST"
action="/patients/<%= p.id %>/files"
enctype="multipart/form-data">
<input type="file"
name="file"
class="form-control form-control-sm mb-2"
required>
<button class="btn btn-sm btn-secondary w-100">
📎 Hochladen
</button>
</form>
</li>
</ul>
</div>
</td>
</tr>
<% }) %>
</tbody>
</table>
<!-- Suchformular -->
<form method="GET" action="/patients" class="row g-2 mb-4">
<div class="col-md-3">
<input
type="text"
name="firstname"
class="form-control"
placeholder="Vorname"
value="<%= query?.firstname || '' %>"
/>
</div>
</div>
</div>
<div class="col-md-3">
<input
type="text"
name="lastname"
class="form-control"
placeholder="Nachname"
value="<%= query?.lastname || '' %>"
/>
</div>
</div>
<script src="/js/bootstrap.bundle.min.js"></script>
</body>
<div class="col-md-3">
<input
type="date"
name="birthdate"
class="form-control"
value="<%= query?.birthdate || '' %>"
/>
</div>
<div class="col-md-3 d-flex gap-2">
<button class="btn btn-primary w-100">Suchen</button>
<a href="/patients" class="btn btn-secondary w-100">
Zurücksetzen
</a>
</div>
</form>
<!-- Tabelle -->
<div class="table-responsive">
<table
class="table table-bordered table-hover align-middle table-sm"
>
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>N.I.E. / DNI</th>
<th>Geschlecht</th>
<th>Geburtstag</th>
<th>E-Mail</th>
<th>Telefon</th>
<th>Adresse</th>
<th>Land</th>
<th>Status</th>
<th>Notizen</th>
<th>Erstellt</th>
<th>Geändert</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<% if (patients.length === 0) { %>
<tr>
<td colspan="13" class="text-center text-muted">
Keine Patienten gefunden
</td>
</tr>
<% } %> <% patients.forEach(p => { %>
<tr>
<td><%= p.id %></td>
<td><strong><%= p.firstname %> <%= p.lastname %></strong></td>
<td><%= p.dni || "-" %></td>
<td>
<% if (p.gender === 'm') { %>m <% } else if (p.gender ===
'w') { %>w <% } else if (p.gender === 'd') { %>d <% } else {
%>-<% } %>
</td>
<td>
<%= new Date(p.birthdate).toLocaleDateString("de-DE") %>
</td>
<td><%= p.email || "-" %></td>
<td><%= p.phone || "-" %></td>
<td>
<%= p.street || "" %> <%= p.house_number || "" %><br />
<%= p.postal_code || "" %> <%= p.city || "" %>
</td>
<td><%= p.country || "-" %></td>
<td>
<% if (p.active) { %>
<span class="badge bg-success">Aktiv</span>
<% } else { %>
<span class="badge bg-secondary">Inaktiv</span>
<% } %>
</td>
<td style="max-width: 200px">
<%= p.notes ? p.notes.substring(0, 80) : "-" %>
</td>
<td><%= new Date(p.created_at).toLocaleString("de-DE") %></td>
<td><%= new Date(p.updated_at).toLocaleString("de-DE") %></td>
<!-- AKTIONEN -->
<td class="text-nowrap">
<div class="dropdown">
<button
class="btn btn-sm btn-outline-secondary"
data-bs-toggle="dropdown"
>
Auswahl ▾
</button>
<ul
class="dropdown-menu dropdown-menu-end position-fixed"
>
<!-- ✏️ BEARBEITEN -->
<li>
<a
class="dropdown-item"
href="/patients/edit/<%= p.id %>"
>
✏️ Bearbeiten
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<!-- 🪑 WARTEZIMMER -->
<% if (p.waiting_room) { %>
<li>
<span class="dropdown-item text-muted">
🪑 Wartet bereits
</span>
</li>
<% } else { %>
<li>
<form
method="POST"
action="/patients/waiting-room/<%= p.id %>"
>
<button class="dropdown-item">
🪑 Ins Wartezimmer
</button>
</form>
</li>
<% } %>
<li><hr class="dropdown-divider" /></li>
<!-- 💊 MEDIKAMENTE -->
<li>
<a
class="dropdown-item"
href="/patients/<%= p.id %>/medications"
>
💊 Medikamente
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<!-- 🔒 STATUS -->
<li>
<% if (p.active) { %>
<form
method="POST"
action="/patients/deactivate/<%= p.id %>"
>
<button class="dropdown-item text-warning">
🔒 Sperren
</button>
</form>
<% } else { %>
<form
method="POST"
action="/patients/activate/<%= p.id %>"
>
<button class="dropdown-item text-success">
🔓 Entsperren
</button>
</form>
<% } %>
</li>
<!-- 📋 ÜBERSICHT -->
<li>
<a class="dropdown-item" href="/patients/<%= p.id %>">
📋 Übersicht
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<!-- 📎 DATEI-UPLOAD -->
<li class="px-3 py-2">
<form
method="POST"
action="/patients/<%= p.id %>/files"
enctype="multipart/form-data"
>
<input
type="file"
name="file"
class="form-control form-control-sm mb-2"
required
/>
<button class="btn btn-sm btn-secondary w-100">
📎 Hochladen
</button>
</form>
</li>
</ul>
</div>
</td>
</tr>
<% }) %>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="/js/bootstrap.bundle.min.js"></script>
</body>
</html>