403 und 404 hübsch gemacht

This commit is contained in:
Bkolb 2026-04-11 19:35:07 +02:00
parent 6789ac73c8
commit 882eb1af66
5 changed files with 332 additions and 35 deletions

View File

@ -336,7 +336,7 @@ def allgemein():
@app.route("/login", methods=["GET", "POST"])
def login():
error_message = ""
next_url = request.args.get("next") or request.form.get("next") or url_for("preise")
next_url = request.args.get("next") or request.form.get("next") or url_for("course_list")
if request.method == "POST":
email = request.form.get("email", "").strip().lower()
@ -2463,3 +2463,19 @@ def download_certificate_admin(guid):
filename = f"{guid}.pdf"
return send_from_directory(certificate_dir, filename, as_attachment=True)
@app.errorhandler(403)
def forbidden(e):
return render_template(
"403.html",
page_title="Zugriff verweigert",
**get_current_user()
), 403
@app.errorhandler(404)
def not_found(e):
return render_template(
"404.html",
page_title="Seite nicht gefunden",
**get_current_user()
), 404

View File

@ -1,18 +1,31 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page_title }}</title>
<link rel="stylesheet" href="/styles/site.css">
</head>
<body>
<main class="content-area">
<section class="content-box">
<h1>Kein Zugriff</h1>
<p>Sie haben keine Berechtigung für diese Seite.</p>
<p><a href="/home">Zurück zur Startseite</a></p>
</section>
</main>
</body>
</html>
{% extends "base.html" %}
{% block content %}
<div class="error-page">
<div class="error-card">
<div class="error-icon">🚫</div>
<h1>Zugriff verweigert</h1>
<p>
Sie haben keine Berechtigung, diese Seite aufzurufen.
</p>
<div class="error-actions">
{% if is_logged_in %}
<a href="/courses" class="btn-secondary">Zu den Kursen</a>
{% else %}
<a href="/login" class="btn-secondary">Login</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
<div class="error-page">
<div class="error-card">
<div class="error-icon">🔍</div>
<h1>Seite nicht gefunden</h1>
<p>
Die angeforderte Seite existiert nicht oder wurde verschoben.
</p>
<div class="error-actions">
{% if is_logged_in %}
<a href="/courses" class="btn-secondary">Zu den Kursen</a>
{% else %}
<a href="/preise" class="btn-secondary">Preise ansehen</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -30,25 +30,57 @@
{% if is_logged_in %}
<div class="user-menu">
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
<div class="user-menu-dropdown">
<button class="user-menu-btn">
{{ user_name }}
</button>
<div class="user-dropdown">
<!-- USER -->
<div class="menu-group">
<div class="menu-group-title">User</div>
<a href="/profil">Profil</a>
<a href="/logout">Logout</a>
</div>
{% if is_admin %}
<div class="menu-separator"></div>
<!-- ADMIN -->
<div class="menu-group">
<div class="menu-group-title">Admin</div>
<a href="/admin/mandanten">Mandanten</a>
<a href="/admin/checklist">Checklist</a>
<a href="/admin/courses">Kurse</a>
<a href="/admin/questions">Assessment</a>
{% endif %}
{% if is_user_admin %}
<a href="/useradmin/mandant">Useradministration</a>
<a href="/reporting/assessments">Reporting</a>
<a href="/certificates">Zertifikate</a>
{% endif %}
{% if is_contentmanager %}
<a href="/dokumente">Dokumente</a>
<a href="/admin/questions">Fragen</a>
</div>
{% endif %}
{% if is_user_admin %}
<div class="menu-separator"></div>
<!-- USERADMIN -->
<div class="menu-group">
<div class="menu-group-title">Useradmin</div>
<a href="/useradmin/mandant">Userverwaltung</a>
<a href="/certificates">Zertifikate</a>
<a href="/reporting">Reporting</a>
</div>
{% endif %}
{% if is_contentmanager %}
<div class="menu-separator"></div>
<!-- CONTENTMANAGER -->
<div class="menu-group">
<div class="menu-group-title">Contentmanager</div>
<a href="/dokumente">Dokumente</a>
</div>
{% endif %}
<a href="/logout">Logout</a>
</div>
</div>
{% else %}

View File

@ -1444,3 +1444,210 @@ button {
height: 100%;
background: linear-gradient(90deg, #22c55e, #15803d);
}
/* ===============================
User Dropdown Menü (neu)
=============================== */
.user-menu {
position: relative;
}
.user-menu-btn {
background: #eef3f9;
border: none;
padding: 8px 14px;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
color: #1f2f46;
}
.user-dropdown {
position: absolute;
right: 0;
top: 42px;
min-width: 240px;
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 14px;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
padding: 10px 0;
display: none;
z-index: 1000;
}
/* Hover öffnen */
.user-menu:hover .user-dropdown {
display: block;
}
/* Gruppen */
.menu-group {
padding: 6px 0;
}
/* Titel */
.menu-group-title {
font-size: 11px;
font-weight: 700;
color: #64748b;
padding: 6px 16px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Links */
.user-dropdown a {
display: block;
padding: 10px 16px;
color: #1f2937;
text-decoration: none;
font-size: 14px;
transition: background 0.2s ease;
}
/* Hover Effekt */
.user-dropdown a:hover {
background: #f1f5f9;
}
/* Separator */
.menu-separator {
height: 1px;
background: #e2e8f0;
margin: 6px 0;
}
/* Pastell-Hintergründe pro Gruppe */
.menu-group:nth-child(1) {
background: #f8fafc;
}
.menu-group:nth-child(3) {
background: #f0fdf4;
}
.menu-group:nth-child(5) {
background: #fefce8;
}
.menu-group:nth-child(7) {
background: #fdf2f8;
}
/*********************/
/*. User Menü */
/*********************/
.top-nav a,
.user-menu-btn,
.lang-btn {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
min-width: 140px; /* 👈 gleich breit */
padding: 0 18px;
border-radius: 22px;
font-weight: 600;
font-size: 15px;
box-sizing: border-box;
}
.top-nav a,
.lang-btn {
gap: 8px;
}
.user-menu {
position: relative;
}
/* 🔥 WICHTIG: kein Abstand mehr */
.user-dropdown {
position: absolute;
right: 0;
top: 100%; /* direkt anschließen */
margin-top: 4px; /* kleiner Abstand erlaubt */
min-width: 240px;
background: #fff;
border-radius: 14px;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
opacity: 0;
pointer-events: none;
transform: translateY(6px);
transition: all 0.2s ease;
}
/* 🔥 Hover stabil */
.user-menu:hover .user-dropdown {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.user-menu::after {
content: "";
position: absolute;
top: 100%;
right: 0;
height: 10px;
width: 100%;
}
/* ===============================
Error Pages
=============================== */
.error-page {
display: flex;
justify-content: center;
align-items: center;
padding: 60px 20px;
}
.error-card {
max-width: 480px;
width: 100%;
background: #ffffff;
border-radius: 18px;
padding: 40px 30px;
text-align: center;
box-shadow: 0 12px 40px rgba(0,0,0,0.08);
}
.error-icon {
font-size: 48px;
margin-bottom: 15px;
}
.error-card h1 {
margin-bottom: 10px;
color: #0d2f57;
}
.error-card p {
color: #475569;
margin-bottom: 25px;
}
.error-actions {
display: flex;
gap: 12px;
justify-content: center;
}
.btn-secondary {
background: #e2e8f0;
color: #1f2937;
padding: 10px 16px;
border-radius: 10px;
text-decoration: none;
font-weight: 600;
}