Darkmode hinzugefügt

This commit is contained in:
Erik Thiele
2026-05-19 22:42:53 +02:00
parent 3b89f9c609
commit df98b6d57f
4 changed files with 208 additions and 6 deletions

6
app.py
View File

@@ -12,7 +12,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
app = Flask(__name__)
app.config["SECRET_KEY"] = "dev-secret-key"
APP_VERSION = "1.5.0"
APP_VERSION = "2.1.0"
DATABASE = Path(__file__).with_name("inventory.db")
LOGFILE = Path(__file__).with_name("inventory.log")
@@ -40,8 +40,8 @@ PRINT_DESCRIPTIONS = {
}
INPUT_PLACEHOLDERS = {
"chip": "z. B. CHIP-1001",
"parking_card": "z. B. PARK-2001",
"chip": "z. B. 100",
"parking_card": "z. B. 200199887755123",
"pool_vehicle": "z. B. GZ-CC-123",
}

View File

@@ -1042,3 +1042,83 @@
2026-05-19 22:07:47,705 INFO 127.0.0.1 - - [19/May/2026 22:07:47] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:07:50,763 INFO 127.0.0.1 - - [19/May/2026 22:07:50] "GET / HTTP/1.1" 200 -
2026-05-19 22:07:50,786 INFO 127.0.0.1 - - [19/May/2026 22:07:50] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:28:58,481 INFO WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5006
* Running on http://192.168.10.191:5006
2026-05-19 22:28:58,484 INFO Press CTRL+C to quit
2026-05-19 22:28:58,488 INFO * Restarting with stat
2026-05-19 22:28:58,657 WARNING * Debugger is active!
2026-05-19 22:28:58,672 INFO * Debugger PIN: 428-899-358
2026-05-19 22:29:01,471 INFO 127.0.0.1 - - [19/May/2026 22:29:01] "GET / HTTP/1.1" 200 -
2026-05-19 22:29:01,519 INFO 127.0.0.1 - - [19/May/2026 22:29:01] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:29:12,441 INFO 127.0.0.1 - - [19/May/2026 22:29:12] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:29:12,462 INFO 127.0.0.1 - - [19/May/2026 22:29:12] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:29:16,459 INFO 127.0.0.1 - - [19/May/2026 22:29:16] "GET /assign HTTP/1.1" 200 -
2026-05-19 22:29:16,483 INFO 127.0.0.1 - - [19/May/2026 22:29:16] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:29:17,805 INFO 127.0.0.1 - - [19/May/2026 22:29:17] "GET /return HTTP/1.1" 200 -
2026-05-19 22:29:17,825 INFO 127.0.0.1 - - [19/May/2026 22:29:17] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:29:19,721 INFO 127.0.0.1 - - [19/May/2026 22:29:19] "GET / HTTP/1.1" 200 -
2026-05-19 22:29:19,745 INFO 127.0.0.1 - - [19/May/2026 22:29:19] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:30:32,956 INFO 127.0.0.1 - - [19/May/2026 22:30:32] "GET / HTTP/1.1" 200 -
2026-05-19 22:30:32,982 INFO 127.0.0.1 - - [19/May/2026 22:30:32] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:31:28,576 INFO 127.0.0.1 - - [19/May/2026 22:31:28] "GET / HTTP/1.1" 200 -
2026-05-19 22:31:28,597 INFO 127.0.0.1 - - [19/May/2026 22:31:28] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:33:58,615 INFO 127.0.0.1 - - [19/May/2026 22:33:58] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:33:58,647 INFO 127.0.0.1 - - [19/May/2026 22:33:58] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:34:01,025 INFO 127.0.0.1 - - [19/May/2026 22:34:01] "GET /assign HTTP/1.1" 200 -
2026-05-19 22:34:01,047 INFO 127.0.0.1 - - [19/May/2026 22:34:01] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:34:02,985 INFO 127.0.0.1 - - [19/May/2026 22:34:02] "GET /return HTTP/1.1" 200 -
2026-05-19 22:34:03,006 INFO 127.0.0.1 - - [19/May/2026 22:34:03] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:34:04,099 INFO 127.0.0.1 - - [19/May/2026 22:34:04] "GET / HTTP/1.1" 200 -
2026-05-19 22:34:04,124 INFO 127.0.0.1 - - [19/May/2026 22:34:04] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:35:27,210 INFO 127.0.0.1 - - [19/May/2026 22:35:27] "GET / HTTP/1.1" 200 -
2026-05-19 22:35:27,241 INFO 127.0.0.1 - - [19/May/2026 22:35:27] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:35:47,127 INFO 127.0.0.1 - - [19/May/2026 22:35:47] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:35:47,155 INFO 127.0.0.1 - - [19/May/2026 22:35:47] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:35:48,755 INFO 127.0.0.1 - - [19/May/2026 22:35:48] "GET /assign HTTP/1.1" 200 -
2026-05-19 22:35:48,779 INFO 127.0.0.1 - - [19/May/2026 22:35:48] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:35:49,726 INFO 127.0.0.1 - - [19/May/2026 22:35:49] "GET /return HTTP/1.1" 200 -
2026-05-19 22:35:49,749 INFO 127.0.0.1 - - [19/May/2026 22:35:49] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:35:54,822 INFO 127.0.0.1 - - [19/May/2026 22:35:54] "GET / HTTP/1.1" 200 -
2026-05-19 22:35:54,841 INFO 127.0.0.1 - - [19/May/2026 22:35:54] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:36:53,033 INFO 127.0.0.1 - - [19/May/2026 22:36:53] "GET / HTTP/1.1" 200 -
2026-05-19 22:36:53,056 INFO 127.0.0.1 - - [19/May/2026 22:36:53] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:37:02,607 INFO 127.0.0.1 - - [19/May/2026 22:37:02] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:37:02,640 INFO 127.0.0.1 - - [19/May/2026 22:37:02] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:37:04,683 INFO 127.0.0.1 - - [19/May/2026 22:37:04] "GET /assign HTTP/1.1" 200 -
2026-05-19 22:37:04,707 INFO 127.0.0.1 - - [19/May/2026 22:37:04] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:37:06,184 INFO 127.0.0.1 - - [19/May/2026 22:37:06] "GET /return HTTP/1.1" 200 -
2026-05-19 22:37:06,206 INFO 127.0.0.1 - - [19/May/2026 22:37:06] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:37:07,536 INFO 127.0.0.1 - - [19/May/2026 22:37:07] "GET / HTTP/1.1" 200 -
2026-05-19 22:37:07,555 INFO 127.0.0.1 - - [19/May/2026 22:37:07] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:46,578 INFO 127.0.0.1 - - [19/May/2026 22:38:46] "GET / HTTP/1.1" 200 -
2026-05-19 22:38:46,606 INFO 127.0.0.1 - - [19/May/2026 22:38:46] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:50,867 INFO 127.0.0.1 - - [19/May/2026 22:38:50] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:38:50,895 INFO 127.0.0.1 - - [19/May/2026 22:38:50] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:52,374 INFO 127.0.0.1 - - [19/May/2026 22:38:52] "GET / HTTP/1.1" 200 -
2026-05-19 22:38:52,395 INFO 127.0.0.1 - - [19/May/2026 22:38:52] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:57,770 INFO 127.0.0.1 - - [19/May/2026 22:38:57] "GET / HTTP/1.1" 200 -
2026-05-19 22:38:57,793 INFO 127.0.0.1 - - [19/May/2026 22:38:57] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:58,294 INFO 127.0.0.1 - - [19/May/2026 22:38:58] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:38:58,314 INFO 127.0.0.1 - - [19/May/2026 22:38:58] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:38:59,411 INFO 127.0.0.1 - - [19/May/2026 22:38:59] "GET / HTTP/1.1" 200 -
2026-05-19 22:38:59,433 INFO 127.0.0.1 - - [19/May/2026 22:38:59] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:31,748 INFO 127.0.0.1 - - [19/May/2026 22:40:31] "GET /users/new HTTP/1.1" 200 -
2026-05-19 22:40:31,780 INFO 127.0.0.1 - - [19/May/2026 22:40:31] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:33,164 INFO 127.0.0.1 - - [19/May/2026 22:40:33] "GET /assign HTTP/1.1" 200 -
2026-05-19 22:40:33,189 INFO 127.0.0.1 - - [19/May/2026 22:40:33] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:34,888 INFO 127.0.0.1 - - [19/May/2026 22:40:34] "GET /return HTTP/1.1" 200 -
2026-05-19 22:40:34,909 INFO 127.0.0.1 - - [19/May/2026 22:40:34] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:37,420 INFO 127.0.0.1 - - [19/May/2026 22:40:37] "GET /logout HTTP/1.1" 302 -
2026-05-19 22:40:37,429 INFO 127.0.0.1 - - [19/May/2026 22:40:37] "GET /login HTTP/1.1" 200 -
2026-05-19 22:40:37,449 INFO 127.0.0.1 - - [19/May/2026 22:40:37] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:37,457 INFO 127.0.0.1 - - [19/May/2026 22:40:37] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 22:40:43,940 INFO 127.0.0.1 - - [19/May/2026 22:40:43] "POST /login HTTP/1.1" 302 -
2026-05-19 22:40:43,967 INFO 127.0.0.1 - - [19/May/2026 22:40:43] "GET / HTTP/1.1" 200 -
2026-05-19 22:40:43,992 INFO 127.0.0.1 - - [19/May/2026 22:40:43] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 22:40:44,005 INFO 127.0.0.1 - - [19/May/2026 22:40:44] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 22:42:50,799 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 22:42:50,836 INFO * Restarting with stat
2026-05-19 22:42:51,090 WARNING * Debugger is active!
2026-05-19 22:42:51,115 INFO * Debugger PIN: 428-899-358

