User management with login, roles and a Config area #25

Merged
jappel merged 8 commits from feature/user-management into main 2026-09-28 22:43:14 +02:00
3 changed files with 38 additions and 19 deletions
Showing only changes of commit 8d2948cdb1 - Show all commits

View file

@ -323,23 +323,24 @@ a {
} }
.sidebar-footer { .sidebar-footer {
padding: 12px; padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.12); border-top: 1px solid rgba(255, 255, 255, 0.12);
} }
/* Square and icon-only so it fits next to Abmelden in .sidebar-bottom-row. */
.sidebar-theme-toggle { .sidebar-theme-toggle {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; justify-content: center;
padding: 10px 14px; flex: 0 0 auto;
width: 36px;
height: 36px;
padding: 0;
border-radius: 8px; border-radius: 8px;
color: #c7d6e0; color: #c7d6e0;
font-size: 0.92rem; font-size: 0.92rem;
font-weight: 500;
border: 1px solid rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.16);
background: rgba(255, 255, 255, 0.06); background: rgba(255, 255, 255, 0.06);
width: 100%;
text-align: left;
cursor: pointer; cursor: pointer;
} }
@ -2747,12 +2748,20 @@ button.structure-entry-main:focus-visible {
white-space: nowrap; white-space: nowrap;
} }
/* Toggle (square) and Abmelden (fills the rest) share one row. */
.sidebar-bottom-row {
display: flex;
align-items: stretch;
gap: 8px;
padding: 0 14px 12px;
}
.sidebar-logout { .sidebar-logout {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 8px; flex: 1 1 auto;
margin-top: 8px; min-width: 0;
padding: 8px 14px; padding: 8px 14px;
border-radius: 8px; border-radius: 8px;
color: #c7d6e0; color: #c7d6e0;
@ -2760,7 +2769,6 @@ button.structure-entry-main:focus-visible {
font-weight: 500; font-weight: 500;
border: 1px solid rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.16);
background: transparent; background: transparent;
width: 100%;
cursor: pointer; cursor: pointer;
} }
@ -2773,6 +2781,11 @@ button.structure-entry-main:focus-visible {
display: none; display: none;
} }
.sidebar-collapsed .sidebar-bottom-row {
justify-content: center;
padding-inline: 0;
}
.login-gate-screen { .login-gate-screen {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;

View file

@ -200,13 +200,18 @@ export function Sidebar({ isCollapsed, onToggleCollapsed }: SidebarProps) {
<span className="sidebar-identity-name">{user.name}</span> <span className="sidebar-identity-name">{user.name}</span>
</div> </div>
)} )}
{/* Toggle and Abmelden share one row: the toggle stays a fixed-size
square, Abmelden takes the rest of the width. Collapsed, only
the square toggle remains (centered). */}
<div className="sidebar-bottom-row">
<ThemeToggle /> <ThemeToggle />
{user && ( {user && !isCollapsed && (
<button type="button" className="sidebar-logout" onClick={() => void logout()}> <button type="button" className="sidebar-logout" onClick={() => void logout()}>
{isCollapsed ? "⏻" : "Abmelden"} Abmelden
</button> </button>
)} )}
</div> </div>
</div>
</aside> </aside>
); );
} }

View file

@ -48,16 +48,17 @@ export function ThemeToggle() {
} }
return ( return (
// Square, icon-only: it shares a row with the Abmelden button, so there
// is no room for a text label. The label moves to title/aria-label.
<button <button
aria-pressed={theme === "dark"} aria-pressed={theme === "dark"}
className="sidebar-theme-toggle" className="sidebar-theme-toggle"
onClick={toggleTheme} onClick={toggleTheme}
title={theme === "dark" ? "Hellmodus" : "Dunkelmodus"}
aria-label={theme === "dark" ? "Hellmodus einschalten" : "Dunkelmodus einschalten"}
type="button" type="button"
> >
<span className="sidebar-icon" aria-hidden="true"> <span aria-hidden="true">{theme === "dark" ? "☀" : "☾"}</span>
{theme === "dark" ? "☀" : "☾"}
</span>
{theme === "dark" ? "Hellmodus" : "Dunkelmodus"}
</button> </button>
); );
} }