Restructure dashboard into sidebar navigation with grouped sections

20 flat scrolling sections replaced with a 10-item sidebar nav (Übersicht,
Mieter, Tickets & Handwerker, Putzplan, WG-Kasse, Inventar & Übergabe,
Zugang, Küche & Müll, Dokumente, Verträge & Abrechnung). One page visible
at a time, collapsible off-canvas menu on mobile. No backend/API changes.
This commit is contained in:
Giuseppe Lombardo 2026-08-13 08:45:25 +00:00
parent d69e07f00d
commit 3b505d231a

View File

@ -328,6 +328,77 @@
border: 1px solid var(--border); border: 1px solid var(--border);
} }
.inventory-thumb-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; } .inventory-thumb-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
/* --- App shell / sidebar navigation --- */
.app-shell { display: flex; align-items: flex-start; min-height: 100vh; }
.sidebar {
flex: 0 0 232px;
width: 232px;
background: var(--card-bg);
border-right: 1px solid var(--border);
min-height: 100vh;
padding: 18px 12px;
position: sticky;
top: 0;
}
.sidebar-brand { padding: 6px 10px 18px; font-weight: 700; font-size: 14.5px; }
.sidebar-brand span { display: block; font-weight: 400; color: var(--text-muted); font-size: 11.5px; margin-top: 2px; }
.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
.nav-item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 10px 12px;
border: none;
background: transparent;
border-radius: 8px;
font-family: inherit;
font-size: 13.5px;
color: var(--text);
text-align: left;
cursor: pointer;
}
.nav-item .nav-icon { font-size: 15px; width: 18px; text-align: center; }
.nav-item:hover { background: var(--bg); }
.nav-item.active { background: var(--green-bg); color: var(--green); font-weight: 600; }
.content-area { flex: 1; min-width: 0; }
.page { display: none; }
.page.active { display: block; }
.hamburger-btn {
display: none;
border: 1px solid var(--border);
background: var(--card-bg);
border-radius: 8px;
padding: 8px 11px;
font-size: 15px;
cursor: pointer;
box-shadow: var(--shadow);
}
.sidebar-overlay { display: none; }
@media (max-width: 880px) {
.sidebar {
position: fixed;
top: 0; left: 0; bottom: 0;
z-index: 60;
transform: translateX(-100%);
transition: transform 0.2s ease;
box-shadow: 4px 0 14px rgba(0,0,0,0.12);
}
.sidebar.open { transform: translateX(0); }
.hamburger-btn { display: inline-flex; }
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.32);
z-index: 50;
}
.sidebar-overlay.open { display: block; }
}
</style> </style>
</head> </head>
<body> <body>
@ -383,7 +454,28 @@
<!-- ================= APP VIEW ================= --> <!-- ================= APP VIEW ================= -->
<div id="appView" class="view"> <div id="appView" class="view">
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<div class="app-shell">
<aside class="sidebar" id="sidebar">
<div class="sidebar-brand">WG Nackenheim<span>Vermieter-Cockpit</span></div>
<nav id="sidebarNav">
<button class="nav-item active" data-page="page-overview" onclick="switchPage('page-overview')"><span class="nav-icon">🏠</span>Übersicht</button>
<button class="nav-item" data-page="page-tenants" onclick="switchPage('page-tenants')"><span class="nav-icon">👥</span>Mieter</button>
<button class="nav-item" data-page="page-tickets" onclick="switchPage('page-tickets')"><span class="nav-icon">🛠️</span>Tickets &amp; Handwerker</button>
<button class="nav-item" data-page="page-cleaning" onclick="switchPage('page-cleaning')"><span class="nav-icon">🧹</span>Putzplan</button>
<button class="nav-item" data-page="page-finance" onclick="switchPage('page-finance')"><span class="nav-icon">💶</span>WG-Kasse</button>
<button class="nav-item" data-page="page-inventory" onclick="switchPage('page-inventory')"><span class="nav-icon">📦</span>Inventar &amp; Übergabe</button>
<button class="nav-item" data-page="page-access" onclick="switchPage('page-access')"><span class="nav-icon">🔑</span>Zugang</button>
<button class="nav-item" data-page="page-household" onclick="switchPage('page-household')"><span class="nav-icon">🍽️</span>Küche &amp; Müll</button>
<button class="nav-item" data-page="page-documents" onclick="switchPage('page-documents')"><span class="nav-icon">📄</span>Dokumente</button>
<button class="nav-item" data-page="page-contracts" onclick="switchPage('page-contracts')"><span class="nav-icon">📑</span>Verträge &amp; Abrechnung</button>
</nav>
</aside>
<div class="content-area">
<div class="topbar"> <div class="topbar">
<button class="hamburger-btn" onclick="openSidebar()"></button>
<span id="whoAmI"></span> <span id="whoAmI"></span>
<button class="btn-secondary" onclick="logout()">Abmelden</button> <button class="btn-secondary" onclick="logout()">Abmelden</button>
</div> </div>
@ -402,8 +494,17 @@
<main> <main>
<div id="errorBanner" style="display:none" class="error-banner"></div> <div id="errorBanner" style="display:none" class="error-banner"></div>
<div class="page active" id="page-overview">
<div class="stat-row" id="statRow"></div> <div class="stat-row" id="statRow"></div>
<section id="rentSection">
<h2 id="rentSectionTitle">Miet-Ampel je Zimmer</h2>
<div class="room-grid" id="roomGrid"></div>
</section>
</div>
<div class="page" id="page-tenants">
<section id="inviteSection" style="display:none"> <section id="inviteSection" style="display:none">
<h2>Mieter einladen</h2> <h2>Mieter einladen</h2>
<div class="invite-card"> <div class="invite-card">
@ -434,11 +535,42 @@
</div> </div>
</section> </section>
<section id="rentSection"> <section id="ratingsSection">
<h2 id="rentSectionTitle">Miet-Ampel je Zimmer</h2> <h2>Mieter-Bewertung</h2>
<div class="room-grid" id="roomGrid"></div> <div class="invite-card" style="margin-bottom:16px;">
<div id="ratingFormError" style="display:none" class="form-error"></div>
<form id="ratingForm" class="invite-form-row" style="flex-wrap:wrap;">
<div class="field" style="flex:1 1 200px;">
<label for="ratingTargetUser">Mieter</label>
<select id="ratingTargetUser" required></select>
</div>
<div class="field" style="flex:1 1 140px;">
<label for="ratingScore">Bewertung</label>
<select id="ratingScore" required>
<option value="5">5 sehr gut</option>
<option value="4">4 gut</option>
<option value="3">3 okay</option>
<option value="2">2 schlecht</option>
<option value="1">1 sehr schlecht</option>
</select>
</div>
<div class="field" style="flex:2 1 240px;">
<label for="ratingComment">Kommentar (optional)</label>
<input type="text" id="ratingComment" placeholder="z. B. sehr zuverlässig, hält sich an den Putzplan" />
</div>
<button type="submit" class="btn-primary" style="width:auto" id="ratingSubmitBtn">Bewertung abgeben</button>
</form>
</div>
<div id="ratingsListWrap" style="display:none">
<p class="meta-text" style="margin-bottom:8px;">Nur für Vermieter/Admin sichtbar.</p>
<div class="ticket-list" id="ratingsList"></div>
</div>
</section> </section>
</div>
<div class="page" id="page-tickets">
<section> <section>
<h2>Schaden melden</h2> <h2>Schaden melden</h2>
<div class="invite-card"> <div class="invite-card">
@ -474,6 +606,37 @@
<div class="ticket-list" id="ticketList"></div> <div class="ticket-list" id="ticketList"></div>
</section> </section>
<section id="craftsmenSection">
<h2>Handwerker-Kontakte</h2>
<div class="invite-card" id="craftsmanFormCard" style="display:none; margin-bottom:16px;">
<div id="craftsmanFormError" style="display:none" class="form-error"></div>
<form id="craftsmanForm" class="invite-form-row" style="flex-wrap:wrap;">
<div class="field" style="flex:1 1 180px;">
<label for="craftsmanName">Firma / Name</label>
<input type="text" id="craftsmanName" required placeholder="z. B. Sanitär Müller" />
</div>
<div class="field" style="flex:1 1 150px;">
<label for="craftsmanTrade">Gewerk</label>
<select id="craftsmanTrade"></select>
</div>
<div class="field" style="flex:1 1 150px;">
<label for="craftsmanPhone">Telefon</label>
<input type="text" id="craftsmanPhone" placeholder="z. B. 06131 12345" />
</div>
<div class="field" style="flex:1 1 180px;">
<label for="craftsmanEmail">E-Mail</label>
<input type="email" id="craftsmanEmail" placeholder="kontakt@handwerker.de" />
</div>
<button type="submit" class="btn-primary" style="width:auto" id="craftsmanSubmitBtn">Hinzufügen</button>
</form>
</div>
<div class="ticket-list" id="craftsmenList"></div>
</section>
</div>
<div class="page" id="page-cleaning">
<section id="cleaningAssignSection" style="display:none"> <section id="cleaningAssignSection" style="display:none">
<h2>Putzaufgabe zuweisen</h2> <h2>Putzaufgabe zuweisen</h2>
<div class="invite-card"> <div class="invite-card">
@ -545,6 +708,10 @@
<div class="ticket-list" id="absenceList"></div> <div class="ticket-list" id="absenceList"></div>
</section> </section>
</div>
<div class="page" id="page-finance">
<section id="expensesSection"> <section id="expensesSection">
<h2>WG-Kasse (Ausgaben-Teiler)</h2> <h2>WG-Kasse (Ausgaben-Teiler)</h2>
<p style="color:var(--text-muted); font-size:13px; margin-top:-8px;"> <p style="color:var(--text-muted); font-size:13px; margin-top:-8px;">
@ -572,6 +739,10 @@
<div class="ticket-list" id="expenseList"></div> <div class="ticket-list" id="expenseList"></div>
</section> </section>
</div>
<div class="page" id="page-inventory">
<section id="inventorySection"> <section id="inventorySection">
<h2>Inventarverwaltung</h2> <h2>Inventarverwaltung</h2>
<div class="invite-card" id="inventoryFormCard" style="display:none; margin-bottom:16px;"> <div class="invite-card" id="inventoryFormCard" style="display:none; margin-bottom:16px;">
@ -630,6 +801,10 @@
<div class="ticket-list" id="handoverList"></div> <div class="ticket-list" id="handoverList"></div>
</section> </section>
</div>
<div class="page" id="page-access">
<section id="guestCodesSection"> <section id="guestCodesSection">
<h2>Smart-Lock-Gastcodes</h2> <h2>Smart-Lock-Gastcodes</h2>
<div class="invite-card" style="margin-bottom:16px;"> <div class="invite-card" style="margin-bottom:16px;">
@ -649,6 +824,10 @@
<div class="ticket-list" id="guestCodeList"></div> <div class="ticket-list" id="guestCodeList"></div>
</section> </section>
</div>
<div class="page" id="page-household">
<section id="storageSection"> <section id="storageSection">
<h2>Küchen-/Schrankplaner</h2> <h2>Küchen-/Schrankplaner</h2>
<div class="invite-card" id="storageFormCard" style="display:none; margin-bottom:16px;"> <div class="invite-card" id="storageFormCard" style="display:none; margin-bottom:16px;">
@ -685,6 +864,10 @@
<div class="ticket-list" id="trashList"></div> <div class="ticket-list" id="trashList"></div>
</section> </section>
</div>
<div class="page" id="page-documents">
<section id="documentsSection"> <section id="documentsSection">
<h2>Dokumenten-Safe &amp; Tutorials</h2> <h2>Dokumenten-Safe &amp; Tutorials</h2>
<div class="invite-card" id="documentFormCard" style="display:none; margin-bottom:16px;"> <div class="invite-card" id="documentFormCard" style="display:none; margin-bottom:16px;">
@ -708,6 +891,10 @@
<div class="ticket-list" id="documentList"></div> <div class="ticket-list" id="documentList"></div>
</section> </section>
</div>
<div class="page" id="page-contracts">
<section id="noticeDeadlinesSection"> <section id="noticeDeadlinesSection">
<h2>Kündigungsfristen</h2> <h2>Kündigungsfristen</h2>
<p class="hint-text">Zeigt für befristete Mietverträge, bis wann spätestens gekündigt werden muss.</p> <p class="hint-text">Zeigt für befristete Mietverträge, bis wann spätestens gekündigt werden muss.</p>
@ -741,65 +928,10 @@
<div class="ticket-list" id="utilityStatementsList"></div> <div class="ticket-list" id="utilityStatementsList"></div>
</section> </section>
<section id="craftsmenSection">
<h2>Handwerker-Kontakte</h2>
<div class="invite-card" id="craftsmanFormCard" style="display:none; margin-bottom:16px;">
<div id="craftsmanFormError" style="display:none" class="form-error"></div>
<form id="craftsmanForm" class="invite-form-row" style="flex-wrap:wrap;">
<div class="field" style="flex:1 1 180px;">
<label for="craftsmanName">Firma / Name</label>
<input type="text" id="craftsmanName" required placeholder="z. B. Sanitär Müller" />
</div> </div>
<div class="field" style="flex:1 1 150px;">
<label for="craftsmanTrade">Gewerk</label>
<select id="craftsmanTrade"></select>
</div>
<div class="field" style="flex:1 1 150px;">
<label for="craftsmanPhone">Telefon</label>
<input type="text" id="craftsmanPhone" placeholder="z. B. 06131 12345" />
</div>
<div class="field" style="flex:1 1 180px;">
<label for="craftsmanEmail">E-Mail</label>
<input type="email" id="craftsmanEmail" placeholder="kontakt@handwerker.de" />
</div>
<button type="submit" class="btn-primary" style="width:auto" id="craftsmanSubmitBtn">Hinzufügen</button>
</form>
</div>
<div class="ticket-list" id="craftsmenList"></div>
</section>
<section id="ratingsSection">
<h2>Mieter-Bewertung</h2>
<div class="invite-card" style="margin-bottom:16px;">
<div id="ratingFormError" style="display:none" class="form-error"></div>
<form id="ratingForm" class="invite-form-row" style="flex-wrap:wrap;">
<div class="field" style="flex:1 1 200px;">
<label for="ratingTargetUser">Mieter</label>
<select id="ratingTargetUser" required></select>
</div>
<div class="field" style="flex:1 1 140px;">
<label for="ratingScore">Bewertung</label>
<select id="ratingScore" required>
<option value="5">5 sehr gut</option>
<option value="4">4 gut</option>
<option value="3">3 okay</option>
<option value="2">2 schlecht</option>
<option value="1">1 sehr schlecht</option>
</select>
</div>
<div class="field" style="flex:2 1 240px;">
<label for="ratingComment">Kommentar (optional)</label>
<input type="text" id="ratingComment" placeholder="z. B. sehr zuverlässig, hält sich an den Putzplan" />
</div>
<button type="submit" class="btn-primary" style="width:auto" id="ratingSubmitBtn">Bewertung abgeben</button>
</form>
</div>
<div id="ratingsListWrap" style="display:none">
<p class="meta-text" style="margin-bottom:8px;">Nur für Vermieter/Admin sichtbar.</p>
<div class="ticket-list" id="ratingsList"></div>
</div>
</section>
</main> </main>
</div>
</div>
</div> </div>
<script> <script>
@ -984,6 +1116,30 @@
location.reload(); location.reload();
} }
// ---------------------------------------------------------------------
// Sidebar-Navigation
// ---------------------------------------------------------------------
function switchPage(pageId) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById(pageId).classList.add('active');
document.querySelectorAll('.nav-item').forEach(b => b.classList.remove('active'));
const activeBtn = document.querySelector(`.nav-item[data-page="${pageId}"]`);
if (activeBtn) activeBtn.classList.add('active');
closeSidebar();
window.scrollTo({ top: 0, behavior: 'instant' in window ? 'instant' : 'auto' });
}
function openSidebar() {
document.getElementById('sidebar').classList.add('open');
document.getElementById('sidebarOverlay').classList.add('open');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('open');
}
function enterApp() { function enterApp() {
showView('appView'); showView('appView');
document.getElementById('whoAmI').textContent = document.getElementById('whoAmI').textContent =