Fix: Login case-insensitive, Settings zusammengeführt, SVG-Icon, Copyright einzeilig
- Login: Benutzername wird case-insensitiv verglichen (func.lower auf beiden Seiten)
- Benutzer anlegen: Username wird immer lowercase gespeichert
- Einstellungen: Panels "Darstellung", "Ansicht & Raster" und "Ausgeblendete Kalender" zu einem einzigen Panel zusammengeführt
- App-Icon: Emoji 📅 durch plattformunabhängiges Inline-SVG ersetzt
- Copyright: white-space:nowrap + damit Zeile nie umbricht
This commit is contained in:
@@ -4,6 +4,7 @@ import pyotp
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
import models
|
||||
@@ -39,7 +40,7 @@ def setup(req: SetupRequest, db: Session = Depends(get_db)):
|
||||
if db.query(models.User).count() > 0:
|
||||
raise HTTPException(400, "Setup already completed")
|
||||
user = models.User(
|
||||
username=req.username,
|
||||
username=req.username.lower(),
|
||||
email=req.email,
|
||||
password_hash=get_password_hash(req.password),
|
||||
is_admin=True,
|
||||
@@ -58,7 +59,7 @@ def login(
|
||||
form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)
|
||||
):
|
||||
user = (
|
||||
db.query(models.User).filter(models.User.username == form_data.username).first()
|
||||
db.query(models.User).filter(func.lower(models.User.username) == form_data.username.lower()).first()
|
||||
)
|
||||
if not user or not verify_password(form_data.password, user.password_hash):
|
||||
raise HTTPException(
|
||||
@@ -78,7 +79,7 @@ def login(
|
||||
@router.post("/login")
|
||||
def login_json(req: LoginRequest, db: Session = Depends(get_db)):
|
||||
user = (
|
||||
db.query(models.User).filter(models.User.username == req.username).first()
|
||||
db.query(models.User).filter(func.lower(models.User.username) == req.username.lower()).first()
|
||||
)
|
||||
if not user or not verify_password(req.password, user.password_hash):
|
||||
raise HTTPException(
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
import models
|
||||
@@ -40,10 +41,10 @@ def create_user(
|
||||
db: Session = Depends(get_db),
|
||||
_: models.User = Depends(get_current_admin),
|
||||
):
|
||||
if db.query(models.User).filter(models.User.username == req.username).first():
|
||||
if db.query(models.User).filter(func.lower(models.User.username) == req.username.lower()).first():
|
||||
raise HTTPException(400, "Username already taken")
|
||||
user = models.User(
|
||||
username=req.username,
|
||||
username=req.username.lower(),
|
||||
email=req.email,
|
||||
password_hash=get_password_hash(req.password),
|
||||
is_admin=req.is_admin,
|
||||
|
||||
@@ -355,6 +355,7 @@ a { color: var(--primary); text-decoration: none; }
|
||||
text-align: center; cursor: pointer;
|
||||
background: none; border: none; border-top: 1px solid var(--border);
|
||||
transition: color var(--transition);
|
||||
white-space: nowrap;
|
||||
position: sticky; bottom: 0;
|
||||
background: var(--bg-sidebar);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<button type="submit" class="btn btn-primary btn-full">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices</button>
|
||||
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices</button>
|
||||
</div>
|
||||
|
||||
<!-- ─── MAIN APP ──────────────────────────────────────────── -->
|
||||
@@ -83,7 +83,7 @@
|
||||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
|
||||
</button>
|
||||
<div class="topbar-logo">
|
||||
<span>📅</span>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
<span class="logo-text">Calendarr</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -170,7 +170,7 @@
|
||||
<div id="cal-list-items"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices</button>
|
||||
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices</button>
|
||||
</aside>
|
||||
|
||||
<!-- MAIN VIEW -->
|
||||
@@ -390,17 +390,16 @@
|
||||
</div>
|
||||
<div class="settings-page-body">
|
||||
<nav class="settings-nav">
|
||||
<button class="settings-nav-btn active" data-panel="appearance">Darstellung</button>
|
||||
<button class="settings-nav-btn" data-panel="view">Ansicht & Raster</button>
|
||||
<button class="settings-nav-btn active" data-panel="general">Einstellungen</button>
|
||||
<button class="settings-nav-btn" data-panel="google">Google Konten</button>
|
||||
<button class="settings-nav-btn" data-panel="hidden">Ausgeblendete Kalender</button>
|
||||
<button class="settings-nav-btn hidden" data-panel="users" id="settings-nav-users">Benutzerverwaltung</button>
|
||||
</nav>
|
||||
|
||||
<div class="settings-panels">
|
||||
|
||||
<!-- Darstellung -->
|
||||
<div class="settings-panel active" id="settings-panel-appearance">
|
||||
<!-- Einstellungen (merged: Darstellung + Ansicht & Raster + Ausgeblendete Kalender) -->
|
||||
<div class="settings-panel active" id="settings-panel-general">
|
||||
|
||||
<h4 class="panel-title">Farben</h4>
|
||||
<div class="form-group">
|
||||
<label>Primärfarbe</label>
|
||||
@@ -441,11 +440,8 @@
|
||||
<button class="contrast-btn" data-val="3"><span class="line-preview" style="border-color:#3a3a52"></span><span class="contrast-lbl">Normal</span></button>
|
||||
<button class="contrast-btn" data-val="4"><span class="line-preview" style="border-color:#5a5a78"></span><span class="contrast-lbl">Stark</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ansicht & Raster -->
|
||||
<div class="settings-panel" id="settings-panel-view">
|
||||
<h4 class="panel-title">Kalenderansicht</h4>
|
||||
<h4 class="panel-title" style="margin-top:24px">Kalenderansicht</h4>
|
||||
<div class="form-group">
|
||||
<label>Standardansicht</label>
|
||||
<select id="cfg-default-view">
|
||||
@@ -477,6 +473,9 @@
|
||||
<button class="contrast-btn" data-val="80"><span class="hour-preview">━━━━</span><span class="contrast-lbl">Komfort</span></button>
|
||||
<button class="contrast-btn" data-val="100"><span class="hour-preview">━━━━━</span><span class="contrast-lbl">Gross</span></button>
|
||||
</div>
|
||||
|
||||
<h4 class="panel-title" style="margin-top:24px">Ausgeblendete Kalender</h4>
|
||||
<div id="hidden-cals-list"><span style="font-size:13px;color:var(--text-3)">Keine ausgeblendeten Kalender</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Google Konten -->
|
||||
@@ -485,12 +484,6 @@
|
||||
<div id="google-accounts-list"><span style="font-size:13px;color:var(--text-3)">Keine Google-Konten verbunden</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Ausgeblendete Kalender -->
|
||||
<div class="settings-panel" id="settings-panel-hidden">
|
||||
<h4 class="panel-title">Ausgeblendete Kalender</h4>
|
||||
<div id="hidden-cals-list"><span style="font-size:13px;color:var(--text-3)">Keine ausgeblendeten Kalender</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Benutzerverwaltung -->
|
||||
<div class="settings-panel" id="settings-panel-users">
|
||||
<h4 class="panel-title">Benutzerverwaltung <span class="badge-admin">Admin</span></h4>
|
||||
|
||||
Reference in New Issue
Block a user