dist files for v1.4.3 built
This commit is contained in:
@@ -54,10 +54,12 @@ Das System umfasst die Darstellung von Menüplänen in einer Wochenübersicht, d
|
||||
| FR-071 | Die Hervorhebung muss anhand von Menüname und Beschreibung erfolgen (Substring-Matching, case-insensitive). | Niedrig | v1.1.0 |
|
||||
| FR-072 | Hervorgehobene Menüs müssen ein Tag-Badge mit dem matchenden Schlagwort anzeigen. | Niedrig | v1.2.4 |
|
||||
| FR-073 | Die Nächste-Woche-Navigation muss die Anzahl der dort gefundenen Highlights als Badge anzeigen. | Niedrig | v1.2.5 |
|
||||
| **Darstellung & Theming** | | | |
|
||||
| FR-080 | Das System muss einen hellen und einen dunklen Darstellungsmodus (Light/Dark Theme) unterstützen. | Niedrig | v1.0.1 |
|
||||
| FR-081 | Die Theme-Präferenz des Benutzers muss persistent gespeichert werden. | Niedrig | v1.0.1 |
|
||||
| FR-082 | Das System muss beim erstmaligen Laden die Betriebssystem-Präferenz für das Farbschema berücksichtigen. | Niedrig | v1.0.1 |
|
||||
| **Header UI & Navigation** | | | |
|
||||
| FR-090 | Die Hauptnavigation (Wochen-Toggles) muss linksbündig neben dem App-Titel positioniert sein. | Niedrig | v1.5.0 |
|
||||
| FR-091 | Ein dynamisches Alarm-Icon im Header muss den Überwachungsstatus geflaggter Menüs anzeigen (Gelb=Überwachung aktiv, Grün=Menü verfügbar, Versteckt=keine Flags). Der Tooltip muss den Zeitpunkt der letzten Prüfung als relativen String (z.B. "vor 4 Min.") enthalten. | Mittel | v1.5.0 |
|
||||
| **Benutzer-Feedback** | | | |
|
||||
| FR-090 | Alle benutzerrelevanten Aktionen (Bestellung, Stornierung, Fehler) müssen durch nicht-blockierende Benachrichtigungen (Toasts) bestätigt werden. | Mittel | v1.0.1 |
|
||||
| FR-091 | Bei einem Verbindungsfehler muss ein Fehlerdialog mit Fallback-Link zur Originalseite angezeigt werden. | Mittel | v1.0.1 |
|
||||
|
||||
2
dist/bookmarklet-payload.js
vendored
2
dist/bookmarklet-payload.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bookmarklet.txt
vendored
2
dist/bookmarklet.txt
vendored
File diff suppressed because one or more lines are too long
2
dist/install.html
vendored
2
dist/install.html
vendored
File diff suppressed because one or more lines are too long
80
dist/kantine-standalone.html
vendored
80
dist/kantine-standalone.html
vendored
@@ -2000,6 +2000,13 @@ body {
|
||||
<h1>Kantinen Übersicht <small class="version-tag" style="font-size: 0.6em; opacity: 0.7; font-weight: 400; cursor: pointer;" title="Klick für Versionsmenü">v1.4.3</small></h1>
|
||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||
</div>
|
||||
<div class="nav-group" style="margin-left: 1rem;">
|
||||
<button id="btn-this-week" class="nav-btn active">Diese Woche</button>
|
||||
<button id="btn-next-week" class="nav-btn">Nächste Woche</button>
|
||||
</div>
|
||||
<button id="alarm-bell" class="icon-btn hidden" aria-label="Benachrichtigungen" title="Keine beobachteten Menüs" style="margin-left: -0.5rem;">
|
||||
<span class="material-icons-round" id="alarm-bell-icon" style="color:var(--text-secondary); transition: color 0.3s;">notifications</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-center-wrapper">
|
||||
<div id="header-week-info" class="header-week-info"></div>
|
||||
@@ -2015,10 +2022,6 @@ body {
|
||||
<button id="btn-highlights" class="icon-btn" aria-label="Persönliche Highlights verwalten" title="Persönliche Highlights verwalten">
|
||||
<span class="material-icons-round">label</span>
|
||||
</button>
|
||||
<div class="nav-group">
|
||||
<button id="btn-this-week" class="nav-btn active">Diese Woche</button>
|
||||
<button id="btn-next-week" class="nav-btn">Nächste Woche</button>
|
||||
</div>
|
||||
<button id="theme-toggle" class="icon-btn" aria-label="Toggle Theme">
|
||||
<span class="material-icons-round theme-icon">light_mode</span>
|
||||
</button>
|
||||
@@ -2484,11 +2487,24 @@ body {
|
||||
const progressFill = document.getElementById('history-progress-fill');
|
||||
const progressText = document.getElementById('history-progress-text');
|
||||
|
||||
// Check memory cache first
|
||||
if (fullOrderHistoryCache) {
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check local storage cache
|
||||
const localCache = localStorage.getItem('kantine_history_cache');
|
||||
if (localCache) {
|
||||
try {
|
||||
fullOrderHistoryCache = JSON.parse(localCache);
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
return;
|
||||
} catch (e) {
|
||||
console.warn('History cache parse error', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!authToken) return;
|
||||
|
||||
historyContent.innerHTML = '';
|
||||
@@ -2526,6 +2542,9 @@ body {
|
||||
}
|
||||
|
||||
fullOrderHistoryCache = allOrders;
|
||||
try {
|
||||
localStorage.setItem('kantine_history_cache', JSON.stringify(allOrders));
|
||||
} catch (e) { console.warn('History cache write error', e); }
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
|
||||
} catch (error) {
|
||||
@@ -2732,6 +2751,7 @@ body {
|
||||
|
||||
if (response.ok || response.status === 201) {
|
||||
showToast(`Bestellt: ${name}`, 'success');
|
||||
localStorage.removeItem('kantine_history_cache');
|
||||
await fetchOrders();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
@@ -2761,6 +2781,7 @@ body {
|
||||
|
||||
if (response.ok) {
|
||||
showToast(`Storniert: ${name}`, 'success');
|
||||
localStorage.removeItem('kantine_history_cache');
|
||||
await fetchOrders();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
@@ -2777,6 +2798,55 @@ body {
|
||||
localStorage.setItem('kantine_flags', JSON.stringify([...userFlags]));
|
||||
}
|
||||
|
||||
function updateAlarmBell() {
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
const bellIcon = document.getElementById('alarm-bell-icon');
|
||||
if (!bellBtn || !bellIcon) return;
|
||||
|
||||
if (userFlags.size === 0) {
|
||||
bellBtn.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
bellBtn.classList.remove('hidden');
|
||||
|
||||
// Check if any flagged item is available
|
||||
let anyAvailable = false;
|
||||
for (const wk of allWeeks) {
|
||||
if (!wk.days) continue;
|
||||
for (const d of wk.days) {
|
||||
if (!d.items) continue;
|
||||
for (const item of d.items) {
|
||||
if (item.available && userFlags.has(item.id)) {
|
||||
anyAvailable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (anyAvailable) break;
|
||||
}
|
||||
if (anyAvailable) break;
|
||||
}
|
||||
|
||||
const lastUpdatedStr = localStorage.getItem('kantine_last_updated');
|
||||
let timeStr = 'Unbekannt';
|
||||
if (lastUpdatedStr) {
|
||||
const lastUpdated = new Date(lastUpdatedStr);
|
||||
const diffMs = Date.now() - lastUpdated.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
if (diffMins < 60) timeStr = `vor ${diffMins} Min.`;
|
||||
else timeStr = `vor ${Math.floor(diffMins / 60)} Std.`;
|
||||
}
|
||||
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
||||
|
||||
if (anyAvailable) {
|
||||
bellIcon.style.color = 'var(--success-color)';
|
||||
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
||||
} else {
|
||||
bellIcon.style.color = 'var(--warning-color)';
|
||||
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFlag(date, articleId, name, cutoff) {
|
||||
const id = `${date}_${articleId}`;
|
||||
if (userFlags.has(id)) {
|
||||
@@ -2790,6 +2860,7 @@ body {
|
||||
}
|
||||
}
|
||||
saveFlags();
|
||||
updateAlarmBell();
|
||||
renderVisibleWeeks();
|
||||
}
|
||||
|
||||
@@ -3158,6 +3229,7 @@ body {
|
||||
updateAuthUI(); // This will trigger fetchOrders if logged in
|
||||
renderVisibleWeeks();
|
||||
updateNextWeekBadge();
|
||||
updateAlarmBell();
|
||||
|
||||
progressMessage.textContent = 'Fertig!';
|
||||
setTimeout(() => progressModal.classList.add('hidden'), 500);
|
||||
|
||||
80
kantine.js
80
kantine.js
@@ -74,6 +74,13 @@
|
||||
<h1>Kantinen Übersicht <small class="version-tag" style="font-size: 0.6em; opacity: 0.7; font-weight: 400; cursor: pointer;" title="Klick für Versionsmenü">{{VERSION}}</small></h1>
|
||||
<div id="last-updated-subtitle" class="subtitle"></div>
|
||||
</div>
|
||||
<div class="nav-group" style="margin-left: 1rem;">
|
||||
<button id="btn-this-week" class="nav-btn active">Diese Woche</button>
|
||||
<button id="btn-next-week" class="nav-btn">Nächste Woche</button>
|
||||
</div>
|
||||
<button id="alarm-bell" class="icon-btn hidden" aria-label="Benachrichtigungen" title="Keine beobachteten Menüs" style="margin-left: -0.5rem;">
|
||||
<span class="material-icons-round" id="alarm-bell-icon" style="color:var(--text-secondary); transition: color 0.3s;">notifications</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-center-wrapper">
|
||||
<div id="header-week-info" class="header-week-info"></div>
|
||||
@@ -89,10 +96,6 @@
|
||||
<button id="btn-highlights" class="icon-btn" aria-label="Persönliche Highlights verwalten" title="Persönliche Highlights verwalten">
|
||||
<span class="material-icons-round">label</span>
|
||||
</button>
|
||||
<div class="nav-group">
|
||||
<button id="btn-this-week" class="nav-btn active">Diese Woche</button>
|
||||
<button id="btn-next-week" class="nav-btn">Nächste Woche</button>
|
||||
</div>
|
||||
<button id="theme-toggle" class="icon-btn" aria-label="Toggle Theme">
|
||||
<span class="material-icons-round theme-icon">light_mode</span>
|
||||
</button>
|
||||
@@ -558,11 +561,24 @@
|
||||
const progressFill = document.getElementById('history-progress-fill');
|
||||
const progressText = document.getElementById('history-progress-text');
|
||||
|
||||
// Check memory cache first
|
||||
if (fullOrderHistoryCache) {
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check local storage cache
|
||||
const localCache = localStorage.getItem('kantine_history_cache');
|
||||
if (localCache) {
|
||||
try {
|
||||
fullOrderHistoryCache = JSON.parse(localCache);
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
return;
|
||||
} catch (e) {
|
||||
console.warn('History cache parse error', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!authToken) return;
|
||||
|
||||
historyContent.innerHTML = '';
|
||||
@@ -600,6 +616,9 @@
|
||||
}
|
||||
|
||||
fullOrderHistoryCache = allOrders;
|
||||
try {
|
||||
localStorage.setItem('kantine_history_cache', JSON.stringify(allOrders));
|
||||
} catch (e) { console.warn('History cache write error', e); }
|
||||
renderHistory(fullOrderHistoryCache);
|
||||
|
||||
} catch (error) {
|
||||
@@ -806,6 +825,7 @@
|
||||
|
||||
if (response.ok || response.status === 201) {
|
||||
showToast(`Bestellt: ${name}`, 'success');
|
||||
localStorage.removeItem('kantine_history_cache');
|
||||
await fetchOrders();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
@@ -835,6 +855,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
showToast(`Storniert: ${name}`, 'success');
|
||||
localStorage.removeItem('kantine_history_cache');
|
||||
await fetchOrders();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
@@ -851,6 +872,55 @@
|
||||
localStorage.setItem('kantine_flags', JSON.stringify([...userFlags]));
|
||||
}
|
||||
|
||||
function updateAlarmBell() {
|
||||
const bellBtn = document.getElementById('alarm-bell');
|
||||
const bellIcon = document.getElementById('alarm-bell-icon');
|
||||
if (!bellBtn || !bellIcon) return;
|
||||
|
||||
if (userFlags.size === 0) {
|
||||
bellBtn.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
bellBtn.classList.remove('hidden');
|
||||
|
||||
// Check if any flagged item is available
|
||||
let anyAvailable = false;
|
||||
for (const wk of allWeeks) {
|
||||
if (!wk.days) continue;
|
||||
for (const d of wk.days) {
|
||||
if (!d.items) continue;
|
||||
for (const item of d.items) {
|
||||
if (item.available && userFlags.has(item.id)) {
|
||||
anyAvailable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (anyAvailable) break;
|
||||
}
|
||||
if (anyAvailable) break;
|
||||
}
|
||||
|
||||
const lastUpdatedStr = localStorage.getItem('kantine_last_updated');
|
||||
let timeStr = 'Unbekannt';
|
||||
if (lastUpdatedStr) {
|
||||
const lastUpdated = new Date(lastUpdatedStr);
|
||||
const diffMs = Date.now() - lastUpdated.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
if (diffMins < 60) timeStr = `vor ${diffMins} Min.`;
|
||||
else timeStr = `vor ${Math.floor(diffMins / 60)} Std.`;
|
||||
}
|
||||
bellBtn.title = `Zuletzt geprüft: ${timeStr}`;
|
||||
|
||||
if (anyAvailable) {
|
||||
bellIcon.style.color = 'var(--success-color)';
|
||||
bellIcon.style.textShadow = '0 0 10px rgba(16, 185, 129, 0.4)';
|
||||
} else {
|
||||
bellIcon.style.color = 'var(--warning-color)';
|
||||
bellIcon.style.textShadow = '0 0 10px rgba(245, 158, 11, 0.4)';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFlag(date, articleId, name, cutoff) {
|
||||
const id = `${date}_${articleId}`;
|
||||
if (userFlags.has(id)) {
|
||||
@@ -864,6 +934,7 @@
|
||||
}
|
||||
}
|
||||
saveFlags();
|
||||
updateAlarmBell();
|
||||
renderVisibleWeeks();
|
||||
}
|
||||
|
||||
@@ -1232,6 +1303,7 @@
|
||||
updateAuthUI(); // This will trigger fetchOrders if logged in
|
||||
renderVisibleWeeks();
|
||||
updateNextWeekBadge();
|
||||
updateAlarmBell();
|
||||
|
||||
progressMessage.textContent = 'Fertig!';
|
||||
setTimeout(() => progressModal.classList.add('hidden'), 500);
|
||||
|
||||
Reference in New Issue
Block a user