View File

@@ -7,6 +7,13 @@
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link rel="stylesheet" href="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
<script>
(() => {
const storedTheme = window.localStorage.getItem("keyadmin-theme");
const theme = storedTheme === "dark" || storedTheme === "light" ? storedTheme : "light";
document.documentElement.setAttribute("data-bs-theme", theme);
})();
</script>
<style>
:root {
--ccm-bg: #f4f6f8;
@@ -78,6 +85,94 @@
color: #ffffff;
}
.theme-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2.5rem;
min-height: 2.5rem;
padding: 0;
line-height: 1;
}
[data-bs-theme="dark"] {
--ccm-bg: #15181d;
--ccm-text: #e5e7eb;
--ccm-header: #0f1720;
--ccm-surface: #15181d;
--ccm-border: #2b3440;
--ccm-muted: #9aa4b2;
}
[data-bs-theme="dark"] body {
background-color: var(--ccm-bg);
color: var(--ccm-text);
}
[data-bs-theme="dark"] .card,
[data-bs-theme="dark"] .dropdown-menu,
[data-bs-theme="dark"] .table,
[data-bs-theme="dark"] .table-responsive,
[data-bs-theme="dark"] .login-card,
[data-bs-theme="dark"] .page-shell,
[data-bs-theme="dark"] .form-card {
background-color: var(--ccm-bg);
color: var(--ccm-text);
border-color: var(--ccm-border);
}
[data-bs-theme="dark"] .page-section-title,
[data-bs-theme="dark"] .grid > .card {
background-color: var(--ccm-bg);
}
[data-bs-theme="dark"] .card-table-shell {
background-color: var(--ccm-bg);
}
[data-bs-theme="dark"] .details th,
[data-bs-theme="dark"] .details td,
[data-bs-theme="dark"] .table > :not(caption) > * > * {
border-color: var(--ccm-border);
color: var(--ccm-text);
}
[data-bs-theme="dark"] .form-control,
[data-bs-theme="dark"] .form-select {
background-color: #11161d;
color: var(--ccm-text);
border-color: var(--ccm-border);
}
[data-bs-theme="dark"] .form-control::placeholder,
[data-bs-theme="dark"] .muted,
[data-bs-theme="dark"] .page-section-title p,
[data-bs-theme="dark"] .app-footer,
[data-bs-theme="dark"] .table thead th {
color: var(--ccm-muted);
}
[data-bs-theme="dark"] .button-secondary {
background: #11161d;
color: var(--ccm-text);
}
[data-bs-theme="dark"] .nav-surface .btn-white {
background: rgba(255, 255, 255, 0.06);
color: #ffffff;
border-color: rgba(255, 255, 255, 0.12);
}
[data-bs-theme="dark"] .nav-surface .btn.active {
background-color: rgba(255, 255, 255, 0.92);
color: #111827;
}
[data-bs-theme="dark"] .theme-toggle {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
}
.navbar-brand-wordmark strong {
letter-spacing: 0.06em;
text-transform: uppercase;
@@ -260,6 +355,11 @@
overflow-x: auto;
}
.card-table-shell {
border-radius: 0 0 1rem 1rem;
overflow: hidden;
}
.card-table .btn {
white-space: nowrap;
}
@@ -500,6 +600,9 @@
<div class="navbar-nav flex-row order-md-last top-actions">
{% if g.current_staff %}
<span class="nav-link disabled">{{ g.current_staff.full_name }} ({{ g.current_staff.role }})</span>
<button type="button" class="btn btn-outline-secondary me-2 theme-toggle" id="theme-toggle" aria-label="Darkmode umschalten" title="Darkmode umschalten">
<i class="ti ti-moon"></i>
</button>
<a class="btn btn-outline-secondary" href="{{ url_for('logout') }}"><i class="ti ti-logout me-1"></i>Abmelden</a>
{% else %}
<a class="btn btn-primary" href="{{ url_for('login') }}"><i class="ti ti-login me-1"></i>Login</a>
@@ -550,6 +653,25 @@
</div>
<script>
window.addEventListener("load", () => {
const themeToggle = document.getElementById("theme-toggle");
const root = document.documentElement;
const syncThemeToggle = () => {
const theme = root.getAttribute("data-bs-theme") || "light";
if (!themeToggle) return;
themeToggle.innerHTML = theme === "dark" ? '<i class="ti ti-sun"></i>' : '<i class="ti ti-moon"></i>';
};
if (themeToggle) {
syncThemeToggle();
themeToggle.addEventListener("click", () => {
const nextTheme = (root.getAttribute("data-bs-theme") || "light") === "dark" ? "light" : "dark";
root.setAttribute("data-bs-theme", nextTheme);
window.localStorage.setItem("keyadmin-theme", nextTheme);
syncThemeToggle();
});
}
window.setTimeout(() => {
document.querySelectorAll(".flash-stack .alert").forEach((element) => {
if (element.classList.contains("alert-error") || element.classList.contains("alert-danger") || element.classList.contains("alert-import-errors")) {

View File

@@ -69,7 +69,7 @@
<div class="card-header">
<h2 class="card-title"><i class="ti ti-users me-1"></i>User</h2>
</div>
<div class="table-responsive table-wrap">
<div class="table-responsive table-wrap card-table-shell">
{% if users %}
<table class="table table-vcenter card-table">
<thead>
@@ -126,7 +126,7 @@
<div class="card-header">
<h2 class="card-title"><i class="ti ti-history me-1"></i>Letzte Bewegungen</h2>
</div>
<div class="table-responsive table-wrap">
<div class="table-responsive table-wrap card-table-shell">
{% if transactions %}
<table class="table table-vcenter card-table">
<thead>
@@ -165,7 +165,7 @@
<div class="card-header">
<h2 class="card-title"><i class="ti ti-file-text me-1"></i>Letzte Logeintraege</h2>
</div>
<div class="table-responsive table-wrap">
<div class="table-responsive table-wrap card-table-shell">
{% if recent_logs %}
<table class="table table-vcenter card-table">
<thead>