Dr. Titel hinzugefügt und auch in den NOtizen

This commit is contained in:
Cay 2026-01-17 16:45:38 +00:00
parent 9b516a48c3
commit 1aef5a5749
11 changed files with 454 additions and 351 deletions

View File

@ -1,6 +1,10 @@
const db = require("../db");
const { createUser, getAllUsers } = require("../services/admin.service");
const bcrypt = require("bcrypt");
const {
createUser,
getAllUsers,
updateUserById,
} = require("../services/admin.service");
async function listUsers(req, res) {
const { q } = req.query;
@ -34,6 +38,7 @@ function showCreateUser(req, res) {
async function postCreateUser(req, res) {
let {
title,
first_name,
last_name,
username,
@ -43,6 +48,7 @@ async function postCreateUser(req, res) {
arztnummer,
} = req.body;
title = title?.trim();
first_name = first_name?.trim();
last_name = last_name?.trim();
username = username?.trim();
@ -69,11 +75,13 @@ async function postCreateUser(req, res) {
// Sicherheit: Mitarbeiter dürfen keine Arzt-Daten haben
fachrichtung = null;
arztnummer = null;
title = null;
}
try {
await createUser(
db,
title,
first_name,
last_name,
username,
@ -265,6 +273,50 @@ async function showInvoiceOverview(req, res) {
}
}
async function updateUser(req, res) {
const userId = req.params.id;
let { title, first_name, last_name, username, role } = req.body;
title = title?.trim() || null;
first_name = first_name?.trim();
last_name = last_name?.trim();
username = username?.trim();
role = role?.trim();
try {
// ✅ Fehlende Felder aus DB holen (weil disabled inputs nicht gesendet werden)
const [rows] = await db
.promise()
.query("SELECT * FROM users WHERE id = ?", [userId]);
if (!rows.length) {
req.session.flash = { type: "danger", message: "User nicht gefunden" };
return res.redirect("/admin/users");
}
const current = rows[0];
// ✅ Fallback: wenn Felder nicht gesendet wurden -> alte Werte behalten
const updatedData = {
title: title ?? current.title,
first_name: first_name ?? current.first_name,
last_name: last_name ?? current.last_name,
username: username ?? current.username,
role: role ?? current.role,
};
await updateUserById(db, userId, updatedData);
req.session.flash = { type: "success", message: "User aktualisiert ✅" };
return res.redirect("/admin/users");
} catch (err) {
console.error(err);
req.session.flash = { type: "danger", message: "Fehler beim Speichern" };
return res.redirect("/admin/users");
}
}
module.exports = {
listUsers,
showCreateUser,
@ -274,4 +326,5 @@ module.exports = {
activateUser,
deactivateUser,
showInvoiceOverview,
updateUser,
};

View File

@ -210,13 +210,14 @@ function moveToWaitingRoom(req, res) {
`
UPDATE patients
SET waiting_room = 1,
discharged = 0
discharged = 0,
active = 1
WHERE id = ?
`,
[id],
(err) => {
if (err) return res.send("Fehler beim Verschieben ins Wartezimmer");
res.redirect("/patients");
return res.redirect("/dashboard"); // optional: direkt Dashboard
}
);
}
@ -245,10 +246,15 @@ function showPatientOverview(req, res) {
`;
const notesSql = `
SELECT *
FROM patient_notes
WHERE patient_id = ?
ORDER BY created_at DESC
SELECT
pn.*,
u.title,
u.first_name,
u.last_name
FROM patient_notes pn
LEFT JOIN users u ON pn.created_by = u.id
WHERE pn.patient_id = ?
ORDER BY pn.created_at DESC
`;
const medicationVariantsSql = `
@ -381,8 +387,9 @@ function addPatientNote(req, res) {
}
db.query(
"INSERT INTO patient_notes (patient_id, note) VALUES (?, ?)",
[patientId, note],
"INSERT INTO patient_notes (patient_id, created_by, note) VALUES (?, ?, ?)",
[patientId, req.session.user.id, note],
(err) => {
if (err) return res.send("Fehler beim Speichern der Notiz");
res.redirect(`/patients/${patientId}/overview`);
@ -407,11 +414,21 @@ function dischargePatient(req, res) {
const patientId = req.params.id;
db.query(
"UPDATE patients SET discharged = 1 WHERE id = ?",
`
UPDATE patients
SET discharged = 1,
waiting_room = 0,
active = 0
WHERE id = ?
`,
[patientId],
(err) => {
if (err) return res.send("Fehler beim Entlassen des Patienten");
res.redirect("/waiting-room");
if (err) {
console.error(err);
return res.send("Fehler beim Entlassen des Patienten");
}
return res.redirect("/dashboard");
}
);
}
@ -462,7 +479,8 @@ function movePatientToWaitingRoom(req, res) {
UPDATE patients
SET waiting_room = 1,
discharged = 0,
status = 'waiting'
status = 'waiting',
active = 1
WHERE id = ?
`,
[patientId],
@ -481,7 +499,7 @@ function movePatientToWaitingRoom(req, res) {
message: "Patient wurde ins Wartezimmer gesetzt",
};
res.redirect("/waiting-room");
return res.redirect("/dashboard");
}
);
}

View File

@ -62,3 +62,19 @@
opacity: 0.4;
}
.auto-hide-flash {
animation: flashFadeOut 3s forwards;
}
@keyframes flashFadeOut {
0% {
opacity: 1;
}
70% {
opacity: 1;
}
100% {
opacity: 0;
visibility: hidden;
}
}

View File

@ -10,6 +10,7 @@ const {
activateUser,
deactivateUser,
showInvoiceOverview,
updateUser,
} = require("../controllers/admin.controller");
const { requireAdmin } = require("../middleware/auth.middleware");
@ -23,5 +24,6 @@ router.post("/users/reset-password/:id", requireAdmin, resetUserPassword);
router.post("/users/activate/:id", requireAdmin, activateUser);
router.post("/users/deactivate/:id", requireAdmin, deactivateUser);
router.get("/invoices", requireAdmin, showInvoiceOverview);
router.post("/users/update/:id", requireAdmin, updateUser);
module.exports = router;

View File

@ -2,6 +2,7 @@ const bcrypt = require("bcrypt");
async function createUser(
db,
title,
first_name,
last_name,
username,
@ -15,9 +16,18 @@ async function createUser(
return new Promise((resolve, reject) => {
db.query(
`INSERT INTO users
(first_name, last_name, username, password, role, fachrichtung, arztnummer, active)
VALUES (?, ?, ?, ?, ?, ?, ?, 1)`,
[first_name, last_name, username, hash, role, fachrichtung, arztnummer],
(title, first_name, last_name, username, password, role, fachrichtung, arztnummer, active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`,
[
title,
first_name,
last_name,
username,
hash,
role,
fachrichtung,
arztnummer,
],
(err) => {
if (err) {
if (err.code === "ER_DUP_ENTRY") {
@ -57,7 +67,27 @@ async function getAllUsers(db, search = null) {
return rows;
}
async function updateUserById(db, userId, data) {
const { title, first_name, last_name, username, role } = data;
const [result] = await db.promise().query(
`
UPDATE users
SET title = ?,
first_name = ?,
last_name = ?,
username = ?,
role = ?
WHERE id = ?
`,
[title, first_name, last_name, username, role, userId]
);
return result;
}
module.exports = {
createUser,
getAllUsers,
updateUserById,
};

View File

@ -1,16 +1,15 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>Benutzer anlegen</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap.min.css" />
</head>
<body class="bg-light">
<div class="container mt-5">
<%- include("partials/flash") %>
<div class="card shadow mx-auto" style="max-width: 500px;">
<div class="card shadow mx-auto" style="max-width: 500px">
<div class="card-body">
<h3 class="text-center mb-3">Benutzer anlegen</h3>
@ -19,56 +18,74 @@
<% } %>
<form method="POST" action="/admin/create-user">
<!-- VORNAME -->
<input class="form-control mb-3"
<input
class="form-control mb-3"
name="first_name"
placeholder="Vorname"
required>
required
/>
<!-- NACHNAME -->
<input class="form-control mb-3"
<input
class="form-control mb-3"
name="last_name"
placeholder="Nachname"
required>
required
/>
<!-- TITEL -->
<input
class="form-control mb-3"
name="title"
placeholder="Titel (z.B. Dr., Prof.)"
/>
<!-- BENUTZERNAME (LOGIN) -->
<input class="form-control mb-3"
<input
class="form-control mb-3"
name="username"
placeholder="Benutzername (Login)"
required>
required
/>
<!-- PASSWORT -->
<input class="form-control mb-3"
<input
class="form-control mb-3"
type="password"
name="password"
placeholder="Passwort"
required>
required
/>
<!-- ROLLE -->
<select class="form-select mb-3"
<select
class="form-select mb-3"
name="role"
id="roleSelect"
required>
required
>
<option value="">Rolle wählen</option>
<option value="mitarbeiter">Mitarbeiter</option>
<option value="arzt">Arzt</option>
</select>
<!-- ARZT-FELDER -->
<div id="arztFields" style="display:none;">
<input class="form-control mb-3"
<div id="arztFields" style="display: none">
<input
class="form-control mb-3"
name="fachrichtung"
placeholder="Fachrichtung">
placeholder="Fachrichtung"
/>
<input class="form-control mb-3"
<input
class="form-control mb-3"
name="arztnummer"
placeholder="Arztnummer">
placeholder="Arztnummer"
/>
</div>
<button class="btn btn-primary w-100">
Benutzer erstellen
</button>
<button class="btn btn-primary w-100">Benutzer erstellen</button>
</form>
<div class="text-center mt-3">
@ -79,12 +96,13 @@
</div>
<script>
document.getElementById("roleSelect").addEventListener("change", function () {
document
.getElementById("roleSelect")
.addEventListener("change", function () {
const arztFields = document.getElementById("arztFields");
arztFields.style.display = this.value === "arzt" ? "block" : "none";
});
</script>
<script src="/js/admin_create_user.js" defer></script>
</body>
</html>

View File

@ -1,100 +1,168 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>User Verwaltung</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap 5 -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/bootstrap-icons/bootstrap-icons.min.css">
<script src="/js/bootstrap.bundle.min.js"></script>
<!-- ✅ Inline Edit wie bei medications -->
<script src="/js/services-lock.js"></script>
<style>
input.form-control { box-shadow: none !important; }
input.form-control:disabled {
background-color: #fff !important;
color: #212529 !important;
opacity: 1 !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
}
input.form-control:disabled:focus {
box-shadow: none !important;
outline: none !important;
}
/* Inaktive User ROT */
tr.table-secondary > td {
background-color: #f8d7da !important;
}
</style>
</head>
<body class="bg-light">
<!-- NAVBAR -->
<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">
<div class="position-absolute top-50 start-50 translate-middle d-flex align-items-center gap-2 text-white">
<i class="bi bi-shield-lock fs-4"></i>
<span class="fw-semibold fs-5">User Verwaltung</span>
</div>
<!-- 🔵 RECHTS: DASHBOARD -->
<div class="ms-auto">
<a href="/dashboard" class="btn btn-outline-primary btn-sm">
⬅️ Dashboard
</a>
<a href="/dashboard" class="btn btn-outline-light btn-sm">⬅️ Dashboard</a>
</div>
</nav>
<!-- CONTENT -->
<div class="container mt-4">
<%- include("partials/flash") %>
<div class="card shadow">
<div class="card-body">
<h4 class="mb-3">Benutzerübersicht</h4>
<div class="table-responsive">
<!-- Neu -->
<div class="mb-3 text-end">
<a href="/admin/create-user" class="btn btn-primary">
+ 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">
<!-- 🔍 Suche -->
<form method="GET" action="/admin/users" class="d-flex gap-2 mb-3">
<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>
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>
<a href="/admin/users" class="btn btn-outline-secondary">Reset</a>
<% } %>
</form>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover table-sm align-middle">
<table class="table table-bordered table-hover align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Titel</th>
<th>Vorname</th>
<th>Nachname</th>
<th>Username</th>
<th>Rolle</th>
<th>Status</th>
<th style="width: 340px;">Aktionen</th>
<th style="width: 260px;">Aktionen</th>
</tr>
</thead>
<tbody>
<tbody>
<% users.forEach(u => { %>
<tr>
<tr class="<%= u.active ? '' : 'table-secondary' %>">
<!-- ✅ UPDATE-FORM (wie medications) -->
<form method="POST" action="/admin/users/update/<%= u.id %>">
<td><%= u.id %></td>
<td>
<strong><%= u.first_name %> <%= u.last_name %></strong><br>
<small class="text-muted">@<%= u.username %></small>
<input
type="text"
name="title"
value="<%= u.title || '' %>"
class="form-control form-control-sm"
disabled
>
</td>
<td>
<% if (u.role === "arzt") { %>
<span class="badge bg-warning text-dark">Arzt</span>
<% } else { %>
<span class="badge bg-info text-dark">Mitarbeiter</span>
<% } %>
<input
type="text"
name="first_name"
value="<%= u.first_name %>"
class="form-control form-control-sm"
disabled
>
</td>
<td>
<input
type="text"
name="last_name"
value="<%= u.last_name %>"
class="form-control form-control-sm"
disabled
>
</td>
<td>
<input
type="text"
name="username"
value="<%= u.username %>"
class="form-control form-control-sm"
disabled
>
</td>
<td>
<select
name="role"
class="form-select form-select-sm"
disabled
>
<option value="mitarbeiter" <%= u.role === "mitarbeiter" ? "selected" : "" %>>
Mitarbeiter
</option>
<option value="arzt" <%= u.role === "arzt" ? "selected" : "" %>>
Arzt
</option>
</select>
</td>
<td class="text-center">
<% if (u.active === 0) { %>
<span class="badge bg-secondary">Inaktiv</span>
<% } else if (u.lock_until && new Date(u.lock_until) > new Date()) { %>
@ -104,78 +172,42 @@
<% } %>
</td>
<td>
<td class="d-flex gap-2">
<button class="btn btn-sm btn-outline-success save-btn" disabled>
💾
</button>
<button type="button" class="btn btn-sm btn-outline-warning lock-btn">
🔓
</button>
</form>
<!-- ✅ Aktiv / Deaktiv separat -->
<% if (u.id !== currentUser.id) { %>
<!-- AKTIV / INAKTIV -->
<% if (u.active === 1) { %>
<form method="POST"
action="/admin/users/deactivate/<%= u.id %>"
class="mb-1">
<button class="btn btn-sm btn-secondary w-100">
Deaktivieren
<form method="POST" action="/admin/users/<%= u.active ? "deactivate" : "activate" %>/<%= u.id %>">
<button class="btn btn-sm <%= u.active ? "btn-outline-danger" : "btn-outline-success" %>">
<%= u.active ? "⛔" : "✅" %>
</button>
</form>
<% } else { %>
<form method="POST"
action="/admin/users/activate/<%= u.id %>"
class="mb-1">
<button class="btn btn-sm btn-success w-100">
Aktivieren
</button>
</form>
<% } %>
<!-- ROLLE ÄNDERN -->
<form method="POST"
action="/admin/users/change-role/<%= u.id %>"
class="mb-1">
<select name="role"
class="form-select form-select-sm mb-1">
<option value="mitarbeiter"
<%= u.role === "mitarbeiter" ? "selected" : "" %>>
Mitarbeiter
</option>
<option value="arzt"
<%= u.role === "arzt" ? "selected" : "" %>>
Arzt
</option>
</select>
<button class="btn btn-sm btn-warning w-100">
Rolle ändern
</button>
</form>
<!-- PASSWORT RESET -->
<form method="POST"
action="/admin/users/reset-password/<%= u.id %>">
<input type="password"
name="password"
class="form-control form-control-sm mb-1"
placeholder="Neues Passwort"
required>
<button class="btn btn-sm btn-danger w-100"
onclick="return confirm('Passwort wirklich zurücksetzen?')">
Passwort zurücksetzen
</button>
</form>
<% } else { %>
<span class="text-muted fst-italic">
<span class="text-muted fst-italic small align-self-center">
Du selbst
</span>
<% } %>
</td>
</tr>
<% }) %>
<% }) %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>

View File

@ -166,10 +166,6 @@
<i class="bi bi-people"></i> Patienten
</a>
<a href="/waiting-room" class="nav-item">
<i class="bi bi-chair"></i> Wartezimmer
</a>
<a href="/medications" class="nav-item">
<i class="bi bi-capsule"></i> Medikamente
</a>

View File

@ -1,5 +1,8 @@
<% if (flash) { %>
<div class="alert alert-<%= flash.type %> alert-dismissible fade show" role="alert">
<div
class="alert alert-<%= flash.type %> alert-dismissible fade show auto-hide-flash"
role="alert"
>
<%= flash.message %>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>

View File

@ -84,13 +84,13 @@
✏️ Patient bearbeiten
</a>
<form
method="POST"
action="/patients/<%= patient.id %>/discharge"
class="d-inline"
onsubmit="return confirm('Patient wirklich entlassen?')"
<form method="POST" action="/patients/<%= patient.id %>/discharge">
<button
class="btn btn-danger btn-sm"
onclick="return confirm('Patient wirklich entlassen?')"
>
<button class="btn btn-danger">🟥 Entlassen</button>
✅ Entlassen
</button>
</form>
</div>
@ -142,7 +142,9 @@
<% } 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") %>
<%= new Date(n.created_at).toLocaleString("de-DE") %> <% if
(n.first_name && n.last_name) { %> <%= (n.title ? n.title
+ " " : "") %><%= n.first_name %> <%= n.last_name %> <% } %>
</div>
<div><%= n.note %></div>
</div>

View File

@ -1,67 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<title>Wartezimmer</title>
<link rel="stylesheet" href="/css/bootstrap.min.css" />
<link rel="stylesheet" href="/bootstrap-icons/bootstrap-icons.min.css" />
</head>
<body>
<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">Wartezimmer</span>
</div>
<!-- 🔵 RECHTS: DASHBOARD -->
<div class="ms-auto">
<a href="/dashboard" class="btn btn-outline-primary btn-sm">
⬅️ Dashboard
</a>
</div>
</nav>
<div class="container mt-4">
<!-- ✅ EINMAL Flash anzeigen -->
<%- include("partials/flash") %> <% if (patients.length === 0) { %>
<div class="alert alert-info">Keine Patienten im Wartezimmer</div>
<% } else { %>
<table class="table table-bordered table-hover">
<thead class="table-light">
<tr>
<th>Name</th>
<th>Geburtstag</th>
<th>Aktion</th>
</tr>
</thead>
<tbody>
<% patients.forEach(p => { %>
<tr>
<td><strong><%= p.firstname %> <%= p.lastname %></strong></td>
<td><%= new Date(p.birthdate).toLocaleDateString("de-DE") %></td>
<td>
<% if (user.role === 'arzt') { %>
<form
method="POST"
action="patients/waiting-room/call/<%= p.id %>"
class="d-inline"
>
<button class="btn btn-sm btn-success">▶️ Aufrufen</button>
</form>
<% } else { %>
<span class="text-muted">🔒 Nur Arzt</span>
<% } %>
</td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
</div>
</body>
</html>