Projektleiter-Rolle, Papierkorb, neue Login-Seite und Benutzerverwaltung

- Neue Rolle "Projektleiter": Projekte anlegen und löschen nur noch für
  Projektleiter und Administratoren (serverseitig in /save-week geprüft)
- Papierkorb: gelöschte Projekte 30 Tage durch Admins wiederherstellbar
- Benutzerverwaltung: Aktionen im Drei-Punkte-Menü mit Rückfrage im Menü,
  getrennte Felder für Vor- und Nachname, E-Mail-Vorschlag
  v.nachname@rauschenberg-ing.de, Benutzername = Anfang der E-Mail,
  Person-ID aus dem Kürzel
- Endgültiges Löschen eines Benutzers entfernt ihn auch aus den Projekten
  und Aufgaben der aktuellen Sitzung; Dateien bleiben als
  "Gelöschter Benutzer"
- Login-Seite neu (Design "Kalenderwoche") mit Logo als Wasserzeichen;
  Anmeldung ohne Groß-/Kleinschreibung, auch mit voller E-Mail-Adresse

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
erikandClaude Opus 5.5 committed 2026-10-22 09:25:33 +02:00
1 parent 93505e0e48
commit f4fb3c550b
13 files changed
+1214 -116

No files matched your search

+183 -2
View File
@@ -4030,8 +4030,63 @@ html[data-theme="dark"] .absence-type-fortbildung { background: rgba(255,159,10,
Seiten-Huelle (.admin-page/.admin-header/.admin-body) sind mit ihr
entfallen, weil der Abschnitt jetzt in der normalen App-Huelle liegt. */
.admin-toolbar { margin-bottom: 14px; }
td.actions-cell { white-space: nowrap; }
td.actions-cell button { margin: 2px; }
td.actions-cell { white-space: nowrap; width: 1%; text-align: right; }
/* Vor- und Nachname nebeneinander (Benutzer anlegen/bearbeiten), auf
schmalen Bildschirmen untereinander. */
.form-row-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.form-row-pair > .form-row { min-width: 0; }
@media (max-width: 480px) { .form-row-pair { grid-template-columns: 1fr; } }
/* Zeilenaktionen der Benutzerverwaltung: „Bearbeiten" + Drei-Punkte-Menü
(js/admin-users.js, openUserActionMenu()). Das Menü hängt an <body> und
steht fixed, damit der quer scrollbare Tabellenrahmen es nicht abschneidet. */
.user-aktionen { display: inline-flex; gap: 6px; align-items: center; }
.user-kebab {
width: 30px; height: 30px; padding: 0;
display: inline-grid; place-items: center;
border: 1px solid transparent; border-radius: var(--r-sm);
background: transparent; color: var(--text-muted); cursor: pointer;
transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.user-kebab:hover { background: var(--surface-sunk); color: var(--text); }
.user-kebab[aria-expanded="true"] {
background: var(--accent-soft); color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.user-kebab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.user-kebab svg { display: block; }
tr.is-menu-open { background: var(--surface-alt); }
.user-menu {
position: fixed; z-index: 250; min-width: 220px; max-width: 280px;
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); box-shadow: var(--sh-3); padding: 5px;
transform-origin: top right;
animation: user-menu-auf var(--dur) var(--ease-out);
}
.user-menu.nach-oben { transform-origin: bottom right; animation-name: user-menu-auf-oben; }
@keyframes user-menu-auf { from { opacity: 0; transform: scale(.96) translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes user-menu-auf-oben { from { opacity: 0; transform: scale(.96) translateY(4px); } to { opacity: 1; transform: none; } }
.user-menu-item {
all: unset; box-sizing: border-box;
display: flex; gap: 10px; align-items: flex-start; width: 100%;
padding: 7px 10px; border-radius: var(--r-sm);
color: var(--text); font-size: 13.5px; line-height: 1.35; cursor: pointer;
}
.user-menu-item:hover:not([aria-disabled="true"]), .user-menu-item:focus-visible { background: var(--surface-sunk); }
.user-menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.user-menu-ic { flex: none; width: 16px; height: 16px; margin-top: 1px; color: var(--text-muted); }
.user-menu-text { display: grid; gap: 1px; }
.user-menu-sub { font-size: 12px; color: var(--text-subtle); }
.user-menu-item.is-danger, .user-menu-item.is-danger .user-menu-ic { color: var(--red); }
.user-menu-item.is-danger:hover:not([aria-disabled="true"]),
.user-menu-item.is-danger:focus-visible { background: color-mix(in srgb, var(--red) 10%, transparent); }
.user-menu-item[aria-disabled="true"], .user-menu-item[aria-disabled="true"] .user-menu-ic { color: var(--text-subtle); cursor: not-allowed; }
.user-menu-trenner { border: 0; border-top: 1px solid var(--line-soft); margin: 5px 4px; }
.user-menu-confirm { padding: 10px 10px 8px; display: grid; gap: 10px; }
.user-menu-confirm p { margin: 0; font-size: 13px; line-height: 1.4; }
.user-menu-confirm-row { display: flex; gap: 6px; justify-content: flex-end; }
.badge-active { color: var(--green); font-weight: 600; }
.badge-inactive { color: var(--text-muted); }
.badge-warn { color: var(--amber); font-weight: 600; }
@@ -5252,3 +5307,129 @@ td.actions-cell button { margin: 2px; }
scroll-behavior: auto !important;
}
}
/* =========================================================================
LOGIN-SEITE (#loginOverlay, Design "Kalenderwoche" — Eriks Wahl B)
Links Logo und Formular, rechts die laufende Kalenderwoche als große Zahl
mit ihren Arbeitstagen (renderLoginWoche(), js/auth.js). Farben über die
App-Tokens; nur die Zahl hat eigene Tokens (Logo-Hellblau im hellen,
gedämpft im dunklen Thema). Liegt über allem (z-index 400 > .modal-bg 300).
Das Logo ist ein blasses Wasserzeichen über die ganze Seite
(.login-wasserzeichen, Variante E), kein eigenes kleines Logo.
========================================================================= */
.login-seite {
--login-zahl: #94b0cc;
--login-jahr: #116896;
--login-wz: .11;
position: fixed; inset: 0; z-index: 400;
display: none; overflow-y: auto; overflow-x: hidden;
background: var(--bg); color: var(--text);
padding: 0 20px;
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) .login-seite { --login-zahl: #4f7695; --login-jahr: var(--accent); --login-wz: .07; }
}
html[data-theme="dark"] .login-seite { --login-zahl: #4f7695; --login-jahr: var(--accent); --login-wz: .07; }
/* Wasserzeichen: ganzes Zeichen sichtbar, an den Rändern angeschnitten. Die
Felder und Tageskacheln haben eigene Flächen und bleiben davor lesbar.
Der Rahmen ist genau so groß wie das Fenster und schneidet alles
Überstehende ab — sonst ragte das Logo über den Rand und machte die
Seite scrollbar. */
.login-wz-rahmen { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.login-wasserzeichen {
position: absolute; z-index: 0; left: 50%; top: 50%;
width: min(1500px, 98vw); height: auto; transform: translate(-44%, -47%);
opacity: var(--login-wz); filter: blur(1px);
pointer-events: none; user-select: none;
}
.login-raster {
position: relative; z-index: 1;
width: 100%; max-width: 1180px; min-height: 100%; margin: 0 auto;
display: grid; grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
gap: clamp(32px, 7vw, 110px); align-items: center;
padding-block: calc(48px + env(safe-area-inset-top, 0px)) calc(48px + env(safe-area-inset-bottom, 0px));
}
.login-titel { margin: 0 0 6px; font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-weight: 500; font-size: 30px; letter-spacing: -.01em; }
.login-unter { margin: 0 0 30px; color: var(--text-muted); }
.login-form { display: grid; gap: 18px; }
.login-feld { display: grid; gap: 7px; }
.login-feld label { font-size: 13.5px; font-weight: 600; color: var(--text); }
.login-feld input {
width: 100%; box-sizing: border-box; min-height: 46px; padding: 11px 13px;
font: inherit; font-size: 15px; color: var(--text);
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.login-feld input::placeholder { color: var(--text-subtle); opacity: 1; }
.login-feld input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.login-feld.hat-fehler input { border-color: var(--red); }
.login-pw { position: relative; }
.login-pw input { padding-right: 96px; }
.login-pw-toggle {
position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
background: transparent; border: 0; border-radius: var(--r-md); padding: 7px 10px; cursor: pointer;
}
.login-pw-toggle:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.login-pw-toggle:focus-visible, .login-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.login-caps { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--amber); }
.login-fehler {
margin: 0; padding: 10px 12px; border-radius: var(--r-lg); font-size: 13.5px; line-height: 1.45;
color: var(--red); background: color-mix(in srgb, var(--red) 10%, transparent);
}
.login-btn {
display: inline-flex; align-items: center; justify-content: center; gap: 10px;
width: 100%; min-height: 48px; margin-top: 4px;
font: inherit; font-size: 15px; font-weight: 650; color: #fff;
background: var(--accent); border: 0; border-radius: var(--r-lg); cursor: pointer;
transition: filter var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .login-btn { color: #0b1c26; } }
html[data-theme="dark"] .login-btn { color: #0b1c26; }
.login-btn:hover { filter: brightness(1.07); }
.login-btn:active { transform: scale(var(--press)); }
.login-btn:disabled { cursor: progress; filter: saturate(.7); }
.login-spinner {
display: none; width: 15px; height: 15px; border-radius: 50%;
border: 2px solid color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor;
animation: login-dreh .7s linear infinite;
}
.login-btn.laedt .login-spinner { display: block; }
@keyframes login-dreh { to { transform: rotate(360deg); } }
.login-hilfe { margin: 18px 0 0; font-size: 13px; color: var(--text-muted); }
.login-woche { justify-self: end; text-align: right; animation: login-auf .7s var(--ease-out) both; }
@keyframes login-auf { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.login-kw-label, .login-kw-jahr, .login-kw-zahl, .login-tage b { font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
.login-kw-label { display: block; font-weight: 500; font-size: 22px; color: var(--text-muted); letter-spacing: .02em; }
.login-kw-zahl {
display: block; font-weight: 300; font-size: clamp(150px, 21vw, 300px); line-height: .86;
letter-spacing: -.05em; color: var(--login-zahl); font-variant-numeric: tabular-nums;
}
.login-kw-jahr { display: block; font-weight: 500; font-size: 20px; color: var(--login-jahr); }
.login-tage { list-style: none; margin: 30px 0 0; padding: 0; display: flex; gap: 8px; justify-content: flex-end; }
.login-tage li {
display: grid; justify-items: center; gap: 2px; width: 58px; padding: 10px 0 9px;
border-radius: var(--r-lg); background: var(--surface); border: 1px solid var(--line);
}
.login-tage li span { font-size: 12px; font-weight: 600; color: var(--text-muted); }
.login-tage li b { font-weight: 500; font-size: 20px; font-variant-numeric: tabular-nums; }
.login-tage li.ist-heute { background: var(--accent); border-color: var(--accent); }
.login-tage li.ist-heute span, .login-tage li.ist-heute b { color: #fff; }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .login-tage li.ist-heute span, html:not([data-theme="light"]) .login-tage li.ist-heute b { color: #0b1c26; } }
html[data-theme="dark"] .login-tage li.ist-heute span, html[data-theme="dark"] .login-tage li.ist-heute b { color: #0b1c26; }
.login-datum { margin: 18px 0 0; color: var(--text-muted); font-size: 15px; }
@media (max-width: 880px) {
.login-raster { grid-template-columns: 1fr; gap: 28px; padding-block: calc(28px + env(safe-area-inset-top, 0px)) 48px; align-content: start; }
.login-woche { order: -1; justify-self: start; text-align: left; }
.login-kw-zahl { font-size: 110px; }
.login-tage { justify-content: flex-start; }
.login-tage li { width: auto; flex: 1; max-width: 58px; }
.login-wasserzeichen { width: 260vw; top: 0; transform: translate(-30%, -10%); }
}
@media (prefers-reduced-motion: reduce) { .login-woche { animation: none; } .login-spinner { animation: none; } }