Weitere übersetzungen

This commit is contained in:
Cay 2026-02-02 12:20:51 -01:00
parent 3f70e1f7f9
commit 65bb75d437
10 changed files with 255 additions and 154 deletions

View File

@ -13,14 +13,27 @@ const safe = (v) => {
* GET: Firmendaten anzeigen * GET: Firmendaten anzeigen
*/ */
async function getCompanySettings(req, res) { async function getCompanySettings(req, res) {
const [[company]] = await db.promise().query( try {
"SELECT * FROM company_settings LIMIT 1" const [[company]] = await db
); .promise()
.query("SELECT * FROM company_settings LIMIT 1");
res.render("admin/company-settings", { res.render("admin/company-settings", {
user: req.user, layout: "layout", // 🔥 wichtig
company: company || {} title: "Firmendaten", // 🔥 DAS FEHLTE
active: "companySettings", // 🔥 Sidebar aktiv
sidebarPartial: "partials/admin-sidebar",
company: company || {},
user: req.session.user, // 🔥 konsistent
lang: req.session.lang || "de"
// t kommt aus res.locals
}); });
} catch (err) {
console.error(err);
res.status(500).send("Datenbankfehler");
}
} }
/** /**

View File

@ -8,9 +8,15 @@ async function showDashboard(req, res) {
const waitingPatients = await getWaitingPatients(db); const waitingPatients = await getWaitingPatients(db);
res.render("dashboard", { res.render("dashboard", {
layout: "layout", // 🔥 DAS FEHLTE
title: "Dashboard",
active: "dashboard",
sidebarPartial: "partials/sidebar",
waitingPatients,
user: req.session.user, user: req.session.user,
waitingPatients lang: req.session.lang || "de"
}); });
} catch (err) { } catch (err) {
console.error(err); console.error(err);

View File

@ -66,7 +66,8 @@
"dashboard": { "dashboard": {
"welcome": "Willkommen", "welcome": "Willkommen",
"waitingRoom": "Wartezimmer-Monitor", "waitingRoom": "Wartezimmer-Monitor",
"noWaitingPatients": "Keine Patienten im Wartezimmer." "noWaitingPatients": "Keine Patienten im Wartezimmer.",
"title":"Dashboard"
}, },
"adminSidebar": { "adminSidebar": {
@ -75,7 +76,8 @@
"user":"Benutzer", "user":"Benutzer",
"invocieoverview":"Rechnungsübersicht", "invocieoverview":"Rechnungsübersicht",
"seriennumber":"Seriennummer", "seriennumber":"Seriennummer",
"databasetable":"Datenbank" "databasetable":"Datenbank",
"companysettings":"Firmendaten"
}, },
"adminuseroverview": { "adminuseroverview": {

View File

@ -39,7 +39,7 @@
"birthday":"Fecha de nacimiento", "birthday":"Fecha de nacimiento",
"email":"Correo electrónico", "email":"Correo electrónico",
"phone":"Teléfono", "phone":"Teléfono",
"address":"AdreDirecciónsse", "address":"Dirección",
"country":"País", "country":"País",
"notice":"Notas", "notice":"Notas",
"create":"Creado", "create":"Creado",
@ -67,7 +67,8 @@
"dashboard": { "dashboard": {
"welcome": "Bienvenido", "welcome": "Bienvenido",
"waitingRoom": "Monitor sala de espera", "waitingRoom": "Monitor sala de espera",
"noWaitingPatients": "No hay pacientes en la sala de espera." "noWaitingPatients": "No hay pacientes en la sala de espera.",
"title":"Dashboard"
}, },
"adminSidebar": { "adminSidebar": {
@ -76,7 +77,8 @@
"user":"usuario", "user":"usuario",
"invocieoverview":"Resumen de facturas", "invocieoverview":"Resumen de facturas",
"seriennumber":"número de serie", "seriennumber":"número de serie",
"databasetable":"base de datos" "databasetable":"base de datos",
"companysettings":"Datos de la empresa"
}, },
"adminuseroverview": { "adminuseroverview": {

View File

@ -6,6 +6,7 @@ const path = require("path");
const { exec } = require("child_process"); const { exec } = require("child_process");
const multer = require("multer"); const multer = require("multer");
const { NodeSSH } = require("node-ssh"); const { NodeSSH } = require("node-ssh");
const uploadLogo = require("../middleware/uploadLogo");
// ✅ Upload Ordner für Restore Dumps // ✅ Upload Ordner für Restore Dumps
@ -31,6 +32,13 @@ const { loadConfig, saveConfig } = require("../config-manager");
// ✅ DB (für resetPool) // ✅ DB (für resetPool)
const db = require("../db"); const db = require("../db");
// ✅ Firmendaten
const {
getCompanySettings,
saveCompanySettings
} = require("../controllers/companySettings.controller");
/* ========================== /* ==========================
VERWALTUNG (NUR ADMIN) VERWALTUNG (NUR ADMIN)
========================== */ ========================== */
@ -485,4 +493,20 @@ router.post("/database/restore", requireAdmin, async (req, res) => {
========================== */ ========================== */
router.get("/invoices", requireAdmin, showInvoiceOverview); router.get("/invoices", requireAdmin, showInvoiceOverview);
/* ==========================
Firmendaten
========================== */
router.get(
"/company-settings",
requireAdmin,
getCompanySettings
);
router.post(
"/company-settings",
requireAdmin,
uploadLogo.single("logo"),
saveCompanySettings
);
module.exports = router; module.exports = router;

View File

@ -1,19 +1,21 @@
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
const { requireArzt } = require("../middleware/auth.middleware"); const { requireAdmin } = require("../middleware/auth.middleware");
const uploadLogo = require("../middleware/uploadLogo"); const uploadLogo = require("../middleware/uploadLogo");
const { const {
getCompanySettings, getCompanySettings,
saveCompanySettings, saveCompanySettings,
} = require("../controllers/companySettings.controller"); } = require("../controllers/companySettings.controller");
router.get("/admin/company-settings", requireArzt, getCompanySettings); // ✅ NUR der relative Pfad
router.get("/company-settings", requireAdmin, getCompanySettings);
router.post( router.post(
"/admin/company-settings", "/company-settings",
requireArzt, requireAdmin,
uploadLogo.single("logo"), // 🔑 MUSS VOR DEM CONTROLLER KOMMEN uploadLogo.single("logo"),
saveCompanySettings, saveCompanySettings
); );
module.exports = router; module.exports = router;

View File

@ -1,101 +1,157 @@
<!DOCTYPE html> <%- include("../partials/page-header", {
<html lang="de"> user,
<head> title,
<meta charset="UTF-8"> subtitle: "",
<title>Firmendaten</title> showUserName: true
<link rel="stylesheet" href="/css/bootstrap.min.css"> }) %>
</head>
<body class="bg-light">
<div class="container mt-4"> <div class="content p-4">
<h3 class="mb-4">🏢 Firmendaten</h3>
<form method="POST" action="/admin/company-settings" enctype="multipart/form-data"> <%- include("../partials/flash") %>
<div class="container-fluid">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="mb-4">
<i class="bi bi-building"></i>
<%= title %>
</h5>
<form
method="POST"
action="/admin/company-settings"
enctype="multipart/form-data"
>
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">Firmenname</label> <label class="form-label">Firmenname</label>
<input class="form-control" name="company_name" <input
value="<%= company.company_name || '' %>" required> class="form-control"
name="company_name"
value="<%= settings.company_name || '' %>"
required
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">Rechtsform</label> <label class="form-label">Rechtsform</label>
<input class="form-control" name="company_legal_form" <input
value="<%= company.company_legal_form || '' %>"> class="form-control"
name="company_legal_form"
value="<%= settings.company_legal_form || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">Inhaber / Geschäftsführer</label> <label class="form-label">Inhaber / Geschäftsführer</label>
<input class="form-control" name="company_owner" <input
value="<%= company.company_owner || '' %>"> class="form-control"
name="company_owner"
value="<%= settings.company_owner || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">E-Mail</label> <label class="form-label">E-Mail</label>
<input class="form-control" name="email" <input
value="<%= company.email || '' %>"> class="form-control"
name="email"
value="<%= settings.email || '' %>"
>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<label class="form-label">Straße</label> <label class="form-label">Straße</label>
<input class="form-control" name="street" <input
value="<%= company.street || '' %>"> class="form-control"
name="street"
value="<%= settings.street || '' %>"
>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label">Hausnummer</label> <label class="form-label">Hausnummer</label>
<input class="form-control" name="house_number" <input
value="<%= company.house_number || '' %>"> class="form-control"
name="house_number"
value="<%= settings.house_number || '' %>"
>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label">PLZ</label> <label class="form-label">PLZ</label>
<input class="form-control" name="postal_code" <input
value="<%= company.postal_code || '' %>"> class="form-control"
name="postal_code"
value="<%= settings.postal_code || '' %>"
>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<label class="form-label">Ort</label> <label class="form-label">Ort</label>
<input class="form-control" name="city" <input
value="<%= company.city || '' %>"> class="form-control"
name="city"
value="<%= settings.city || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">Land</label> <label class="form-label">Land</label>
<input class="form-control" name="country" <input
value="<%= company.country || 'Deutschland' %>"> class="form-control"
name="country"
value="<%= settings.country || 'Deutschland' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">USt-ID / Steuernummer</label> <label class="form-label">USt-ID / Steuernummer</label>
<input class="form-control" name="vat_id" <input
value="<%= company.vat_id || '' %>"> class="form-control"
name="vat_id"
value="<%= settings.vat_id || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">Bank</label> <label class="form-label">Bank</label>
<input class="form-control" name="bank_name" <input
value="<%= company.bank_name || '' %>"> class="form-control"
name="bank_name"
value="<%= settings.bank_name || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">IBAN</label> <label class="form-label">IBAN</label>
<input class="form-control" name="iban" <input
value="<%= company.iban || '' %>"> class="form-control"
name="iban"
value="<%= settings.iban || '' %>"
>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label">BIC</label> <label class="form-label">BIC</label>
<input class="form-control" name="bic" <input
value="<%= company.bic || '' %>"> class="form-control"
name="bic"
value="<%= settings.bic || '' %>"
>
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="form-label">Rechnungs-Footer</label> <label class="form-label">Rechnungs-Footer</label>
<textarea class="form-control" rows="3" <textarea
name="invoice_footer_text"><%= company.invoice_footer_text || '' %></textarea> class="form-control"
rows="3"
name="invoice_footer_text"
><%= settings.invoice_footer_text || '' %></textarea>
</div> </div>
<div class="col-12"> <div class="col-12">
@ -107,11 +163,11 @@
accept="image/png, image/jpeg" accept="image/png, image/jpeg"
> >
<% if (company.invoice_logo_path) { %> <% if (settings.invoice_logo_path) { %>
<div class="mt-2"> <div class="mt-2">
<small class="text-muted">Aktuelles Logo:</small><br> <small class="text-muted">Aktuelles Logo:</small><br>
<img <img
src="<%= company.invoice_logo_path %>" src="<%= settings.invoice_logo_path %>"
style="max-height:80px; border:1px solid #ccc; padding:4px;" style="max-height:80px; border:1px solid #ccc; padding:4px;"
> >
</div> </div>
@ -120,13 +176,21 @@
</div> </div>
<div class="mt-4"> <div class="mt-4 d-flex gap-2">
<button class="btn btn-primary">💾 Speichern</button> <button class="btn btn-primary">
<a href="/dashboard" class="btn btn-secondary">Zurück</a> <i class="bi bi-save"></i>
<%= t.global.save %>
</button>
<a href="/dashboard" class="btn btn-secondary">
Zurück
</a>
</div> </div>
</form> </form>
</div>
</div> </div>
</body> </div>
</html> </div>

View File

@ -1,15 +1,8 @@
<div class="layout"> <!-- KEIN layout, KEINE sidebar, KEIN main -->
<!-- ✅ SIDEBAR -->
<%- include("partials/sidebar", { user, active: "patients", lang }) %>
<!-- ✅ MAIN -->
<div class="main">
<!-- ✅ HEADER (inkl. Uhrzeit) -->
<%- include("partials/page-header", { <%- include("partials/page-header", {
user, user,
title: "Dashboard", title: t.dashboard.title,
subtitle: "", subtitle: "",
showUserName: true, showUserName: true,
hideDashboardButton: true hideDashboardButton: true
@ -17,50 +10,34 @@
<div class="content p-4"> <div class="content p-4">
<!-- Flash Messages -->
<%- include("partials/flash") %> <%- include("partials/flash") %>
<!-- =========================
WARTEZIMMER MONITOR
========================= -->
<div class="waiting-monitor"> <div class="waiting-monitor">
<h5 class="mb-3">🪑 <%=t.global.waitingroomtext%></h5> <h5 class="mb-3">🪑 <%= t.dashboard.waitingRoom %></h5>
<div class="waiting-grid"> <div class="waiting-grid">
<% if (waitingPatients && waitingPatients.length > 0) { %> <% if (waitingPatients && waitingPatients.length > 0) { %>
<% waitingPatients.forEach(p => { %> <% waitingPatients.forEach(p => { %>
<% if (user.role === 'arzt') { %> <% if (user.role === "arzt") { %>
<form method="POST" action="/patients/<%= p.id %>/call" style="width:100%; margin:0;"> <form method="POST" action="/patients/<%= p.id %>/call">
<button type="submit" class="waiting-slot occupied clickable waiting-btn"> <button class="waiting-slot occupied clickable">
<div class="patient-text"> <div><%= p.firstname %> <%= p.lastname %></div>
<div class="name"><%= p.firstname %> <%= p.lastname %></div>
<div class="birthdate">
<%= new Date(p.birthdate).toLocaleDateString("de-DE") %>
</div>
</div>
</button> </button>
</form> </form>
<% } else { %> <% } else { %>
<div class="waiting-slot occupied"> <div class="waiting-slot occupied">
<div class="patient-text"> <div><%= p.firstname %> <%= p.lastname %></div>
<div class="name"><%= p.firstname %> <%= p.lastname %></div>
<div class="birthdate">
<%= new Date(p.birthdate).toLocaleDateString("de-DE") %>
</div>
</div>
</div> </div>
<% } %> <% } %>
<% }) %> <% }) %>
<% } else { %> <% } else { %>
<div class="text-muted"><%=t.global.waitingroomtextnopatient%></div> <div class="text-muted">
<%= t.dashboard.noWaitingPatients %>
</div>
<% } %> <% } %>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>

View File

@ -20,7 +20,6 @@
<body> <body>
<div class="layout"> <div class="layout">
<!-- ✅ Sidebar dynamisch --> <!-- ✅ Sidebar dynamisch -->
<% if (typeof sidebarPartial !== "undefined" && sidebarPartial) { %> <% if (typeof sidebarPartial !== "undefined" && sidebarPartial) { %>
<%- include(sidebarPartial, { <%- include(sidebarPartial, {

View File

@ -26,6 +26,18 @@
<div class="sidebar-menu"> <div class="sidebar-menu">
<!-- ✅ Firmendaten Verwaltung -->
<a
href="<%= hrefIfAllowed(isAdmin, '/admin/company-settings') %>"
class="nav-item <%= active === 'companySettings' ? 'active' : '' %> <%= lockClass(isAdmin) %>"
title="<%= isAdmin ? '' : 'Nur Admin' %>"
>
<i class="bi bi-people"></i> <%= t.adminSidebar.companysettings %>
<% if (!isAdmin) { %>
<span style="margin-left:auto;"><i class="bi bi-lock-fill"></i></span>
<% } %>
</a>
<!-- ✅ User Verwaltung --> <!-- ✅ User Verwaltung -->
<a <a
href="<%= hrefIfAllowed(isAdmin, '/admin/users') %>" href="<%= hrefIfAllowed(isAdmin, '/admin/users') %>"