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:
parent
d69e07f00d
commit
3b505d231a
@ -328,6 +328,77 @@
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.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>
|
||||
</head>
|
||||
<body>
|
||||
@ -383,26 +454,56 @@
|
||||
<!-- ================= APP VIEW ================= -->
|
||||
<div id="appView" class="view">
|
||||
|
||||
<div class="topbar">
|
||||
<span id="whoAmI"></span>
|
||||
<button class="btn-secondary" onclick="logout()">Abmelden</button>
|
||||
</div>
|
||||
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
|
||||
|
||||
<header>
|
||||
<div>
|
||||
<h1>WG Nackenheim — Vermieter-Cockpit</h1>
|
||||
<p>170 qm · 4 Zimmer · Einzelzimmervermietung</p>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:12px;">
|
||||
<div class="meta" id="lastUpdated">–</div>
|
||||
<button class="refresh-btn" onclick="loadData()">↻ Aktualisieren</button>
|
||||
</div>
|
||||
</header>
|
||||
<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 & 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 & Ü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 & 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 & Abrechnung</button>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main>
|
||||
<div id="errorBanner" style="display:none" class="error-banner"></div>
|
||||
<div class="content-area">
|
||||
<div class="topbar">
|
||||
<button class="hamburger-btn" onclick="openSidebar()">☰</button>
|
||||
<span id="whoAmI"></span>
|
||||
<button class="btn-secondary" onclick="logout()">Abmelden</button>
|
||||
</div>
|
||||
|
||||
<div class="stat-row" id="statRow"></div>
|
||||
<header>
|
||||
<div>
|
||||
<h1>WG Nackenheim — Vermieter-Cockpit</h1>
|
||||
<p>170 qm · 4 Zimmer · Einzelzimmervermietung</p>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:12px;">
|
||||
<div class="meta" id="lastUpdated">–</div>
|
||||
<button class="refresh-btn" onclick="loadData()">↻ Aktualisieren</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div id="errorBanner" style="display:none" class="error-banner"></div>
|
||||
|
||||
<div class="page active" id="page-overview">
|
||||
<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">
|
||||
<h2>Mieter einladen</h2>
|
||||
@ -434,11 +535,42 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="rentSection">
|
||||
<h2 id="rentSectionTitle">Miet-Ampel je Zimmer</h2>
|
||||
<div class="room-grid" id="roomGrid"></div>
|
||||
<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>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-tickets">
|
||||
|
||||
<section>
|
||||
<h2>Schaden melden</h2>
|
||||
<div class="invite-card">
|
||||
@ -474,6 +606,37 @@
|
||||
<div class="ticket-list" id="ticketList"></div>
|
||||
</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">
|
||||
<h2>Putzaufgabe zuweisen</h2>
|
||||
<div class="invite-card">
|
||||
@ -545,6 +708,10 @@
|
||||
<div class="ticket-list" id="absenceList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-finance">
|
||||
|
||||
<section id="expensesSection">
|
||||
<h2>WG-Kasse (Ausgaben-Teiler)</h2>
|
||||
<p style="color:var(--text-muted); font-size:13px; margin-top:-8px;">
|
||||
@ -572,6 +739,10 @@
|
||||
<div class="ticket-list" id="expenseList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-inventory">
|
||||
|
||||
<section id="inventorySection">
|
||||
<h2>Inventarverwaltung</h2>
|
||||
<div class="invite-card" id="inventoryFormCard" style="display:none; margin-bottom:16px;">
|
||||
@ -630,6 +801,10 @@
|
||||
<div class="ticket-list" id="handoverList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-access">
|
||||
|
||||
<section id="guestCodesSection">
|
||||
<h2>Smart-Lock-Gastcodes</h2>
|
||||
<div class="invite-card" style="margin-bottom:16px;">
|
||||
@ -649,6 +824,10 @@
|
||||
<div class="ticket-list" id="guestCodeList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-household">
|
||||
|
||||
<section id="storageSection">
|
||||
<h2>Küchen-/Schrankplaner</h2>
|
||||
<div class="invite-card" id="storageFormCard" style="display:none; margin-bottom:16px;">
|
||||
@ -685,6 +864,10 @@
|
||||
<div class="ticket-list" id="trashList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-documents">
|
||||
|
||||
<section id="documentsSection">
|
||||
<h2>Dokumenten-Safe & Tutorials</h2>
|
||||
<div class="invite-card" id="documentFormCard" style="display:none; margin-bottom:16px;">
|
||||
@ -708,6 +891,10 @@
|
||||
<div class="ticket-list" id="documentList"></div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page" id="page-contracts">
|
||||
|
||||
<section id="noticeDeadlinesSection">
|
||||
<h2>Kündigungsfristen</h2>
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@ -984,6 +1116,30 @@
|
||||
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() {
|
||||
showView('appView');
|
||||
document.getElementById('whoAmI').textContent =
|
||||
|
||||
Loading…
Reference in New Issue
Block a user