Redirect login to home, reorder cookbook menu, fix sticky header+filter in shopping list, update modal unit row and clean item view
This commit is contained in:
+4
-4
@@ -248,10 +248,10 @@ def get_service_worker():
|
||||
# ==========================================
|
||||
|
||||
@app.get("/login", response_class=HTMLResponse)
|
||||
async def login_get(request: Request, next: str = "/cookbook"):
|
||||
async def login_get(request: Request, next: str = "/"):
|
||||
token = request.cookies.get(COOKIE_NAME)
|
||||
if verify_auth_token(token):
|
||||
return RedirectResponse(url=next or "/cookbook", status_code=303)
|
||||
return RedirectResponse(url=next or "/", status_code=303)
|
||||
return templates.TemplateResponse(
|
||||
request=request,
|
||||
name="login.html",
|
||||
@@ -264,7 +264,7 @@ async def login_post(
|
||||
username: str = Form("admin"),
|
||||
password: str = Form(...),
|
||||
remember: Optional[str] = Form(None),
|
||||
next: str = Form("/cookbook")
|
||||
next: str = Form("/")
|
||||
):
|
||||
valid_user = (username.strip().lower() == APP_USERNAME.lower()) or (username.strip() == "admin")
|
||||
valid_pass = (password == APP_PASSWORD)
|
||||
@@ -272,7 +272,7 @@ async def login_post(
|
||||
if valid_user and valid_pass:
|
||||
days = 30 if remember else 1
|
||||
token = create_auth_token(username.strip(), days=days)
|
||||
target = next if next and not next.startswith("/login") else "/cookbook"
|
||||
target = next if next and not next.startswith("/login") else "/"
|
||||
resp = RedirectResponse(url=target, status_code=303)
|
||||
max_age = days * 86400
|
||||
resp.set_cookie(
|
||||
|
||||
+13
-12
@@ -494,12 +494,6 @@
|
||||
|
||||
<!-- Desktop Buttons (auf Bildschirmen > 768px) -->
|
||||
<div class="cb-nav-actions desktop-nav-actions">
|
||||
<a href="/" class="cb-nav-btn" title="Menü-Übersicht öffnen">
|
||||
<span>🏠</span> <span>Menü</span>
|
||||
</a>
|
||||
<a href="/shopping-list" class="cb-nav-btn" title="Einkaufsliste öffnen">
|
||||
<span>🛒</span> <span>Einkaufsliste</span>
|
||||
</a>
|
||||
<button type="button" onclick="openMealPlanModal(event)" class="cb-nav-btn" title="Wochenmenü-Plan öffnen">
|
||||
<span>📅</span> <span>Wochenplan</span>
|
||||
</button>
|
||||
@@ -512,6 +506,12 @@
|
||||
<button type="button" onclick="openPrintModal(event)" class="cb-nav-btn" title="Druckansicht & PDF">
|
||||
<span>🖨️</span> <span>Drucken</span>
|
||||
</button>
|
||||
<a href="/" class="cb-nav-btn" title="Zur Startseite">
|
||||
<span>🏠</span> <span>Startseite</span>
|
||||
</a>
|
||||
<a href="/shopping-list" class="cb-nav-btn" title="Einkaufsliste öffnen">
|
||||
<span>🛒</span> <span>Einkaufsliste</span>
|
||||
</a>
|
||||
<a href="/logout" class="cb-nav-btn cb-nav-btn-logout" title="Abmelden">
|
||||
<span>🚪</span> <span>Abmelden</span>
|
||||
</a>
|
||||
@@ -523,12 +523,6 @@
|
||||
<span>⋮</span>
|
||||
</button>
|
||||
<div id="cb-mobile-menu" class="cb-mobile-dropdown">
|
||||
<a href="/" class="cb-mobile-item">
|
||||
<span>🏠</span> <span>Menü-Übersicht</span>
|
||||
</a>
|
||||
<a href="/shopping-list" class="cb-mobile-item">
|
||||
<span>🛒</span> <span>Einkaufsliste</span>
|
||||
</a>
|
||||
<button type="button" onclick="handleMobileMenuAction(openMealPlanModal)" class="cb-mobile-item">
|
||||
<span>📅</span> <span>Wochenmenü-Plan</span>
|
||||
</button>
|
||||
@@ -542,6 +536,13 @@
|
||||
<span>🖨️</span> <span>Drucken & PDF</span>
|
||||
</button>
|
||||
<hr class="cb-mobile-divider">
|
||||
<a href="/" class="cb-mobile-item">
|
||||
<span>🏠</span> <span>Startseite</span>
|
||||
</a>
|
||||
<a href="/shopping-list" class="cb-mobile-item">
|
||||
<span>🛒</span> <span>Einkaufsliste</span>
|
||||
</a>
|
||||
<hr class="cb-mobile-divider">
|
||||
<a href="/logout" class="cb-mobile-item cb-mobile-logout">
|
||||
<span>🚪</span> <span>Abmelden</span>
|
||||
</a>
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="/login" class="login-form">
|
||||
<input type="hidden" name="next" value="{{ next_url or '/cookbook' }}">
|
||||
<input type="hidden" name="next" value="{{ next_url or '/' }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Benutzername</label>
|
||||
|
||||
+103
-251
@@ -10,7 +10,7 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="What To Cook?">
|
||||
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
||||
<link rel="stylesheet" href="/static/cookbook.css?v=4.6.3">
|
||||
<link rel="stylesheet" href="/static/cookbook.css?v=4.6.4">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛒</text></svg>">
|
||||
<style>
|
||||
:root {
|
||||
@@ -36,12 +36,18 @@
|
||||
background-size: 20px 20px;
|
||||
font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
||||
color: var(--shop-schwarz);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Fester Container für Header + Filterleiste (kleben gemeinsam oben) */
|
||||
.cb-sticky-header-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
background: var(--shop-green);
|
||||
}
|
||||
|
||||
/* Live Online Badge in Navbar */
|
||||
.cb-online-pill {
|
||||
font-size: 0.72rem;
|
||||
@@ -65,7 +71,6 @@
|
||||
|
||||
/* Hauptinhalt Container */
|
||||
.shop-main-container {
|
||||
flex: 1 0 auto;
|
||||
max-width: 820px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
@@ -212,7 +217,7 @@
|
||||
background: rgba(30, 77, 59, 0.05);
|
||||
}
|
||||
|
||||
/* Badges: Notiz & Rezept-Quelle */
|
||||
/* Badge nur noch für Notiz (Quelle wurde entfernt) */
|
||||
.shop-item-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -230,17 +235,6 @@
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
.shop-badge-source {
|
||||
font-size: 0.75rem;
|
||||
color: #555;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
/* Löschen-Icon am rechten Rand */
|
||||
.shop-item-delete {
|
||||
@@ -328,11 +322,6 @@
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
.modal-form-row-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
.modal-btn-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -374,95 +363,83 @@
|
||||
</head>
|
||||
<body class="shopping-app-body">
|
||||
|
||||
<!-- ========================================== -->
|
||||
<!-- 100% IDENTISCHE NAVIGATION ZUM KOCHBUCH -->
|
||||
<!-- ========================================== -->
|
||||
<header class="cb-nav">
|
||||
<a href="/shopping-list" class="cb-nav-brand" title="Einkaufsliste neu laden">
|
||||
<span class="cb-brand-icon">🛒</span>
|
||||
<h1 class="cb-nav-title">Einkaufsliste</h1>
|
||||
</a>
|
||||
<span id="shop-online-pill" class="cb-online-pill" title="Synchronisations-Status">● Live</span>
|
||||
|
||||
<!-- Suchfeld (automatisch angepasst, überläuft mobil nicht) -->
|
||||
<div class="cb-search-box no-print">
|
||||
<span class="cb-search-icon">🔍</span>
|
||||
<input type="search" id="shop-search-input" class="cb-search-input" placeholder="Artikel filtern..." oninput="onSearchInput(this.value)" autocomplete="off">
|
||||
<button type="button" id="shop-search-clear" class="cb-search-clear" onclick="clearSearch()" style="display: none;" title="Suche leeren">✕</button>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Buttons (auf Bildschirmen > 768px) -->
|
||||
<div class="cb-nav-actions desktop-nav-actions">
|
||||
<a href="/" class="cb-nav-btn" title="Startmenü">
|
||||
<span>🏠</span> <span>Menü</span>
|
||||
<!-- ============================================================== -->
|
||||
<!-- FIXIERTER KOPFBEREICH: HEADER & KATEGORIE-FILTERLEISTE GEMEINSAM -->
|
||||
<!-- ============================================================== -->
|
||||
<div class="cb-sticky-header-container">
|
||||
<header class="cb-nav">
|
||||
<a href="/shopping-list" class="cb-nav-brand" title="Einkaufsliste neu laden">
|
||||
<span class="cb-brand-icon">🛒</span>
|
||||
<h1 class="cb-nav-title">Einkaufsliste</h1>
|
||||
</a>
|
||||
<a href="/cookbook" class="cb-nav-btn" title="Kochbuch & Rezepte">
|
||||
<span>📖</span> <span>Kochbuch</span>
|
||||
</a>
|
||||
<button type="button" onclick="openMealPlanImportModal()" class="cb-nav-btn" title="Zutaten aus aktuellem Wochenplan holen">
|
||||
<span>📅</span> <span>Wochenplan</span>
|
||||
</button>
|
||||
<button type="button" onclick="toggleSortMode()" class="cb-nav-btn" id="desktop-sort-btn" title="Sortierung umschalten">
|
||||
<span id="desktop-sort-icon">⇅</span> <span id="desktop-sort-label">Kategorien</span>
|
||||
</button>
|
||||
<button type="button" onclick="openCategoriesModal()" class="cb-nav-btn" title="Supermarkt-Bereiche & Laufweg anpassen">
|
||||
<span>🏬</span> <span>Bereiche</span>
|
||||
</button>
|
||||
<button type="button" onclick="openDeleteStep1Modal()" class="cb-nav-btn cb-btn-danger" style="background:#b02a37; border-color:#b02a37; color:#fff;" title="Liste leeren">
|
||||
<span>🗑️</span> <span>Leeren</span>
|
||||
</button>
|
||||
<a href="/logout" class="cb-nav-btn cb-nav-btn-logout" title="Abmelden">
|
||||
<span>🚪</span> <span>Abmelden</span>
|
||||
</a>
|
||||
</div>
|
||||
<span id="shop-online-pill" class="cb-online-pill" title="Synchronisations-Status">● Live</span>
|
||||
|
||||
<!-- Mobile 3-Punkte (Kebab) Menü (auf Bildschirmen <= 768px) -->
|
||||
<div class="mobile-nav-wrapper">
|
||||
<button type="button" class="cb-kebab-btn" onclick="toggleMobileMenu(event)" aria-label="Menü öffnen" title="Menü">
|
||||
<span>⋮</span>
|
||||
</button>
|
||||
<div id="cb-mobile-menu" class="cb-mobile-dropdown">
|
||||
<a href="/" class="cb-mobile-item">
|
||||
<span>🏠</span> <span>Startmenü</span>
|
||||
</a>
|
||||
<a href="/cookbook" class="cb-mobile-item">
|
||||
<span>📖</span> <span>Zum Kochbuch</span>
|
||||
</a>
|
||||
<button type="button" onclick="handleMobileMenuAction(openMealPlanImportModal)" class="cb-mobile-item">
|
||||
<span>📅</span> <span>Aus Wochenplan holen</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(toggleSortMode)" class="cb-mobile-item">
|
||||
<span id="mobile-sort-icon">⇅</span> <span id="mobile-sort-label">Sortierung: Laufweg</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(toggleCategoryFilterBar)" class="cb-mobile-item">
|
||||
<span>🏷️</span> <span id="mobile-filter-label">Kategorien filtern</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(openCategoriesModal)" class="cb-mobile-item">
|
||||
<!-- Desktop Buttons (auf Bildschirmen > 768px) -->
|
||||
<div class="cb-nav-actions desktop-nav-actions">
|
||||
<button type="button" onclick="openCategoriesModal()" class="cb-nav-btn" title="Supermarkt-Bereiche & Laufweg anpassen">
|
||||
<span>🏬</span> <span>Bereiche anpassen</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(deleteCheckedItems)" class="cb-mobile-item">
|
||||
<button type="button" onclick="openMealPlanImportModal()" class="cb-nav-btn" title="Zutaten aus Wochenplan holen">
|
||||
<span>📅</span> <span>Aus Wochenplan holen</span>
|
||||
</button>
|
||||
<button type="button" onclick="deleteCheckedItems()" class="cb-nav-btn" title="Erledigte Artikel löschen">
|
||||
<span>🧹</span> <span>Erledigte löschen</span>
|
||||
</button>
|
||||
<hr class="cb-mobile-divider">
|
||||
<button type="button" onclick="handleMobileMenuAction(openDeleteStep1Modal)" class="cb-mobile-item" style="color: #b02a37; font-weight: 700;">
|
||||
<span>🗑️</span> <span>Einkaufsliste leeren</span>
|
||||
<button type="button" onclick="openDeleteStep1Modal()" class="cb-nav-btn cb-btn-danger" style="background:#b02a37; border-color:#b02a37; color:#fff;" title="Liste leeren">
|
||||
<span>🗑️</span> <span>Liste leeren</span>
|
||||
</button>
|
||||
<hr class="cb-mobile-divider">
|
||||
<a href="/logout" class="cb-mobile-item cb-mobile-logout">
|
||||
<a href="/" class="cb-nav-btn" title="Zur Startseite">
|
||||
<span>🏠</span> <span>Startseite</span>
|
||||
</a>
|
||||
<a href="/cookbook" class="cb-nav-btn" title="Kochbuch & Rezepte">
|
||||
<span>📖</span> <span>Kochbuch</span>
|
||||
</a>
|
||||
<a href="/logout" class="cb-nav-btn cb-nav-btn-logout" title="Abmelden">
|
||||
<span>🚪</span> <span>Abmelden</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ========================================== -->
|
||||
<!-- KATEGORIE-FILTERLEISTE (WIE IM KOCHBUCH) -->
|
||||
<!-- ========================================== -->
|
||||
<nav class="cb-filter-bar no-print" id="shop-category-bar">
|
||||
<div class="cb-filter-scroll-container" id="shop-category-chips">
|
||||
<!-- Dynamisch befüllt mit Kategorien -->
|
||||
</div>
|
||||
</nav>
|
||||
<!-- Mobile 3-Punkte (Kebab) Menü (auf Bildschirmen <= 768px) -->
|
||||
<div class="mobile-nav-wrapper">
|
||||
<button type="button" class="cb-kebab-btn" onclick="toggleMobileMenu(event)" aria-label="Menü öffnen" title="Menü">
|
||||
<span>⋮</span>
|
||||
</button>
|
||||
<div id="cb-mobile-menu" class="cb-mobile-dropdown">
|
||||
<button type="button" onclick="handleMobileMenuAction(openCategoriesModal)" class="cb-mobile-item">
|
||||
<span>🏬</span> <span>Bereiche anpassen</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(openMealPlanImportModal)" class="cb-mobile-item">
|
||||
<span>📅</span> <span>Aus Wochenplan holen</span>
|
||||
</button>
|
||||
<button type="button" onclick="handleMobileMenuAction(deleteCheckedItems)" class="cb-mobile-item">
|
||||
<span>🧹</span> <span>Erledigte löschen</span>
|
||||
</button>
|
||||
<hr class="cb-mobile-divider">
|
||||
<button type="button" onclick="handleMobileMenuAction(openDeleteStep1Modal)" class="cb-mobile-item" style="color: #b02a37; font-weight: 700;">
|
||||
<span>🗑️</span> <span>Liste leeren</span>
|
||||
</button>
|
||||
<hr class="cb-mobile-divider">
|
||||
<a href="/" class="cb-mobile-item">
|
||||
<span>🏠</span> <span>Startseite</span>
|
||||
</a>
|
||||
<a href="/cookbook" class="cb-mobile-item">
|
||||
<span>📖</span> <span>Kochbuch</span>
|
||||
</a>
|
||||
<hr class="cb-mobile-divider">
|
||||
<a href="/logout" class="cb-mobile-item cb-mobile-logout">
|
||||
<span>🚪</span> <span>Abmelden</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- DAUERHAFT FIXIERTE KATEGORIEN-LEISTE -->
|
||||
<nav class="cb-filter-bar no-print" id="shop-category-bar">
|
||||
<div class="cb-filter-scroll-container" id="shop-category-chips">
|
||||
<!-- Dynamisch befüllt mit Kategorien -->
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- ========================================== -->
|
||||
<!-- HAUPTINHALT: EINKAUFSLISTEN-EINTRÄGE -->
|
||||
@@ -515,15 +492,14 @@
|
||||
<select id="edit-item-category" class="cb-select"></select>
|
||||
</div>
|
||||
|
||||
<div class="modal-form-row-2">
|
||||
<div>
|
||||
<label class="cb-label" for="edit-item-amount">Menge</label>
|
||||
<input type="text" id="edit-item-amount" class="cb-input" placeholder="z. B. 2 oder 500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="cb-label" for="edit-item-unit">Einheit</label>
|
||||
<input type="text" id="edit-item-unit" class="cb-input" placeholder="z. B. g, l, Bund, Dose">
|
||||
</div>
|
||||
<div>
|
||||
<label class="cb-label" for="edit-item-amount">Menge</label>
|
||||
<input type="text" id="edit-item-amount" class="cb-input" placeholder="z. B. 2 oder 500">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="cb-label" for="edit-item-unit">Einheit</label>
|
||||
<input type="text" id="edit-item-unit" class="cb-input" placeholder="z. B. g, l, Bund, Dose">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -572,15 +548,14 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="modal-form-row-2">
|
||||
<div>
|
||||
<label class="cb-label" for="add-item-amount">Menge</label>
|
||||
<input type="text" id="add-item-amount" class="cb-input" placeholder="z. B. 2 oder 500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="cb-label" for="add-item-unit">Einheit</label>
|
||||
<input type="text" id="add-item-unit" class="cb-input" placeholder="z. B. g, l, Bund, Dose">
|
||||
</div>
|
||||
<div>
|
||||
<label class="cb-label" for="add-item-amount">Menge</label>
|
||||
<input type="text" id="add-item-amount" class="cb-input" placeholder="z. B. 2 oder 500">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="cb-label" for="add-item-unit">Einheit</label>
|
||||
<input type="text" id="add-item-unit" class="cb-input" placeholder="z. B. g, l, Bund, Dose">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -723,9 +698,6 @@
|
||||
let allItems = [];
|
||||
let allCategories = [];
|
||||
let currentFilterCategory = null;
|
||||
let currentSortMode = 'category'; // 'category' (Laufweg) oder 'alpha'
|
||||
let currentSearchTerm = '';
|
||||
let isFilterBarVisible = true;
|
||||
let ws = null;
|
||||
let reconnectTimeout = null;
|
||||
let pingInterval = null;
|
||||
@@ -742,10 +714,6 @@
|
||||
allCategories = JSON.parse(localStorage.getItem('wtc_shop_cats_cache') || '[]');
|
||||
}
|
||||
|
||||
// Gespeicherte Sortiereinstellung laden
|
||||
const savedSort = localStorage.getItem('wtc_shop_sort_mode');
|
||||
if (savedSort) currentSortMode = savedSort;
|
||||
|
||||
// ==========================================
|
||||
// MOBILE NAVIGATION (3-PUNKTE-MENÜ)
|
||||
// ==========================================
|
||||
@@ -784,7 +752,6 @@
|
||||
|
||||
ws.onopen = () => {
|
||||
updateOnlineStatus(true);
|
||||
// Ping Heartbeat starten
|
||||
if (pingInterval) clearInterval(pingInterval);
|
||||
pingInterval = setInterval(() => {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
@@ -937,7 +904,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Auch 'Sonstiges' anzeigen falls vorhanden
|
||||
// 'Sonstiges' anzeigen falls vorhanden
|
||||
const uncategorizedCount = allItems.filter(it => !it.category || it.category === 'Sonstiges').length;
|
||||
if (uncategorizedCount > 0 && !allCategories.some(c => c.name === 'Sonstiges')) {
|
||||
const isAct = currentFilterCategory === 'Sonstiges';
|
||||
@@ -957,67 +924,8 @@
|
||||
renderShoppingList();
|
||||
}
|
||||
|
||||
function toggleCategoryFilterBar() {
|
||||
const bar = document.getElementById('shop-category-bar');
|
||||
if (!bar) return;
|
||||
isFilterBarVisible = !isFilterBarVisible;
|
||||
bar.style.display = isFilterBarVisible ? 'flex' : 'none';
|
||||
const label = document.getElementById('mobile-filter-label');
|
||||
if (label) {
|
||||
label.textContent = isFilterBarVisible ? 'Kategorien ausblenden' : 'Kategorien filtern';
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// LIVE-SUCHE
|
||||
// ==========================================
|
||||
function onSearchInput(val) {
|
||||
currentSearchTerm = val.trim().toLowerCase();
|
||||
const clearBtn = document.getElementById('shop-search-clear');
|
||||
if (clearBtn) clearBtn.style.display = currentSearchTerm ? 'inline-block' : 'none';
|
||||
renderShoppingList();
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
const input = document.getElementById('shop-search-input');
|
||||
if (input) input.value = '';
|
||||
currentSearchTerm = '';
|
||||
const clearBtn = document.getElementById('shop-search-clear');
|
||||
if (clearBtn) clearBtn.style.display = 'none';
|
||||
renderShoppingList();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// SORTIERUNG TOGGLE (LAUFWEG VS ALPHABETISCH)
|
||||
// ==========================================
|
||||
function toggleSortMode() {
|
||||
currentSortMode = (currentSortMode === 'category') ? 'alpha' : 'category';
|
||||
localStorage.setItem('wtc_shop_sort_mode', currentSortMode);
|
||||
updateSortButtonUI();
|
||||
renderShoppingList();
|
||||
showToast(currentSortMode === 'category' ? 'Sortierung: Supermarkt-Laufweg' : 'Sortierung: Alphabetisch (A–Z)');
|
||||
}
|
||||
|
||||
function updateSortButtonUI() {
|
||||
const isCat = (currentSortMode === 'category');
|
||||
|
||||
const dIcon = document.getElementById('desktop-sort-icon');
|
||||
const dLabel = document.getElementById('desktop-sort-label');
|
||||
if (dIcon && dLabel) {
|
||||
dIcon.textContent = isCat ? '⇅' : '🔤';
|
||||
dLabel.textContent = isCat ? 'Kategorien' : 'A–Z';
|
||||
}
|
||||
|
||||
const mIcon = document.getElementById('mobile-sort-icon');
|
||||
const mLabel = document.getElementById('mobile-sort-label');
|
||||
if (mIcon && mLabel) {
|
||||
mIcon.textContent = isCat ? '⇅' : '🔤';
|
||||
mLabel.textContent = isCat ? 'Sortierung: Laufweg' : 'Sortierung: Alphabetisch';
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// RENDERING: EINKAUFSLISTEN-ITEMS
|
||||
// RENDERING: EINKAUFSLISTEN-ITEMS (LAUFWEG)
|
||||
// ==========================================
|
||||
function renderShoppingList() {
|
||||
const container = document.getElementById('items-container');
|
||||
@@ -1029,13 +937,6 @@
|
||||
if (currentFilterCategory !== null) {
|
||||
filtered = filtered.filter(it => (it.category || 'Sonstiges') === currentFilterCategory);
|
||||
}
|
||||
if (currentSearchTerm) {
|
||||
filtered = filtered.filter(it =>
|
||||
(it.item_name || '').toLowerCase().includes(currentSearchTerm) ||
|
||||
(it.note || '').toLowerCase().includes(currentSearchTerm) ||
|
||||
(it.source || '').toLowerCase().includes(currentSearchTerm)
|
||||
);
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
container.innerHTML = '';
|
||||
@@ -1045,11 +946,7 @@
|
||||
if (emptyState) emptyState.style.display = 'none';
|
||||
}
|
||||
|
||||
if (currentSortMode === 'alpha') {
|
||||
renderAlphabeticalList(container, filtered);
|
||||
} else {
|
||||
renderCategorizedList(container, filtered);
|
||||
}
|
||||
renderCategorizedList(container, filtered);
|
||||
}
|
||||
|
||||
function renderCategorizedList(container, items) {
|
||||
@@ -1059,7 +956,7 @@
|
||||
catOrderMap[c.name] = (c.sort_order !== null && c.sort_order !== undefined) ? c.sort_order : idx;
|
||||
});
|
||||
|
||||
// 2. Items in Gruppen aufteilen
|
||||
// 2. Items in Gruppen aufteilen (offen vs. abgehakt)
|
||||
const groups = {};
|
||||
items.forEach(it => {
|
||||
const cat = it.category || 'Sonstiges';
|
||||
@@ -1104,54 +1001,10 @@
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderAlphabeticalList(container, items) {
|
||||
// Offene alphabetisch, erledigte alphabetisch am Ende
|
||||
const openItems = items.filter(it => !it.checked).sort((a, b) => (a.item_name || '').localeCompare(b.item_name || '', 'de'));
|
||||
const checkedItems = items.filter(it => it.checked).sort((a, b) => (a.item_name || '').localeCompare(b.item_name || '', 'de'));
|
||||
|
||||
let html = `
|
||||
<section class="shop-cat-section">
|
||||
<div class="shop-cat-header">
|
||||
<h2 class="shop-cat-title">
|
||||
<span>🔤</span> <span>Alphabetische Liste</span>
|
||||
</h2>
|
||||
<span class="shop-cat-count">${openItems.length} offen</span>
|
||||
</div>
|
||||
<ul class="shop-items-list">
|
||||
${openItems.map(item => renderItemRow(item)).join('')}
|
||||
</ul>
|
||||
</section>
|
||||
`;
|
||||
|
||||
if (checkedItems.length > 0) {
|
||||
html += `
|
||||
<section class="shop-cat-section" style="margin-top: 24px;">
|
||||
<div class="shop-cat-header" style="border-left-color: #888; background: rgba(0,0,0,0.04);">
|
||||
<h2 class="shop-cat-title" style="color: #666;">
|
||||
<span>✓</span> <span>Bereits erledigt</span>
|
||||
</h2>
|
||||
<span class="shop-cat-count">${checkedItems.length}</span>
|
||||
</div>
|
||||
<ul class="shop-items-list">
|
||||
${checkedItems.map(item => renderItemRow(item)).join('')}
|
||||
</ul>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderItemRow(item) {
|
||||
const isChecked = item.checked ? 'is-checked' : '';
|
||||
const amountStr = formatAmount(item.amount, item.unit);
|
||||
|
||||
// Clean Source: Falls Name "Wochenmenüplan - Rezeptname" enthält, nur Rezeptname anzeigen
|
||||
let cleanSource = '';
|
||||
if (item.source) {
|
||||
cleanSource = item.source.replace(/^Wochenmenüplan\s*[-–:]\s*/i, '').trim();
|
||||
}
|
||||
|
||||
return `
|
||||
<li class="shop-item-row ${isChecked}" id="shop-row-${item.id}">
|
||||
<label class="shop-checkbox-wrap" onclick="event.stopPropagation();">
|
||||
@@ -1165,10 +1018,11 @@
|
||||
${amountStr ? `<span class="shop-item-amount">${escapeHtml(amountStr)}</span>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="shop-item-badges">
|
||||
${item.note ? `<span class="shop-badge-note">📝 ${escapeHtml(item.note)}</span>` : ''}
|
||||
${cleanSource ? `<span class="shop-badge-source">🍳 ${escapeHtml(cleanSource)}</span>` : ''}
|
||||
</div>
|
||||
${item.note ? `
|
||||
<div class="shop-item-badges">
|
||||
<span class="shop-badge-note">📝 ${escapeHtml(item.note)}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
<button type="button" class="shop-item-delete" onclick="deleteItemDirectly(${item.id}, event)" title="Eintrag löschen">
|
||||
@@ -1463,7 +1317,6 @@
|
||||
data-name="${escapeAttr(ing.item_name)}"
|
||||
data-amount="${escapeAttr(ing.amount || '')}"
|
||||
data-unit="${escapeAttr(ing.unit || '')}"
|
||||
data-source="${escapeAttr(g.recipe_title)}"
|
||||
onchange="updateMealPlanConfirmBtn()">
|
||||
<span><strong>${escapeHtml(formatAmount(ing.amount, ing.unit))}</strong> ${escapeHtml(ing.item_name)}</span>
|
||||
</label>
|
||||
@@ -1504,7 +1357,7 @@
|
||||
item_name: cb.dataset.name,
|
||||
amount: cb.dataset.amount || '',
|
||||
unit: cb.dataset.unit || '',
|
||||
source: cb.dataset.source || 'Wochenmenüplan',
|
||||
source: '',
|
||||
category: null,
|
||||
note: ''
|
||||
}));
|
||||
@@ -1714,7 +1567,6 @@
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateSortButtonUI();
|
||||
renderCategoryFilterChips();
|
||||
renderShoppingList();
|
||||
initWebSocket();
|
||||
|
||||
Reference in New Issue
Block a user