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);
|
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 & 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>
|
||||||
|
|
||||||
|
<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 & Tutorials</h2>
|
<h2>Dokumenten-Safe & 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,66 +928,11 @@
|
|||||||
<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>
|
||||||
const API_BASE_URL = window.WG_API_BASE_URL ||
|
const API_BASE_URL = window.WG_API_BASE_URL ||
|
||||||
@ -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 =
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user